【五十】【算法分析与设计】快速排序和归并排序

发布于:2024-04-16 ⋅ 阅读:(168) ⋅ 点赞:(0)

912. 排序数组,快速排序

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1] 输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0] 输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 10(4)

  • -5 * 10(4) <= nums[i] <= 5 * 10(4)

1. srand(time(NULL)); start,rand开启随机器,给它一个种子time(NULL)。

2. void qsort(vector<int>& nums, int left, int right) { 定义递归函数,qsort,将nums数组left~right区间进行排序

递归内部逻辑,随机选取一个key值,[left,l][l+1,r-1][r,right]分成三个区间,左边元素值小于key,中间元素值等于key,右边元素值大于key。

意义:将key值的元素放到了排序的正确位置

接着将左边区间排序,右边区间排序即可。递归调用自己

3. if (left >= right) return;

递归函数出口,如果区间只有一个元素,left==right,此时不需要排序。

如果区间没有元素,left>right,此时也不需要排序。

合并起来就是left>=right,直接返回return。

4. int key = getRandom(nums, left, right);

编写一个函数直接返回nums数组区间left,right区间随机数值。

5.

递归函数内部逻辑。

[left,right]我们区间是这样的,目标区间划分是这样的[left,l][l+1,r-1][r,right]。

因此我们需要将[left,right]区间中的数全部添加到[left,l][l+1,r-1][r,right]中。

其中left和right是固定的位置,而l和r是我们可以控制的位置。

因此在[l+1,r-1]区间分割出一段区间表示待遍历的区间。

[left,l][l+1,i-1][i,r-1][r,right]左边全是小于key的元素,第二部分全是等于key的元素,第三部分是待遍历的部分,第四部分是小于key的部分。

6. int i = left; int l = left - 1, r = right + 1;

初始化区间,[i,r-1]区间一开始包括[left,right]区间所有元素。

7. while (i < r) {

while循环黑盒,将i位置元素添加到已经处理区间中。 if (nums[i] < key) { swap(nums[i++], nums[++l]);

[left,l][l+1,i-1][i,r-1][r,right]

如果nums[i]<key,说明i位置元素一个位于第一部分,因此将l+1与i位置元素交换,l++,i++。 } else if (nums[i] == key) { i++;

如果nums[i]==key,说明i位置元素位于第二部分,i++即可。 } else { swap(nums[i], nums[--r]);

如果nums[i]>key,说明i位置元素位于第四部分,交换r-1和i位置,r--,此时i不需要++,因为r-1位置原本是待排序元素。 } }

8.

上述代码意义:将元素值为key的元素放到正确的位置上,此时只需要排序左边和右边即可。 qsort(nums, left, l); qsort(nums, r, right); }

9.

获取nums数组left,right区间随机元素。

只需要随机获取left,right区间所有的下标即可。

也就是随机获取left~right这些数字。

等价于随机获取(0~right-left)这些数字,+left偏移量即可。 int getRandom(vector<int>& nums, int left, int right) { int rand1 = rand();

随机获取一个整形。

rand1%(right-left+1)意义:rand1的取值范围为0~right-left+1-1即0~right-left

+left偏移量就等于获取随机left~right,也就是随机区间下标

外面再套取一个nums获取随机值返回即可 return nums[(rand1 % (right - left + 1) + left)]; } };


class Solution {
public:
    vector<int> sortArray(vector<int>& nums) { // 快排
        // 递归排序---快速排序
        // 定义qsort递归函数,排序nums数组
        // 内部逻辑,选取key
        //[0,left][left+1,right-1][right,n-1]左边小于key,中间等于key,右边大于key
        // 先将数值等于key正确排序
        // 然后排序左边,排序右边
        srand(time(NULL));

        int n = nums.size();
        qsort(nums, 0, n - 1);
        return nums;
    }
    void qsort(vector<int>& nums, int left, int right) {
        // 排序[left,l][l+1,r-1][r,right] key
        // 递归出口,如果left==right,区间只有一个数
        // 此时不需要排序
        // 如果left==l+1,left>right,此时区间没有数,不需要排序

        if (left >= right)
            return;
        int key = getRandom(nums, left, right);
        int i = left;
        int l = left - 1, r = right + 1;
        while (i < r) {
            if (nums[i] < key) {
                swap(nums[i++], nums[++l]);
            } else if (nums[i] == key) {
                i++;
            } else {
                swap(nums[i], nums[--r]);
            }
        }
        qsort(nums, left, l);
        qsort(nums, r, right);
    }
    int getRandom(vector<int>& nums, int left, int right) {
        int rand1 = rand();
        return nums[(rand1 % (right - left + 1) + left)];
    }
};

