快速排序图解(两种思想)
创始人
2024-01-16 22:13:27
0

七大排序之快速排序

文章目录

  • 七大排序之快速排序
  • 前言
  • 一、《算法导论》中的分区思想
    • 1.1 算法思想
    • 1.2 代码实现
  • 二、Hoare挖坑法
    • 2.1 算法思想
    • 2.2 代码实现
  • 三、算法分析
  • 四、注意事项
  • 总结


前言

博主个人社区:开发与算法学习社区

博主个人主页:Killing Vibe的博客

欢迎大家加入,一起交流学习~~

一、《算法导论》中的分区思想

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

动图如下:
在这里插入图片描述

1.1 算法思想

快速排序是20世纪最伟大的算法之一

核心的思路就是分区

分区值:默认选择最左侧元素pivot(当然也可以随机选择)

  1. 从无序区间选择一个值作为分界点pivot开始扫描原集合
  2. 将数组中所有小于该pivot的元素放在分界点左侧
  3. 大于等于该元素的值放在分区点的右侧
  4. 经过本轮交换,pivot放在了最终位置,pivot的左侧都是小于该值的元素,pivot的右侧都是大于该值的元素,在这两个子区间重复上述过程,直到整个集合有序。

举个栗子:

在这里插入图片描述
1.若arr[i] >= v

在这里插入图片描述

2.若arr[i] < v

索引 j 指向了最后一个 < v 的元素,而 j+1 恰好是第一个 >= v的元素

在这里插入图片描述

3.当 i 扫描完集合时,数组划分如下:

在这里插入图片描述
4.交换 l 和 j 所在的元素

在这里插入图片描述
5.橙色和紫色的部分继续重复上述过程即可

1.2 代码实现

