0
点赞
收藏
分享

微信扫一扫

使用Jenkins打包Untiy问题集

Java架构领域 2024-08-17 阅读 19
数据结构

博客中所有代码均在leetcode912. 排序数组中执行

(一)插入排序类

1、直接插入排序

1)思路

2)特性分析

3)代码

class Solution {
public:
    //插入排序
    vector<int> sortArray(vector<int>& nums) {
        for(int i = 1; i < nums.size(); i++)
        {
            int end = i - 1;
            int tmp = nums[i];
            while(end >= 0)
            {
                if(nums[end] > tmp)
                {
                    nums[end+1] = nums[end];
                    end--;
                }
                else break;
            }
            nums[end+1] = tmp;
        }
        return nums;
    }
};

4)leetcode运行结果 

 当数据量为50000时会超时 

2、希尔排序 

1)思路 

2)特性分析

3)代码

class Solution {
public:
    //希尔排序
    vector<int> sortArray(vector<int>& nums) {
        int len = nums.size();
        int gap = nums.size() / 2;
        while(gap >= 1)
        {
            for(int i = gap; i < len; i++)
            {
                int end = i - gap;
                int tmp = nums[i];
                while(end >= 0)
                {
                    if(nums[end] > tmp)
                    {
                        nums[end+gap] = nums[end];
                        end-=gap;
                    }
                    else break;
                }
                nums[end+gap] = tmp;
            }
            gap /= 2;
        }
        return nums;
    }
};

4)leetcode运行结果 

(二)选择排序类

1、直接选择排序

1)思路 

2)特性分析

3)代码

一次选一个数最小

class Solution {
public:
    //直接选择排序
    vector<int> sortArray(vector<int>& nums) {
        int len = nums.size();
        for(int i = 0; i < nums.size()-1; i++)
        {
            int minNum = nums[i];
            int idx = i;
            for(int j = i+1; j < nums.size(); j++)
            {
                if(minNum > nums[j])
                {
                    minNum = nums[j];
                    idx = j;
                }  
            }
            swap(nums[i],nums[idx]);
        }
        return nums;
    }
};

一次选两个数最小最大(最后交换的时候有一个坑)

class Solution {
public:
    //直接选择排序
    vector<int> sortArray(vector<int>& nums) {
        int left = 0, right = nums.size()-1;
        while(left < right)
        {
            int minIdx = left, maxIdx = left;
            for(int i = left + 1; i <= right; i++)
            {
                if(nums[minIdx] > nums[i]) 
                    minIdx = i;
                if(nums[maxIdx] < nums[i])
                    maxIdx = i;
            }
            swap(nums[left], nums[minIdx]);
            //如果left与maxIdx重叠
            if(maxIdx == left) swap(nums[right],nums[minIdx]);
            else swap(nums[right],nums[maxIdx]);
            left++,right--;
        }
        return nums;
    }
};

4)leetcode运行结果 

当数据量为50000时会超时 

2、堆排序

1)思路 

2)特性分析

3)代码

class Solution {
public:
    //堆排序
    void down(int u, vector<int>& nums, int len) {
        int t = u;
        int leftChild = u * 2 + 1;
        int rightChild = u * 2 + 2;
        if (leftChild < len && nums[leftChild] > nums[t]) t = leftChild;
        if (rightChild < len && nums[rightChild] > nums[t]) t = rightChild;
        if (t != u) {
            swap(nums[t], nums[u]);
            down(t, nums, len); // 继续向下递归
        }
    }

    vector<int> sortArray(vector<int>& nums) {
        // 建堆 (从最后一个非叶子节点开始下沉)
        for (int i = nums.size() / 2 - 1; i >= 0; i--) down(i, nums, nums.size());
        
        int len = nums.size();
        for (int i = nums.size() - 1; i > 0; i--) {
            swap(nums[0], nums[i]); // 堆顶元素和当前堆末尾元素交换
            len--;                  // 缩小堆的范围
            down(0, nums, len);     // 重新调整堆
        }
        return nums;
    }
};

4)leetcode运行结果 

(三)交换排序类

1、冒泡排序

1)思路 

2)特性分析

3)代码

class Solution {
public:
    //冒泡排序
    vector<int> sortArray(vector<int>& nums) {
        for(int i = 0; i < nums.size(); i++)//决定遍历次数,从后往前确定排序好的数
            for(int j = 1; j < nums.size() - i; j++)//两两比较,将最大的数向后移
                if(nums[j-1] > nums[j]) swap(nums[j-1],nums[j]);
        return nums;
    }
};

优化版

