快速排序的几种写法(c++)

快速排序

快速排序一般使用双指针与分而治之的思路来进行排序,所有函数都使用左闭右开区间
快排的时间复杂度:O(NlogN)
空间复杂度:logN
不稳定:排序算法不稳定的定义是如果相等的数发生了交换,那么就不稳定
快排遍历条件含有等于,也就是key值交换后,等于它的可能排在key前面了,所以不稳定,如果一个数组全部相等,快排的效率很低

双边遍历1:双指针相遇

1.选定一个比较点(一般使用第一个元素)key,两个指针first(初始值0),last(初始值:最后一个元素),之所以采用左闭右闭区间,是因为key值必然小于key值,所以可以从第一个元素开始遍历
2.从两侧遍历数组,first值小于等于key,first前进,last值大于等于key,last前进,
注:一定要包含等于,若是只有小于,遇到等于就会暂停,同理last也是如此,直接交换,而交换之后仍然不能继续遍历,函数失效
3.若情况相反,则指针暂停,两指针指向的值交换,直到两个指针相遇
4.那么此时指针的左侧数一定都小于等于key,右侧都大于等于key
5.再将key与指针相遇处值交换,先进行first的遍历,那么就与first交换
6.交换后,对first左侧与右侧数递归调用函数,最终全部有序

void quick_sort(vector<int> &nums, int l, int r) //1.029   //左闭右开
{
    
     //左闭右开
    if (l + 1 >= r)   //数组只有一个数或者为空,直接返回
        return;
    int first = l, last = r - 1, key = nums[first]; //左闭右闭  5 2 3 4 1  1 2 3 4 5
    while (first < last)
    {
    
    
        while (first < last && nums[first] <= key)   //这一步的first<last可以省略
            first++;
        while (first < last && nums[last] >= key)   //这一步不能省
            last--;
        if (first < last)                   //出循环时可能是first==last因此只有first<last才交换
        {
    
    
            swap(nums[first], nums[last]);
        }
    }  
    //*如果最后一次交换时,last=first+1,先进行first的循环,那么first=last退出循环,也不会进行交换,此时的first值大于key,直接交换就会出问题*
    if(nums[first]<=key){
    
          //确定first是不是小于key,小于则交换
       swap(nums[l], nums[first]);
    }
    else {
    
    
        swap(nums[l], nums[first-1]);  //大于则和first前一位交换,first=last,在key两边均有可能
        first=first-1;
    }   
    quick_sort(nums,l,first);  //递归排序左边
    quick_sort(nums,first+1,r); //递归排序右边
}

双边遍历2:循环插空代替值的交换

思想上和双边遍历没有区别,只是在如何交换值上有所改动
1.第一次循环前将first的值保存,那么first就可以看作一个空位,这个空位理所当然应该填入比key值小的元素
2.要找到比key值小的元素,那么就应该先使用last遍历数组,遇到比key小的数直接填入first的空位,此时last位置又空出来了
3.再使用first遍历数组,找到比key值大的数,再填入last的空位,这时候first又空出,循环2-3步
4.如果到达临界条件,last出循环后等于first,也只是自身赋值给自身,没有影响
5.由于这个方法是从last开始循环,**因此相等的时候应该是last去找first,此时相遇点的值是first,必然小于等于key,**不需要再进行判定
因此双指针遍历还是从last开始为好

void quick_sort1(vector<int> &nums, int l, int r)  //1.172
{
    
    
    if (l + 1 >= r)
    {
    
    
        return;
    }
    int first = l, last = r - 1, key = nums[first];   //first是个空位
    while (first < last)
    {
    
    
        while (first < last && nums[last] >= key)
        {
    
    
            --last;
        }
        nums[first] = nums[last];          //比key小的数填入空位first,last空出
        while (first < last && nums[first] <= key)
        {
    
    
            ++first;
        }
        nums[last] = nums[first];       //比key大的数填入last,first空出,如果last都没进循环,也只是一次简单的交换
    }
    nums[first] = key;   //最后key填入空位
    quick_sort1(nums, l, first);
    quick_sort1(nums, first + 1, r);
}

双边遍历3:双指针相交

1.这种快排也是双边遍历,不过将出循环的临界值设置为first、last相交,而不是相遇,这样如果相遇之前有比较多相同的值,两个指针也可以跳过直到遇到比key大的first和比key小的last,不会受到相遇的影响而停住。
2.出循环后如果相交,退出循环,如果不相交,就交换
3.遍历之后,last必定比key小,first必定比key大,必然是相交,那么first和last之间的值可能是相等且等于key的,或者first=last+1;
4.这时候交换,只能交换key与last,last前面的数都不大于key

void quick_sort2(vector<int> &nums,int l,int r){
    
      //1.129
        if(l+1>=r) return;
        int first=l,last=r-1,key=nums[l];
        while(1){
    
    
            while(first<r&&nums[first]<=key) first++;
            while(last>l&&nums[last]>=key) last--;
             //只要找出最大选择点,因此不需要顾及相遇之后浪费的步数,优先找到last的下界
            if(first>=last) break;
            swap(nums[first],nums[last]);
        }
        swap(nums[last],nums[l]);
        quick_sort3(nums,l,last);
        quick_sort3(nums,last+1,r);
    }

单边遍历:快慢指针

仍然是双指针,不过是快慢指针从数组的同一侧遍历
1.fast从slow的后一位遍历,如果遇到小于key的值,slow向前移动一位,再交换,这样slow每走过的地方都小于key
注:如果slow+1=fast,那么自身交换没问题,如果slow+1<fast,说明slow+1是个不小于key的数,此时与fast交换,本质上和双边遍历的核心相同
2.fast继续遍历至数组末尾,这样数组所有小于key的值都在slow的左边(包括slow本身),除了key不小于自身,这时将slow与key交换,那么slow左边就都小于key了
3.slow两边递归,完成排序

void quick_sort3(vector<int> &nums,int l,int r){
    
      //1.162
    if(l+1>=r) return;
    int slow=l,fast=l+1, key=nums[l];   //快指针从key后面开始遍历
    while(fast<r){
    
    
        if(nums[fast]<key){
    
                    //遇到比key小的才停下,fast之前的都是比key大或者等于的
           swap(nums[++slow],nums[fast]);  //slow向后移动一格,可能正好等于fast,也可能比key大,都进行交换
           }
        fast++;        //fast继续遍历,遍历一遍为止,slow及之前数都小于key
    }
    swap(nums[slow],nums[l]);  //slow与l交换
    quick_sort2(nums,l,slow);
    quick_sort2(nums,slow+1,r);
}  

测试程序

#include <iostream>
#include <vector>
#include <time.h>
#include "sort.h"
#define MAXK 1000000
using namespace std;
int main(){
    
    
    
    //vector<int> nums = {1,3,5,7,2,6,4,8,9,2,8,7,6,0,3,5,9,4,1,0};
    //vector<int> temp(nums.size());
    clock_t start,stop;
    start=clock();
    for(int i=0;i<MAXK;i++){
    
    
        vector<int> nums = {
    
    1,3,5,7,2,6,4,8,9,2,8,7,6,0,3,5,9,4,1,0};
        quick_sort(nums,0,nums.size());
    }
    stop=clock();
    double x=((double)(stop - start))/CLK_TCK;
    cout<<x<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40602655/article/details/115388687