一个优雅的三路快排

思路:数组nums,首元素nums[0]

设置头尾两个指针i, j 分别指向第二个元素和最后一个元素。

不断向右移动i,找到第一个大于nums[0]的元素;

不断向左移动j,找到第一个小于nums[0]的元素;

将nums[i]和nums[j]互换。

重复上述过程直到i>j, 然后将此时的nums[0]和nums[j]互换。

分别将j左边的一组数和右边的一组数分别进行上述递归。


但是我们一般情况下写的这种两段的快排是有问题的,比如说当数组是0111111111111111111111111112这种情况复杂度就会直接退化成O(n^2)。所以正确的快排应该写成三段,大于nums[0],等于nums[0],小于nums[0]三段,而且不能总选取数组第一个元素进行比较,而是应该随机选取这个点。


思路1:最开始分区的过程,我是一个一个区来划分的,这种写法比较挫,还是不删了,放着做纪念吧。

    void Sortdfs(vector<int>&nums, int first, int last)
    {
        if(first<last)
        {
            int a=first+rand()%(last-first+1);
            swap(nums[first],nums[a]);
            int i=first+1, j=last;
            //分成小于等于和大于两段
            while(true)
            {
                while(i<=j && nums[j]>nums[first])
                    j--;
                while(i<j && nums[i]<=nums[first])
                    i++;
                if(i<j)
                    swap(nums[i],nums[j]);
                else
                    break;
            }
            //first到j小于等于,j+1到last大于

             //分成小于和等于两段
            int p=first+1, q=j;
            while(true)
            {
                while(p<=q && nums[q]==nums[first])
                    q--;
                while(p<q && nums[p]<nums[first])
                    p++;
                if(p<q)
                    swap(nums[q],nums[p]);
                else
                    break;    
            }
            swap(nums[first], nums[q]);
            //first到q-1小于,q到j等于
            Sortdfs(nums, j+1, last);
            Sortdfs(nums, first, q-1);
        }  
    }


思路2:通过力扣(75、分类颜色)这道题得到了启发,扫一遍直接实现分区,优雅地实现了三路快排。也祝愿大家都在追求优雅极致的代码的路上越走越远!

分区思路:设置两个指针,l记录第一个nums[a]的位置,l左边比nums[a]小,r记录最后以个nums[a]的位置,r右边比nums[a]大。
然后使用i从头到尾扫一遍,直到与r相遇。
i遇到比nums[a]小的数就换到左边去,遇到比nums[a]大的数就换到右边去,遇到nums[a]就跳过。
需要注意的是:
1、当遇到比nums[a]大的数就换到右边去,换回来的可能是比nums[a]小的数(或者nums[a]),i不能前进,因为i前面可能会有nums[a],要后续判断;
2、当遇到比nums[a]小的数就换到左边去,i每次都要前进,不能等待后续判断,因为l会前进,但是i却不动的话,l会跑到i前面,出错。
那为什么这里不需要后续判断呢?如果换回来的是比nums[a]小的数,那说明[0,l]区间内都是比nums[a]小的数,i前面不会有nums[a],l和i往前进即可。
上述两种情况如果换回来的是nums[a],就没啥可说的了,主要就是怕出现比nums[a]小的数前面有nums[a]的情况。

由此该数组分为4段:[0,left)-->比nums[a]小的数; [left,i)-->nums[a]; [i,right]-->乱序; (right,n-1]-->比nums[a]大的数

    void quick_sort2(vector<int>&nums, int first, int last)
    {
        if (first<last)
        {
            int a = first + rand() % (last - first + 1), aa = nums[a];
            int l = first, r = last, i = first;
            while (i <= r){
                if (nums[i] > aa){
                    swap(nums[i], nums[r]);
                    --r;
                }
                if (nums[i] == aa) ++i;
                if (i <= r && nums[i] < aa){
                    swap(nums[i], nums[l]);
                    ++l, ++i;
                }
            }
            quick_sort2(nums, first, l-1);
            quick_sort2(nums, r+1, last);
        }
    }

猜你喜欢

转载自blog.csdn.net/scarlett_guan/article/details/78799396