【排序算法】C语言实现随机快排,巨详细讲解

文章目录

  • 🚀前言
  • 🚀快排的核心过程partition(划分过程)
  • 🚀快排1.0
  • 🚀随机快速排序
  • 🚀稳定性

🚀前言

铁子们好啊!继续我们排序算法 今天要讲的是快排 ,通常大家所说的快排都是指随机快速排序,这里阿辉会详细的讲快排及其优化以及复杂度和稳定性的分析,话不多说开始我们今天的学习吧!!!

🚀快排的核心过程partition(划分过程)

在整个快排的过程中,快排最为核心的过程就是划分过程

划分过程:就是给定一个数作为划分值,将待划分的数组分成小于划分值的部分放在数组左边、等于划分值的部分在中间和大于划分值的部分在右边(为了方便,下文阿辉就直接简称为小于区、等于区和大于区)

对于划分过程是怎么样的思路呢?

对于一个数组的划分,我们需要三个指针来控制整个划分过程

用指针i来控制整个数组的遍历,用指针left来管理小于区域,用指针right来管理大于区域

假设我们取3作为划分值,i从左向右遍历数组,要分三种情况:

  • i指向的元素小于划分值时,i指向的数要与left指向的数字交换,然后++i同时++left
  • i指向的元素大于划分值时,i指向的数要与right指向的数字交换,然后--righti不变
  • i指向的元素等于划分值时,仅有i自增

为什么这么设计呢?
left控制着小于区,在left左边的元素都属于小于区;right控制着大于区,在right的右边的元素都属于大于区,而等于区的左边界是left右边界是rightleftright自身以及他们俩之间的元素都属于等于区。
++left代表小于划分值的元素发货到小于区,--right代表大于划分值的元素发货到大于区。为什么发货到小于区++i因为i是从左向右遍历的,left的数换到i位置不需要再看一遍了,而发货到大于区的时候,right的数换到i位置还没有看过,还得再看一遍它该发货到哪个区域。

对于上面的数组划分完是下面这样的:

让数组这样划分成这样后,对于中间的等于区域就不需要在管了,因为就算排好序它也应该在这个位置,前面都是小于它的后面都是大于大于它的,你说它是不是该在这!!!

附上partition函数的代码:

cpp 复制代码
void partition(int a[],int l,int r,int x){//划分函数
        //l是待划分区域左边界,r是待划分过程右边界,x是划分值
        int i = l;//遍历偏移量
        while(i <= end){//i>end时说明要划分的区域已经划分完成
            if(a[i] == x){//遇到等于区域数不用管,i直接遍历下一位置
                ++i;
            }else if(a[i] < x){//遇到小于区域数,发货到小于区域
                swap(a[i++],a[l++]);//l、i同时去到下一个位置
            }else{//遇到大于区域数发货到大于区域
                swap(a[i],a[r--]);//r同时去到下一位置
            }
        }
    }

划分过程是O(N)的时间复杂度,因为划分过程会遍历数组整个元素,跳出循环的条件是i>r,在每一次循环过程中都是i++或者--r,所以时间复杂度是O(N)

🚀快排1.0

有了上面的划分过程,就好办了,请出我们的老伙计------递归,当我们对于原数组等于区排好了,我们给原数组小于区域和大于区再次划分然后递归下去,递归到数组只有1个元素时数组天然有序作为我们的base case也就是递归的限制条件。

现在我们的重心就是要选定划分值,但是对于固定流程的程序,我们一定可以找到让这个程序最难受的数据状况,比如数组中只有1~9这9个元素,如果我们划分值选在数组最右的元素,对于下面这个数组

我们每次都会选到数组最大的元素,导致没有大于区域,而且一次递归只能排好一个位置的数,对于划分过程partition函数的时间复杂度是O(N),然后每次待排序数组递减,明显和冒泡、插入一个级别的O(N2)的时间复杂度,也就是说快排1.0的时间复杂度是O(N2)

代码:

