三种快排算法理解

这篇具有很好参考价值的文章主要介绍了三种快排算法理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

快速排序是目前比较常用的排序算法,也是需要掌握的排序算法,光听它的名字就知道这种算法的运算速度很快,没错!这是目前已知的算法中平均排序速率最快的。当然这里是说只使用一种排序算法比较的前提下。

快速排序算法主要分为以下几步:

1)选择基准值
2)双指针操作将小于基准的放左边,大于的放右边
3)重复2操作,直至结束

快速排序算法是利用排序轮数不变,每轮排序只比较了log2n次来提高排序速度,这与堆排序,归并排序的原理类似。但是快速排序没有初始建堆的时间和不占用相同大小排序序列空间的优势。

目前快速排序算法主要有三种:左右指针法,挖坑法,前后指针法

下面我先给出递归操作的代码:

public static void quicksort(int[] arrs,int start,int end){
       if(start>=end)
           return;
       int mid = sort(arrs, start, end);
       quicksort(arrs, start, mid-1);

       quicksort(arrs, mid+1, end);
}

左右指针法:
大致过程是先以arrs[0]作为基准,然后让j往又从左走,i从左往右走,(注意如果以arrs[0]作为基准,那么就必须让j先执行,具体原因下面说),然后当出现arrs[j]小于基准时,arrs[i]大于基准时,交换arrs[i]和arrs[j]的值,然后继续执行。直到i,j相遇,最后交换arrs[start]与arrs[i]的值即可。

public static int sort1(int[] arrs,int start,int end){
       int i=start,j=end;
       int tmp = arrs[i];
       while(i<j){
          while (arrs[j] >= tmp && i<j)
               j--;
           while (arrs[i] <= tmp && i<j)
               i++;
           swap(arrs[i], arrs[j]);
       }
       swap(arrs[i], arrs[start]);
       return i;
}

这种方式也是我们最常见的方式,但是这种方式在测试的时候一不小心就会出现上面的错误,现在我们就说一下出错的原因:

eq:2, 6, 9, 3, 0, 1 -->2, 1, 9, 3, 0, 6–>2, 1, 0, 3, 9, 6 ------------->3, 1, 0, 2, 9, 6

例如上面的一组数,首先以2为基准,第一次循环i指向6,j指向1,i,j交换,到第二列数;

然后进行第二次循环i指向9,j指向0,i,j交换,到第三列数;

此时如果让i先走,i遇到3停止,接着j走,但是i,j相遇,不能继续执行,i,j交换后退出循环,此时交换arrs[start]和arrs[i];到第四列数;

这时就会发现一轮交换下来的数出现了错误,而让j先走的话就不会出现这样的错误,读者可以按照上面的数和代码自己分析。

挖坑法:

大致过程也是先以arrs[0]作为基准并赋给一个临时变量tmp,然后让j往又从左走,i从左往右走,(这种方式也需要j先走),然后当出现arrs[j]小于基准时,将arrs[j]赋值给arrs[i],然后当arrs[i]大于基准,将arrs[i]赋值给arrs[j],继续执行。直到i,j相遇,最后将tmp赋值给arrs[i];

public static int sort2(int[] arrs,int start,int end){
   int i=start,j=end;
   int tmp = arrs[i];
   while(i<j){
       while (arrs[j] >= tmp && i<j)
           j--;
       arrs[i]=arrs[j];
       while (arrs[i] <= tmp && i<j)
           i++;
       arrs[j]=arrs[i];
   }
   arrs[i]=tmp;
   return i;
}

这种方式和它的名字相同,先将arrs[0]的值挖出来赋给tmp,这时只有arrs[0]一个坑,所以也需要j先走,当j退出内循环时,挖出arrs[j]的值赋给arrs[i],此时arrs[j]里面的值就会变成一个废值,接着继续执行,最后再将tmp的值赋给最后一个废坑中。

前后指针法:

大致过程也是先以arrs[0]作为基准并赋给一个临时变量tmp,利用两个指针怕q,p分别指向下一个位置和当前位置,当小于基准时,p跟在q的后面,如果出现大于基准时,q向后移动,p不动,然后当再次出现小于基准的值时且p没有跟在q的后面,p,q位置上的值交换,继续执行,最后将p指向的值与start位置上的值互换。

public static int sort3(int[] arrs,int start,int end){
   int p=start,q = start+1;
   int tmp = arrs[p];
   while(q<=end) {
       while(tmp>=arrs[q] && ++p!=q)
           swap(arrs[p], arrs[q]);
       q++;
   }
   arrs[start] = arrs[p];
   arrs[p] = tmp;
   return p;
}

这是这三种快速排序算法设计思路最奇特的方法,而且这种方法还可以用于链表的排序,个人还是比较欣赏这种算法的设计者,而且这种思路的代码设计也比较巧妙。

以上三种都是快速排序的实现方式,三种方法虽然想法上不太一致。但都达到了快速排序的要求。

缺陷:快速排序也有自身的缺点,因为快速排序对于基准值的要求比较高,如果基准值选择不当,就会导致排序效率严重降低。

eq 1,2,3,4,5,6,7,8

比如上面的这个序列如果使用快速排序并以第一个数为基准值,则排序的时间复杂度则为(n*n),而且快速排序还是一种不稳定的排序。但是这并不能掩盖它的优势,它依旧比较火的排序。

当然,网上对于基准值的选择也有一些优化的手段,具体的方法可以参考下面参照链接。