class Solution {
public:
    //冒泡排序
    vector<int> sortArray(vector<int>& nums) {
        for(int i = 0; i < nums.size(); i++)
        {
            int exchange = 0;
            for(int j = 1; j < nums.size() - i; j++)
                if(nums[j-1] > nums[j]) exchange = 1, swap(nums[j-1],nums[j]);
            if(exchange == 0) break;
        }
            
        return nums;
    }
};

4)leetcode运行结果 

当数据量为50000时会超时 

2、快速排序

1)思路 

a)hoare法

选取最左位置元素作为key值,从右往左遍历,当元素小于key时停止,再从左往右遍历,至当前元素大于key为止,交换两个元素,继续遍历。

左边做key右边先走原因:保证最后两者相遇位置一定比key值小or 相遇位置就是key的位置

  1. R找到小,L找大没有找到,L遇到R;
  2. R找小,没有找到,直接遇到L,要么就是一个比key小的位置或者直接到keyi

缺点:

如果 nums 已经有序(例如 [1, 2, 3, 4, 5]),在使用上述快速排序算法时可能会出现 性能下降 的问题,具体表现为时间复杂度从预期的平均 O(n log n) 退化为最坏的 O(n^2)

原因:
解决方法:
b)挖坑法

c)前后指针法

核心思想:把比key大的值往右翻,比key小的值,翻到左边 

步骤:

说明: 

d)小区间优化:

当子区间长度小于某个阈值时,使用插入排序对剩余元素进行排序。

e)非递归法

递归调用可能导致的栈溢出问题,下边使用非递归法。

2)特性分析

3)代码

当前三种方法均使用递归法实现。非递归法在该部分最后(使用前后指针法+栈实现)

a)hoare法
以最左端元素作为key值
class Solution {
public:
    //快速排序
    void quickSort(vector<int>& nums,int left, int right)
    {
        if(left >= right) return;
        int i = left, j = right;
        int key = left;
        while(i < j)
        {
            // 从右向左找小于基准的元素
            while(i < j && nums[j] >= nums[key]) j--;
            // 从左向右找大于基准的元素
            while(i < j && nums[i] <= nums[key]) i++;
            if(i < j) swap(nums[i],nums[j]);
        }
        // 最后将基准元素放到中间位置
        swap(nums[key],nums[i]);
        key = i;
         // 递归处理基准元素左右两侧的部分
        quickSort(nums,left,key-1);
        quickSort(nums,key+1,right);

    }
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
随机选元素作为key值
class Solution {
public:
    //快速排序
    void quickSort(vector<int>& nums,int left, int right)
    {
        if(left >= right) return;
        int i = left, j = right;
        
        // 随机选择一个基准元素并与左边界元素交换
        int pivotIndex = left + rand() % (right - left + 1);
        swap(nums[left], nums[pivotIndex]);

        int key = left;
        while(i < j)
        {
            // 从右向左找小于基准的元素
            while(i < j && nums[j] >= nums[key]) j--;
            // 从左向右找大于基准的元素
            while(i < j && nums[i] <= nums[key]) i++;
            if(i < j) swap(nums[i],nums[j]);
        }
        // 最后将基准元素放到中间位置
        swap(nums[key],nums[i]);
        key = i;
         // 递归处理基准元素左右两侧的部分
        quickSort(nums,left,key-1);
        quickSort(nums,key+1,right);

    }
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
三数取中选key值 
class Solution {
public:
    // 获取三数中值的下标
    int getMiddleIndex(int left, int right, vector<int>& nums) {
        int middle = left + (right - left) / 2;
        int a = nums[left];
        int b = nums[middle];
        int c = nums[right];
        // 比较三者之间的大小,直接返回中间值的下标
        if ((a <= b && b <= c) || (c <= b && b <= a)) {
            return middle;
        } else if ((b <= a && a <= c) || (c <= a && a <= b)) {
            return left;
        } else {
            return right;
        }
    }