cpp 复制代码
  int begin,end;
   void quicksort(int a[],int l,int r){//快排主逻辑
       if(l >= r) return;//base case终止条件
       int x = a[r];//以数组最右元素作为划分值
       partition(a,l,r,x);//给数组根据随机出的划分值,做划分
       //用临时变量捕捉当前的边界,全局变量会被子递归过程更改
       int left = begin;
       int right = end;
       quicksort(a,l,left - 1);//左部分递归
       quicksort(a,right + 1,r);//右部分递归
   }
   void partition(int a[],int l,int r,int x){//划分函数
       //l是划分区域左边界,r是划分过程右边界
       begin = l;//全局变量记录等于区域左边界
       end = r;//全局变量记录等于区域右边界
       int i = l;//遍历偏移量
       while(i <= end){//i>end时说明要划分的区域已经划分完成
           if(a[i] == x){//遇到等于区域数不用管,i直接遍历下一位置
               ++i;
           }else if(a[i] < x){//遇到小于区域数,发货到小于区域
               swap(a[i++],a[begin++]);//begin、i同时去到下一个位置
           }else{//遇到大于区域数发货到大于区域
               swap(a[i],a[end--]);//end、i同时去到下一位置
           }
       }
   }

我们想要的是O(NlogN)的快排,可不是这个和三大最挫排序一样效率的排序,好,让我们见识一下全盛时期的快排吧!!!

🚀随机快速排序

其实随机快排就比上面的快排1.0只换了一行代码,就让快拍的时间复杂度达到了O(NlogN)

代码:

cpp 复制代码
  int begin,end;
   void quicksort(vector<int>& a,int l,int r){//快排主逻辑
       if(l >= r) return;//base case终止条件
       int x = a[l + rand() % (r - l + 1)];//随机一个划分值
       partition(a,l,r,x);//给数组根据随机出的划分值,做划分
       //用临时变量捕捉当前的边界,全局变量会被子递归过程更改
       int left = begin;
       int right = end;
       quicksort(a,l,left - 1);//左部分递归
       quicksort(a,right + 1,r);//右部分递归
   }
   void partition(vector<int>& a,int l,int r,int x){//划分函数
       //l是划分区域左边界,r是划分过程右边界
       begin = l;//全局变量记录等于区域左边界
       end = r;//全局变量记录等于区域右边界
       int i = l;//遍历偏移量
       while(i <= end){//i>end时说明要划分的区域已经划分完成
           if(a[i] == x){//遇到等于区域数不用管,i直接遍历下一位置
               ++i;
           }else if(a[i] < x){//遇到小于区域数,发货到小于区域
               swap(a[i++],a[begin++]);//begin、i同时去到下一个位置
           }else{//遇到大于区域数发货到大于区域
               swap(a[i],a[end--]);//end、i同时去到下一位置
           }
       }
   }

就改了对与划分值的选取,用了随机的方式,在数组中随机选取一个元素作为划分值,随机快速排序的时间复杂度是O(NlogN),这里是为什么呢,阿辉也只是记住,并不知道原理,这是数学家把每一种结果的概率都求出来,然后算出期望,随机快排的时间复杂度收敛于O(NlogN),在算法导论上有证明,感兴趣的小伙伴可以去研究

🚀稳定性

随机快排是没有稳定性的,换句话说是划分过程没有稳定性,比如:

对于这样的数组,i位置与right一换,最后位置的2一下子跨越他前面那么多2,所以快排没有稳定性,但是快排比归并以及堆排序都快,是常数时间上快


相关推荐
牵手夏日几秒前
题目类型——左右逢源
算法
愚润求学30 分钟前
【递归、搜索与回溯】FloodFill算法(一)
c++·算法·leetcode
sunny-ll2 小时前
【C++】详解vector二维数组的全部操作(超细图例解析!!!)
c语言·开发语言·c++·算法·面试
西装没钱买3 小时前
C语言多进程TCP服务器与客户端
服务器·c语言·tcp/ip·进程
嵌入式@秋刀鱼3 小时前
《第四章-筋骨淬炼》 C++修炼生涯笔记(基础篇)数组与函数
开发语言·数据结构·c++·笔记·算法·链表·visual studio code
嵌入式@秋刀鱼3 小时前
《第五章-心法进阶》 C++修炼生涯笔记(基础篇)指针与结构体⭐⭐⭐⭐⭐
c语言·开发语言·数据结构·c++·笔记·算法·visual studio code
简简单单做算法3 小时前
基于PSO粒子群优化的VMD-LSTM时间序列预测算法matlab仿真
算法·matlab·lstm·时间序列预测·pso·vmd-lstm·pso-vmd-lstm
无聊的小坏坏3 小时前
高精度算法详解:从原理到加减乘除的完整实现
算法
whoarethenext3 小时前
使用 C/C++的OpenCV 裁剪 MP4 视频
c语言·c++·opencv
愚润求学3 小时前
【递归、搜索与回溯】FloodFill算法(二)
c++·算法·leetcode