代码如下:(请往后看)

    private static void quickSortInternal(int[] arr, int l, int r) {// 2.小区间上使用插入排序来优化,不用递归到底if (r - l <= 15) {insertionSort(arr,l,r);return;}int p = partition(arr,l,r);// 继续在左右两个子区间进行快速排序// 所有 < v的元素quickSortInternal(arr,l,p - 1);// 所有 >= v的元素quickSortInternal(arr,p + 1,r);}private static int partition(int[] arr, int l, int r) {// 1.优化1.使用一个随机位置作为分区点,避免快排在近乎有序数组上的性能退化int randomIndex = random.nextInt(l,r);swap(arr,l,randomIndex);int v = arr[l];// arr[l + 1..j] < v// 最开始区间没有元素int j = l;// arr[j + 1..i) >= v// 最开始大于区间也没有元素for (int i = l + 1; i <= r; i++) {if (arr[i] < v) {swap(arr,i,j + 1);j ++;}}// 此时元素j就是最后一个 < v的元素,就把v换到j的位置swap(arr,l,j);return j;}

注意:这是优化后的代码

在这里插入图片描述

小数组采用插入排序可以提高性能,若不能理解可以把这段改成:

 if (r - l <= 0) return;  

非递归写法:

    public static void quickSortNonRecursion(int[] arr) {Deque stack = new ArrayDeque<>();// rstack.push(arr.length - 1);// lstack.push(0);// 每次从栈中取出两个元素,这辆个元素就是待排序区间的l..rwhile (!stack.isEmpty()) {int l = stack.pop();int r = stack.pop();if (l >= r) {// 当前子数组已经处理完毕continue;}int p = partition(arr,l,r);// 继续入栈两个子区间stack.push(p - 1);stack.push(l);stack.push(r);stack.push(p + 1);}}private static int partition(int[] arr, int l, int r) {// 1.优化1.使用一个随机位置作为分区点,避免快排在近乎有序数组上的性能退化int randomIndex = random.nextInt(l,r);swap(arr,l,randomIndex);int v = arr[l];// arr[l + 1..j] < v// 最开始区间没有元素int j = l;// arr[j + 1..i) >= v// 最开始大于区间也没有元素for (int i = l + 1; i <= r; i++) {if (arr[i] < v) {swap(arr,i,j + 1);j ++;}}// 此时元素j就是最后一个 < v的元素,就把v换到j的位置swap(arr,l,j);return j;}

二、Hoare挖坑法

目前市面上和教科书上的常用分区方法。

2.1 算法思想

  1. 先从序列中随机选一个pivot,默认从最左边元素

  2. 将两个索引 i 和 j 分别从左右两边开始往中间遍历

  3. 先让 j 从后往前找到第一个 < v 的元素停止,把这个元素直接赋值给i所对应得元素。

  4. 再让 i 从前往后找到第一个 > v 的元素停止

  5. 当 i 和 j 重合时,arr[i] = pivot 即可~

没有元素交换的时候都是直接赋值,理论上会减少因为交换带来的时间损耗

2.2 代码实现

代码如下:

    private static void quickSortInternalHoare(int[] arr, int l, int r) {// 2.小区间上使用插入排序来优化,不用递归到底if (r - l <= 15) {insertionSort(arr,l,r);return;}int p = partitionHoare(arr,l,r);// 继续在左右两个子区间进行快速排序// 所有 < v的元素quickSortInternalHoare(arr,l,p - 1);// 所有 >= v的元素quickSortInternalHoare(arr,p + 1,r);}/*** 挖坑分区法* @param arr* @param l* @param r* @return*/private static int partitionHoare(int[] arr, int l, int r) {int randomIndex = random.nextInt(l,r);swap(arr,l,randomIndex);int pivot = arr[l];int i = l;int j = r;while (i < j) {// 先让j从后向前扫描到第一个 < v的元素停止while (i < j && arr[j] >= pivot) {j --;}arr[i] = arr[j];// 再让i从前向后扫描到第一个 > v的元素停止while (i < j && arr[i] <= pivot) {i ++;}arr[j] = arr[i];}arr[i] = pivot;return i;}

三、算法分析

快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现来。

时间复杂度: O(nlogn)

  • n:每次分区函数的数组扫描
  • logn:递归次数,”递归树“的高度

空间复杂度:递归调用次数 O(logn)

如图所示:递归调用次数平均情况下就是一个二叉树的高度logn

在这里插入图片描述

数组扫描O(n)

在这里插入图片描述
所以时间复杂度是O(nlogn),空间复杂度是O(logn)

四、注意事项

归并排序无论数据长啥样子,都是无脑的一分为二,保证递归次数一定是logn级别,非常稳定的nlogn的算法。

快速排序的性能严格受制于初始数据的情况而定。

近乎有序的数组上,快速排序的性能退化非常的快。

关于分区点的选择问题:

极端情况下,数组就是一个完全有序的数组

此时当数组近乎有序时,按照最左侧元素进行分区的时候,造成左右两颗递归树严重不平衡,甚至极端情况下退化为链表

空间:O(lognlognlogn) -> O(nnn)

时间: nlognnlognnlogn => n2n^2n2

在这里插入图片描述

分区值的选择不能武断的就选择最左侧或者最右侧

a. 三数取中 =》 最左侧,最右侧,中间值 =》 选择其中之一

b. 每次递归时选择数组中任意一个元素作为分区点

优化:

关于分区点的选择。使用随机数随机取一个数组索引的元素作为分区点,基本上不可能出现单支树的情况,避免近乎有序数组上快排退化问题。

在这里插入图片描述

总结

以上就是快速排序的图解和代码,有什么疑问可以私信博主~有帮助的话可以关注博主后续更新。

相关内容

热门资讯

年会精彩致辞 年会精彩致辞(通用7篇)  在学习、工作乃至生活中,大家对致辞都不陌生吧,致辞具有很强的实用性和针对...
少儿活动主持人主持词 少儿活动主持人主持词  主持词需要富有情感,充满热情,才能有效地吸引到观众。我们眼下的社会,主持人参...
晚会主持词开场白 【必备】晚会主持词开场白(通用13篇)  主持词已成为各种演出活动和集会中不可或缺的一部分。在人们越...
六一儿童节鼓励致辞 六一儿童节鼓励致辞(通用20篇)  无论是身处学校还是步入社会,说到致辞,大家肯定都不陌生吧,致辞具...
幼儿园元旦联欢会主持词 2014年幼儿园元旦联欢会主持词2014年幼儿园元旦联欢会主持词1师:尊敬的各位老师幼:亲爱的小朋友...
同学会联欢会主持词 同学会联欢会主持词  借鉴诗词和散文诗是主持词的一种写作手法。在一步步向前发展的社会中,越来越多的活...
搞笑脱口秀台词脱口秀台词 搞笑脱口秀台词脱口秀台词1100字校园脱口秀台词每天,当我的双脚迈入合肥七中的大门,强相互作用会把我...
学生会换届大会主持词 学生会换届大会主持词  主持词的内容  主持词一般由开场白、中间部分与结束语组成。  开场白 演出或...
教研活动公开课主持稿   教研活动公开课主持稿  篇一:数学教研活动主持词  各位领导、各位老师,大家好!  在这样一个春...
《花木兰》感人台词 《花木兰》感人台词  壹 孝,替父从军父女情  感人段落:军令如山,花弧爱国心切,无奈年老气衰,百病...
红歌赛主持词 红歌赛主持词  由主持人于节目进行过程中串联节目的串联词。如今的各种演出活动和集会中,主持人往往成了...
联欢晚会主持词 联欢晚会主持词3篇  主持词可以采用和历史文化有关的表述方法去写作以提升活动的文化内涵。在如今这个时...
金榜题名主持词 金榜题名主持词(精选23篇)  主持词要根据活动对象的不同去设置不同的主持词。随着社会一步步向前发展...
光荣退休领导致辞 光荣退休领导致辞范文(通用5篇)  在学习、工作或生活中,要用到致辞的情况还是蛮多的,致辞是指在仪式...
大学迎新晚会主持词 大学迎新晚会主持词  迎新,全称迎接新春,又叫迎接新年。迎新是中国的传统节日形式。或者欢迎、迎接新来...
教师节校长简短致辞 教师节校长简短致辞(通用10篇)  在日常学习、工作抑或是生活中,大家或多或少都用到过致辞吧,在各种...
张国荣经典台词 关于张国荣经典台词  1、哭,我为了感动谁,笑,又为了碰着谁。  ——《路过蜻蜓》  2、虽然我很喜...
新郎婚礼简短致辞 新郎婚礼简短致辞(精选10篇)  在平平淡淡的学习、工作、生活中,大家都经常接触到致辞吧,致辞是指在...
美剧经典台词截图 美剧经典台词截图  在社会发展不断提速的今天,用到台词的地方越来越多,台词是一种特殊的文学语言,必须...
女朋友撒娇的经典台词 女朋友撒娇的经典台词  1、这种被朋友的情况让我很失落,因为我喜欢他。  2、“她就是躲着我我该怎么...