【排序算法】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,所以快排没有稳定性,但是快排比归并以及堆排序都快,是常数时间上快


相关推荐
洛水水16 小时前
【力扣100题】16.两两交换链表中的节点
算法·leetcode·链表
wuweijianlove16 小时前
算法教学中的抽象建模与动态可视化设计的技术7
算法
2zcode16 小时前
基于改进YOLO11算法的芯片微缺陷检测系统(UI界面+数据集+分析界面+处置建议+训练代码)
算法·芯片缺陷
leoufung16 小时前
LeetCode 30:Substring with Concatenation of All Words 题解(含 C 语言 uthash 实现)
c语言·leetcode·c#
王老师青少年编程16 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:荷马史诗
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·荷马史诗
爱编码的小八嘎16 小时前
C语言完美演绎9-6
c语言
样例过了就是过了16 小时前
LeetCode热题100 最小路径和
c++·算法·leetcode·动态规划
Aaron158817 小时前
RFSOC+VU13P+GPU 在6G互联网中的技术应用
大数据·人工智能·算法·fpga开发·硬件工程·信息与通信·信号处理
迷途之人不知返17 小时前
Stack & Queue
c++·算法
SunnyByte17 小时前
线性表——单链表的增删查改操作
c语言·单链表