    //快速排序
    void quickSort(vector<int>& nums,int left, int right)
    {
        if(left >= right) return;
        int i = left, j = right;

        // 三数取中选元素
        int pivotIndex = getMiddleIndex(left,right,nums);
        swap(nums[left], nums[pivotIndex]);

        int key = left;
        while(i < j)
        {
            // 从右向左找小于基准的元素
            while(i < j && nums[j] >= nums[key]) j--;
            // 从左向右找大于基准的元素
            while(i < j && nums[i] <= nums[key]) i++;
            if(i < j) swap(nums[i],nums[j]);
        }
        // 最后将基准元素放到中间位置
        swap(nums[key],nums[i]);
        key = i;
         // 递归处理基准元素左右两侧的部分
        quickSort(nums,left,key-1);
        quickSort(nums,key+1,right);

    }
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
b)挖坑法
class Solution {
public:
    // 快速排序 -- 挖坑法
    void quickSort(vector<int>& nums, int left, int right) {
        if (left >= right) return; // 递归结束条件,当左边界大于右边界时结束
        int i = left, j = right;
        int position = left; // 记录"坑"的位置
        int key = nums[left]; // 基准元素,取当前范围的第一个元素作为基准

        while (i < j) {
            // 从右向左寻找小于基准元素的元素
            while (i < j && nums[j] >= key) j--;
            if (i < j) {
                nums[position] = nums[j]; // 把找到的小于基准的元素填入"坑"中
                position = j; // 更新"坑"的位置
            }
            // 从左向右寻找大于基准元素的元素
            while (i < j && nums[i] <= key) i++;
            if (i < j) {
                nums[position] = nums[i]; // 把找到的大于基准的元素填入"坑"中
                position = i; // 更新"坑"的位置
            }
        }
        // 最后将基准元素放到"坑"的位置
        nums[position] = key;

        // 递归处理基准元素左右两侧的部分
        quickSort(nums, left, position - 1);
        quickSort(nums, position + 1, right);
    }

    // 对外接口,调用快速排序
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
c)前后指针法
class Solution {
public:
    // 快速排序 -- 前后指针法
    void quickSort(vector<int>& nums, int left, int right) {
        if (left >= right) return; // 递归结束条件,当左边界不小于右边界时结束
        
        int key = nums[left];
        int pre = left, cur = left + 1;

        while (cur <= right) {
            if (nums[cur] < key) swap(nums[++pre], nums[cur]);
            cur++;
        }
        swap(nums[pre], nums[left]);

        // 递归处理基准元素左右两侧的部分
        quickSort(nums, left, pre - 1);
        quickSort(nums, pre + 1, right);
    }

    // 对外接口,调用快速排序
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
d)小区间优化:
class Solution {
public:
    //插入排序
    void insertSort(vector<int>& nums, int left , int right) {
        for(int i = left+1; i <= right; i++)
        {
            int end = i - 1;
            int tmp = nums[i];
            while(end >= left)
            {
                if(nums[end] > tmp)
                {
                    nums[end+1] = nums[end];
                    end--;
                }
                else break;
            }
            nums[end+1] = tmp;
        }
    }

    // 快速排序 -- 前后指针法
    void quickSort(vector<int>& nums, int left, int right) {
        if (left >= right) return; // 递归结束条件,当左边界不小于右边界时结束
        
        int key = nums[left];
        int pre = left, cur = left + 1;

        while (cur <= right) {
            if (nums[cur] < key) swap(nums[++pre], nums[cur]);
            cur++;
        }
        swap(nums[pre], nums[left]);

        if(pre - left > 16)//设定阈值为16
            quickSort(nums, left, pre - 1);//快速排序
        else
            insertSort(nums,left,pre-1);//直接插入排序
        if(right - pre > 16)
            quickSort(nums, pre + 1, right);//快速排序
        else 
            insertSort(nums,pre+1,right);//直接插入排序
    }

    // 对外接口,调用快速排序
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
e)非递归法
class Solution {
public:
    typedef pair<int,int> PII;
    vector<int> sortArray(vector<int>& nums) {
        stack<PII> s;
        s.push({0,nums.size()-1});
        while(!s.empty())
        {
            int left = s.top().first;
            int right = s.top().second;
            s.pop();
            if(left >= right) continue;
            int pre = left, cur = left+1;
            int keyIdx = left;
            while(cur <= right)
            {
                if(nums[cur] < nums[keyIdx]) swap(nums[cur],nums[++pre]);
                cur++;
            }
            swap(nums[pre],nums[keyIdx]);
            s.push({pre+1,right});
            s.push({left,pre-1});
        }
        return nums;
    }
};

4)leetcode运行结果 

a)hoare法 
以最左端元素作为key值结果:

数据量为50000时会超时

随机选元素作为key值结果: 

三数取中选key值方法结果:
 
b)挖坑法

c)前后指针法

数据量为50000时会超时

(四)归并排序

1、归并排序

1)思路 

2)特性分析

3)代码

a)递归法 
class Solution {
public:
    vector<int> tmp;
    
