当前位置: 首页 > news >正文

快速排序(JAVA详细讲解快速排序的四种方式)

1.快速排序的实质

快速排序本质上是在数组的一个范围内,对未经划分的数据,通过计算,分为左右两区或者三区,从左到右判断一个数属于左边的小于等于区(或分开),还是右边的大于区,判断一次,让两边的区域扩展一次

2.快速排序1.0

以给定范围内最右边的数为排序的基准,比该数大的放在右边的大于区,反之则放在小于区,将大于区设置在R处(包含),小于等于区设置在L-1处,从范围内的左边到右边进行遍历

如果这个数属于左边的区域,那么将下标所指的数与小于区下一个数做交换,同时小于区扩大,判断数的下标+1

如果这个数大于基数,那么将下标所指的数与大于区的下一个也就是左边的数做交换,大于区扩大,但是判断数字的下标不+1

因为我们要判断的是没有经过计算的数,而刚才换过来的数,也没有经过判断,因此我们需要对该数也进行一个判断

到最后,将判断数的下标与大于区的下标重叠,我们再将最右边的数换到大于区的最左边,这时候呢大于区指针左边指的是小于等于区的最右边,而大于区指针++则是他区域的最左边

大于区指针所指的数肯定是基准数,我们返回 传入函数的L和小于区下标++ 以及大于区下标和传入函数的L即可(这里面小于区下标可能没有动,可能是-1,大于区没有动的话是R,怎么传取决于后面你想要在第二次更细节的快排时怎么用,自己不混淆即可)

//快排1.0 <=区 >区,然后i<大于区 让最右边的掉回来,public static void partion1(int[] arr, int L, int R){if (arr==null|| arr.length<2){return;}if (L>=R) {return;}int eqIndex=quickSort(arr,L,R);partion1(arr,L,eqIndex-1);partion1(arr,eqIndex+1,R);}public static int quickSort(int[]arr,int L,int R){int num=arr[R];//int Lp=L-1;int Rp=R;int i=L;while (i<Rp){if (arr[i]<=num)i++;else {int temp=arr[i];arr[i]=arr[Rp-1];arr[Rp-1]=temp;Rp--;}}arr[R]=arr[i];arr[i]=num;return i;}
3.快速排序2.0

我们刚才呢,每次根据最右边的数进行分区时,只传回来了一个数,仔细想一下会发现,其实可能等于区不止一个,那怎么进行划分呢?

1.传入要判断数组的最左边L 和最右边R

2.设置小于区下标为L-1 大于区下标为R+1

3.将R处的数字用一个变量提出来,以便后续判断

4.判断下标从L开始直到判断下表与大于区下标重叠

5.当所判断数与基准数相等时,小于区和大于区的指针均不移动,判断下标++

6.当所判断的数小于基准数时,我们就应该交换小于区指针下一个单位的数与当前所判断数 然后让小于区的指针右移,判断下表++

7.当判断书大于基准数时,交换大于区指针--的数,与当当前数做交换,大于区指针--,判断下表不动,因为换过来的数没有进行判断

//2.0 等于区//以最右边为界限去选一个数来进行分区// 之后返回等于区间的第一个,和最后一个// 因为可能大于小于区的指针是-1或者超length//特殊情况呢是L>R 和L=R//相等时,返回的是L R 大于时返回的是-1 -1public static void possess2(int []arr,int L,int R){if (L>=R)return;int []equalnums=equalflag(arr,L,R);possess2(arr,L,equalnums[0]-1);possess2(arr,equalnums[1]+1,R);}public static int[] equalflag(int[] arr,int L,int R){if (L>R){return new int[]{-1,-1};}if (L==R){return new int[]{L,R};}int less=L-1;//以右边的数为交换的基准int more=R;int i=L;while (i<more){//如果是=那就直接右移指针//如果是小于那就变化less指针,交换less下一个与i i位置肯定是小的// less下一个可能是等于区,也可能是i 同时i++这样就肯定正确了//如果是大于,那就变换与more指针前面数的位置,同时i不++因为传过来的数if (arr[i]==arr[R])i++;else if (arr[i]<arr[R]){int temp=arr[i];arr[i]=arr[less+1];arr[less+1]=temp;less++;i++;}else {int temp=arr[i];arr[i]=arr[more-1];arr[more-1]=temp;more--;}}//more区有一个是=的int temp=arr[R];arr[R]=arr[i];arr[i]=temp;//more肯定不会越界,less可能会越界return new int[]{less+1,more};}
3.快速排序3.0

刚才呢我们一直都是用最右边的数作为基准,如果传入的数组是随机的,那还好,但是如果判断的数组不是随机的,那么该算法的时间复杂度就会提升,当数组每次划分区域刚好为原来的一半时,时间复杂度最低,那怎么能够确保呢?

我们随机挑选L-R上的数为基准数,之后呢通过复杂的数学计算得到期望就等于NlogN,接近于每次划分为两半的递归然后用master公式进行计算

每次划分两半,每次规模减半,logb a=1 而每次递归前进行的数组运算的时间复杂度为O(N)