后续:很遗憾java8中的Collections.sort()方法并没有使用快速排序算法,可能是因为快速排序对于基准值难以把握。那就有点奇怪了,快速排序算法明明号称是速度第一的排序算法,为什么java8不用它,难道编写java8的人不知道吗?当然不是,值得一提的是java8中的sort方法并没有单独的利用某一个排序算法,而是充分利用了八大排序算法的优势,当排序序列小于32时使用折半查找的直接插入算法,当大于32时使用归并排序算法分割序列,序列小于32时依旧使用折半查找的直接插入算法,不过其中还有很多很多的优化策略,例如当一对序列进行归并时,归并算法需要重新分配与之长度相同的一段数组空间,很是浪费空间,但是java8先计算出不需要排序的子序列,然后只new出较短序列长度相同的数组存储临时值,与普通的归并算法比较,可以节省至少一半的空间。再其次是较短序列排序时,使用直插排序更好,所以当分隔到32长度时,选择使用直插算法。然而排序直接插入算法的时间复杂度为(nn),而java8利用折半查找的方式让每一轮比较只进行log2N次,这样总的排序效率可以达到(nlog2N)的效果,而且任何序列都可以保证等等。总的来说,每一种排序算法都有其优势和劣势,但是如果能够充分利用各个排序算法的优势,就能够达到最佳的效果。

参照:快速排序(三种算法实现和非递归实现)文章来源地址https://www.toymoban.com/news/detail-714935.html

到了这里,关于三种快排算法理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构——三路划分(快排优化)

    刷Leetcode时遇到的问题,用普通的快排去跑,发现有问题。  普通的Hoare或者其他的快排好像都没有直接解决掉这个问题,当一个数重复出现的时候,用普通的快排效率其实并没有那么高。所以,这也是普通快排的缺点之一。 所以,在一个元素重复出现多次的时候,可以用三

    2024年02月08日
    浏览(50)
  • 【数据结构】快排的详细讲解

    江河入海,知识涌动,这是我参与江海计划的第7篇。 目录:         快排是排序算法中效率是比较高的,快排的基本思想是运用二分思想,与二叉树的前序遍历类似,将数据划分,每次划分确定1个基准值(就是已经确定好有序后位置的数据),以升序为例,基准值左面的数据

    2024年02月06日
    浏览(91)
  • 【数据结构】排序之交换排序(冒泡 | 快排)

    在之前的博客中介绍了插入排序,有需要的可以点这个链接: link,这次来介绍交换排序,包括冒泡和快排。 话不多说,正文开始。 基本思想 :所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置。 交换排序的特点是:将键值较大的记录向序

    2024年02月03日
    浏览(39)
  • (一)深入理解Mysql底层数据结构和算法

    索引是帮助MySQL高效获取数据的排好序的数据结构 数据结构模拟网站:Data Structure Visualization 二叉树 不适合做自增ID的数据结构。如下示意图,假设采用二叉树作为表自增主键ID的数据存储结果如下:当查询id为5的数据时,其查询次数为5次 红黑树 不适合做mysql的索引,因为当

    2024年01月25日
    浏览(75)
  • 【算法与数据结构】二叉树的三种遍历代码实现(下)—— 非递归方式实现(大量图解)

     上篇: 【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解_Hacynn的博客-CSDN博客 https://blog.csdn.net/zzzzzhxxx/article/details/133609612?spm=1001.2014.3001.5502 目录 前言 1、先序遍历 1.1、详细图解描述 1.2、先序遍历非递归代码实现  2、中序遍历 2.1、详细图解描

    2024年02月08日
    浏览(39)
  • 【C语言】【数据结构初阶】 快排变慢排?怎么个事儿?

    我们知道,快排是一种很好的排序算法。但是在 极少数 的一些情况下,“快速排序”好像名不副实了。 当数据量非常大,且递归深度太深,有栈溢出的风险。 这样,我们就得到了一个很可惜的结论:快排不是万金油。 但是,这是指的 递归版本 的快排,我们可以写 非递归

    2024年02月17日
    浏览(54)
  • 【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解

      本篇博客 (上篇) 先带大家学习 递归方式 进行三种遍历, 而在后续的 (下篇) 中将为大家详细讲解非递归的三种遍历方式。 目录 1、二叉树 2、二叉树的递归遍历 2.1、先序遍历 2.2、中序遍历 2.3、后序遍历  二叉树(Binary tree)是树形结构的一个重要类型。许多实际问

    2024年02月08日
    浏览(44)
  • Mysql性能调优——1.深入理解Mysql索引数据结构和算法

    本系列所说的Mysql性能调优,主要是针对开发者在实际环境中的sql调优,代码层面上的优化。不涉及到mysql底层代码的调优。 我们知道,一个mysql数据表,数据量小的时候,可能简单的查询耗时不会太久,性能也可以接受。但当数据量大的时候,查询速度会很缓慢。这时候我们

    2024年02月09日
    浏览(38)
  • 深入理解Java线程池ThreadPoolExcutor实现原理、数据结构和算法(源码解析)

    什么是线程池?         线程池主要是为了解决执行新任务执行时,应用程序为减少为任务创建一个新线程和任务执行完毕时销毁线程所带来的开销。通过线程池,可以在项目初始化时就创建一个线程集合,然后在需要执行新任务时重用这些线程而不是每次都新建一个线

    2024年02月07日
    浏览(45)
  • 【数据结构】稀疏矩阵存储的三种方法及三元组表示稀疏矩阵转置算法的两种实现 —— C++

    1. 三元组顺序表数据结构 注意:data[]中的元素是按行存储或者按列存储的,所以 在将三元组逆置时,不能简单地将行列下标对换,data[]数组中元素的顺序也需要重新排列 2. 三元组表示稀疏矩阵转置算法1 3. 三元组表示稀疏矩阵转置算法2:快速转置 为了 便于随机存取任意一

    2024年02月05日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包