    void mergeSort(vector<int>& nums, int left, int right) {
        if(left >= right) return;
        
        int mid = left + (right - left) / 2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        
        int begin1 = left, end1 = mid;
        int begin2 = mid + 1, end2 = right;
        int k = left; // 用来记录 tmp 中的索引
        
        while(begin1 <= end1 && begin2 <= end2) {
            if(nums[begin1] < nums[begin2]) tmp[k++] = nums[begin1++];
            else tmp[k++] = nums[begin2++];
        }
        
        while(begin1 <= end1) tmp[k++] = nums[begin1++];
        while(begin2 <= end2) tmp[k++] = nums[begin2++];
        
        for(int i = left; i <= right; i++) {
            nums[i] = tmp[i];
        }
    }
    
    vector<int> sortArray(vector<int>& nums) {
        tmp.resize(nums.size());  // 将 tmp 数组的大小调整为 nums 的大小
        mergeSort(nums, 0, nums.size() - 1);
        return nums;
    }
};
b)非递归法
class Solution {
public:
    vector<int> tmp;
    
    void mergeSort(vector<int>& nums, int left, int right)
    {
        int n = nums.size();
        tmp.resize(n);
        
        // 非递归合并排序
        for(int gap = 1; gap < n; gap *= 2) {
            // 合并大小为gap的子数组
            for(int i = 0; i + gap < n; i += 2 * gap) {
                int begin1 = i, end1 = i + gap - 1;
                int begin2 = i + gap, end2 = min(i + 2 * gap - 1, n - 1);
                int idx = 0;
                
                // 合并两个子数组
                int k = begin1;
                while (begin1 <= end1 && begin2 <= end2) {
                    if(nums[begin1] <= nums[begin2]) tmp[k++] = nums[begin1++];
                    else tmp[k++] = nums[begin2++];
                }
                while (begin1 <= end1) tmp[k++] = nums[begin1++];
                while (begin2 <= end2) tmp[k++] = nums[begin2++];
                
                // 将临时数组中的排序结果拷贝回原数组
                for (int j = i; j <= end2; ++j) {
                    nums[j] = tmp[j];
                }
            }
        }
    }
    
    vector<int> sortArray(vector<int>& nums) {
        mergeSort(nums, 0, nums.size() - 1);
        return nums;
    }
};

4)leetcode运行结果 

a)递归法结果 

b)非递归法结果:

(五)非比较排序

1、计数排序

1)思路 

2)特性分析

3)代码

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        int maxNum = nums[0], minNum = nums[0];
        for(auto x : nums)
        {
            maxNum = max(maxNum,x);
            minNum = min(minNum,x);
        }
        int range = maxNum - minNum + 1;
        vector<int> hash(range,0);
        for(auto x : nums)
        {
            hash[x-minNum]++;
        }
        int k = 0;
        for(int i = 0; i < range; i++)
        {
            while(hash[i]--) nums[k++] = minNum + i;
        }
        return nums;
    }
};

4)leetcode运行结果 

2、基数排序

1)思路 

举例 :

2)特性分析

3)代码

当前代码中增加了负数的处理逻辑 

#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        if (nums.empty()) return nums;

        // 分离正数和负数
        vector<int> positiveNums, negativeNums;
        for (int num : nums) {
            if (num >= 0) {
                positiveNums.push_back(num);
            } else {
                negativeNums.push_back(-num);  // 负数取反
            }
        }

        // 对正数和负数分别进行基数排序
        if (!positiveNums.empty()) {
            radixSort(positiveNums);
        }
        if (!negativeNums.empty()) {
            radixSort(negativeNums);
            // 负数取反后再排序需要恢复负号
            for (int& num : negativeNums) {
                num = -num;
            }
            // 负数部分需要从大到小排序
            reverse(negativeNums.begin(), negativeNums.end());
        }

        // 合并负数和正数
        nums = negativeNums;
        nums.insert(nums.end(), positiveNums.begin(), positiveNums.end());

        return nums;
    }

private:
    void radixSort(vector<int>& nums) {
        int maxVal = *max_element(nums.begin(), nums.end());

        for (int exp = 1; maxVal / exp > 0; exp *= 10) {
            countingSort(nums, exp);
        }
    }

    void countingSort(vector<int>& nums, int exp) {
        int n = nums.size();
        vector<int> output(n);
        vector<int> count(10, 0);

        for (int i = 0; i < n; i++) {
            int index = (nums[i] / exp) % 10;
            count[index]++;
        }

        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        for (int i = n - 1; i >= 0; i--) {
            int index = (nums[i] / exp) % 10;
            output[count[index] - 1] = nums[i];
            count[index]--;
        }

        for (int i = 0; i < n; i++) {
            nums[i] = output[i];
        }
    }
};

4)leetcode运行结果 

举报

相关推荐

0 条评论