912. 排序数组,归并排序

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1] 输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0] 输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 10(4)

  • -5 * 10(4) <= nums[i] <= 5 * 10(4)

1. vector<int> tmp;

临时数组,用于存储排序后的序列。

2. vector<int> sortArray(vector<int>& nums) { tmp.resize(nums.size());

先给临时数组分配空间,大小为nums的size大小。 mergeSort(nums, 0, nums.size() - 1);

mergeSort函数给nums数组0~nums.size()-1区间排序。 return nums; }

3. void mergeSort(vector<int>& nums, int left, int right) {

定义递归函数mergeSort,给nums数组[left,right]区间排序。 if (left >= right) return;

递归的出口,如果left==right此时区间只有一个元素,不需要再排序。

如果left>right,此时区间没有元素,不需要排序。

合并起来就是left>=right直接返回return。

4.

计算区间中点,以便于我们平均化区间长度,尽可能平均化。 int mid = left + (right - left) / 2; //[left,mid][mid+1,right]

5.

先将[left,mid][mid+1,right]两部分区间进行排序。 mergeSort(nums, left, mid); mergeSort(nums, mid + 1, right); 6.

用双指针遍历两端区间,将两端区间进行合并排序区间。 int cur1 = left, end1 = mid; int cur2 = mid + 1, end2 = right; int index = left;

定义index对应tmp临时数组的下标存储合并排序区间。

7. while (cur1 <= end1 && cur2 <= end2) { // 黑盒,cur1,cur2小的放到tmp中index tmp[index++] = nums[cur1] < nums[cur2] ? nums[cur1++] : nums[cur2++]; } 黑盒,每一次内部逻辑,将cur1和cur2中小的元素放到index位置。

放完之后,index++和(cur1++或者cur2++)。 while (cur1 <= end1) { tmp[index++] = nums[cur1++]; } while (cur2 <= end2) { tmp[index++] = nums[cur2++]; }

将还没有排序完的区间依次加入到index中。 8. for (int i = left; i <= right; i++) { nums[i] = tmp[i]; }

最后将排序完,合并完的序列依次赋值给原数组中。 } };


class Solution {
public:
    vector<int> tmp;
    vector<int> sortArray(vector<int>& nums) {
        tmp.resize(nums.size());
        mergeSort(nums, 0, nums.size() - 1);
        return nums;
    }
    void mergeSort(vector<int>& nums, int left, int right) {
        if (left >= right)
            return;
        int mid = left + (right - left) / 2;
        //[left,mid][mid+1,right]
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        int cur1 = left, end1 = mid;
        int cur2 = mid + 1, end2 = right;
        int index = left;
        while (cur1 <= end1 && cur2 <= end2) { // 黑盒,cur1,cur2小的放到tmp中index
            tmp[index++] =
                nums[cur1] < nums[cur2] ? nums[cur1++] : nums[cur2++];
        }

        while (cur1 <= end1) {
            tmp[index++] = nums[cur1++];
        }
        while (cur2 <= end2) {
            tmp[index++] = nums[cur2++];
        }

        for (int i = left; i <= right; i++) {
            nums[i] = tmp[i];
        }
    }
};

小总结

1.快速排序和归并排序都是递归排序。

2.快速排序的递归内部逻辑,只做一小步,这一小步是,随机获取key值,将元素值为key值的元素放到排序的正确位置。

然后左边排序,右边排序。

3.归并排序的递归内部逻辑,只做一小步,将已经排序好的区间[left,mid][mid+1,right]合并成一个排序的区间。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!


网站公告

今日签到

点亮在社区的每一天
去签到