master公式计算得到结果,二者相等,时间复杂度的结果为O(N的logb a次方 *logN)=O(N的logb a次方 *logN)

//3.0 随机选一个数public static void possess3(int arr[],int L,int R){if (L>=R)return;int []equationNum=randomEquateSession(arr,L,R);possess3(arr,L,equationNum[0]-1);possess3(arr,equationNum[0]+1,R);}public static int [] randomEquateSession(int arr[],int L,int R){//随机选一个是为了制造随机,给的数组可能不是随机的,给的数组如果是最差的//每一次分的两半极不均匀,那么复杂度就不能是NlogN了//那么我们如果固定选R,时间复杂度就不能是NlogN了,但是如果是随机的那么数学最后算的就是NlogNif (L>R){return new int[]{-1,-1};}if (L==R){return new int[]{L,R};}int randomIndex= L+(int) (Math.random()*(R-L+1));int compareNum=arr[randomIndex];int less=L-1;int more=R+1;int i=L;while (i<more){if (arr[i]==compareNum)i++;else if (arr[i]<compareNum){int temp=arr[i];arr[i]=arr[less+1];arr[less+1]=temp;i++;less++;}else {int temp=arr[i];arr[i]=arr[more-1];arr[more-1]=temp;more--;}}return new int[]{less+1,more-1};}
4.快速排序4.0(非递归)

我们前三种均为计算出下一次两个分区的左右边界,之后调用函数进行递归调用,这样很明显,太过于耗费内存,那怎么转为非递归的呢?

我们把步骤拆到最小的会发现我们是,先在数组的L R范围做以基数为标准的划分区域,之后对每个区域再进行细化分,ok这就是最原始的步骤

我们利用自定义栈,对最原始的步骤进行模拟,运算,实现即可,以减少内存的损耗,减少内存溢出的可能性以及时间的损耗

//用模拟栈实现 非递归形式//我们刚才的缺点是什么?一直在递归函数,会导致内存溢出//如果我们手动模拟,然后每次是选择循环判断进行操作//那么内存耗费就会大大减少//我们只需要用栈模拟每次返回的R L即可//不需要开辟新的空间去耗费内存public static void partionSort(int arr[]){if (arr==null||arr.length<2)return;Stack<int[]> stack=new Stack();stack.push(new int[]{0,arr.length-1});while (!stack.empty()){int[] pop = stack.pop();if (pop[0]>=pop[1])continue;//左右边界是pop范围的+1 -1int randomIndex=pop[0]+(int) (Math.random()*(pop[1]-pop[0]+1));int less=pop[0]-1;int more=pop[1]+1;int i=less+1;int compareNum=arr[randomIndex];while (i<more){if (arr[i]==compareNum)i++;else if (arr[i]<compareNum){int temp=arr[i];arr[i]=arr[less+1];arr[less+1]=temp;less++;i++;}else {int temp=arr[i];arr[i]=arr[more-1];arr[more-1]=temp;more--;}}if (less-pop[0]>pop[1]-more){stack.push(new int[]{pop[0],less});stack.push(new int[]{more,pop[1]});}else {stack.push(new int[]{more,pop[1]});stack.push(new int[]{pop[0],less});}}}

http://www.dtcms.com/a/507849.html

相关文章:

  • 数据结构四大简单排序算法详解:直接插入排序、选择排序、基数排序和冒泡排序
  • 官渡网站建设wordpress单页面制作
  • 企业电子商务网站开发数据库设计昆明seo博客
  • 东道 网站建设erp系统哪家做得好
  • 现代 Web 开发中检测用户离开页面的完整方案(附 Vue 实现)
  • [crackme]029-figugegl.1
  • 网站建站分辨率腾讯企点怎么注册
  • 第四章:L2CAP 的“数据语言”——揭秘蓝牙通信的报文格式
  • 【代码随想录算法训练营——Day43(Day42周日休息)】动态规划——300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组
  • block的样式有哪些?如果copy的话分别会有啥样式
  • 如何做网络投票网站大数据开发工程师
  • 提示词 prompt 快速上手
  • 网站降权查询工具lnmp中安装wordpress
  • 一个空间放两个网站蓟门桥网站建设
  • DPC和DPC-KNN算法
  • git中tag标签远程管理
  • Babylon.js UtilityLayerRenderer 深度解析:创建3D工具与调试层的完整指南
  • 如何制造一个网站网站的图片怎么更换
  • 区块链安全评估:守护数字世界的“安全密码”
  • 多语言网站建设公司教你做企业网站
  • 第19节-非规范化数据类型-Drop-Type
  • docker desktop的容器间通信
  • 宝安做网站的公司企业文化经典句子
  • 学校二级网站建设百度关键词优化怎么做
  • 百度前端面试准备
  • 立创EDA学习(一、新建项目与自定义元件)
  • dify项目智能记账
  • 使用Jmeter进行接口测试:HTTP请求与响应报文结构详解
  • 前端6:CSS3 2D转换,CSS3动画,CSS3 3D转换
  • Python中使用SQLite