从两种框架,三种思想来展开对快速排序的叙述

两种框架:递归与非递归

三种思想:左右交换法、挖坑法、前后指针法

但三种思想整体来说就是要找一个key值,把比key小的放在key左边,把比key大的放在key右边,然后再从[0,key-1][key+1,end]这两个区间排序,一直递归或者迭代,直到要排序的节点只有一个的时候,即为有序。

目录

一.递归

0.递归框架

1.左右交换法

2.挖坑法

3.前后指针法

二.非递归

0.非递归框架

三.时间复杂度/优化(避免最坏情况)/小区间优化/稳定性

1.时间复杂度

2.优化(避免最坏情况)

3.小区间优化

4.稳定性


一.递归

0.递归框架

递归框架可以类比成二叉树中的先序,先整体排一遍之后,递归左,再递归右... ...

//递归
void QuickSort(int* arr, int begin, int end)
{//只有一个节点if (begin >= end){return;}int left = begin, right = end;//三种方法int key = //PartSort1(arr, left, right);//左右交换法//PartSort2(arr, left, right);//挖坑法PartSort3(arr, left, right);//前后指针法//[begin,key-1]QuickSort(arr, begin, key - 1);//[key+1,end]QuickSort(arr, key + 1, end);
}

1.左右交换法

思想:默认选最左侧的一个数为key,先从右向左找比key小的,再从左向右找比key大的,交换两数。循环至左右指向同一个数时停止,这时将最左侧的key值与左右指针指向的数交换。

注意:这个思想有三处容易错的地方

1.为了保证最后一次交换一定是要比key小的值,比key值小才可以交换到key值左边,所以要先从右找比key小的。

2.不管是从右找小还是从左找大一定要在循环中加上left<right条件,否则可能发生越界

3.在从右找小或者从左找大的过程中,等于key的情况不能交换,否则可能发生死循环

int PartSort1(int* arr, int left, int right)
{int keyi = left;while (left < right){//从右找小while (left < right && arr[right] >= arr[keyi]){right--;}//从左找大while (left < right && arr[left] <= arr[keyi]){left++;}//交换Swap(&arr[right], &arr[left]);}//循环结束后,交换最后一次,交换最左侧的key值与左右指针同时指向的值Swap(&arr[left], &arr[keyi]);keyi = left;return keyi;
}

2.挖坑法

思想:把最左边的值看作坑,把值用pit保存起来。从右开始找比pit小的数,找到后把此位置的值放到坑中,把此位置看作是新的坑;从左开始找比pit大的数,找到后把此位置的值放到坑中,把此位置看做是新的坑。循环结束后,左右指针指向同一个位置,此位置必然是坑,把一开始最左边的数pit放到坑中。

int PartSort2(int* arr, int left, int right)
{int pit = arr[left], piti = left;while (left < right){//从右找小while (left < right && arr[right] >= pit){right--;}//放到坑中arr[piti] = arr[right];//把自己变成新的坑piti = right;//从左找大while (left < right && arr[left] <= pit){left++;}//放到坑中arr[piti] = arr[left];//把自己变成新的坑piti = left;}arr[piti] = pit;return piti;
}

3.前后指针法

思想:定义两个指针,prev指向最左边,cur = prev+1;从cur位置出发,开始寻找比最左边的key值小的数,找到了就先++prev,如果prev!=cur再交换prev与cur位置的值,不管找没找到,都++cur,将数组每个数挨个遍历,最后再交换最左边的key值与prev。

int PartSort3(int* arr, int left, int end)
{int prev = left, cur = prev + 1;int keyi = left;while (cur <= end){if (arr[cur] < arr[keyi] && ++prev != cur){Swap(&arr[prev], &arr[cur]);}cur++;}Swap(&arr[keyi], &arr[prev]);return prev;
}

二.非递归

0.非递归框架

使用栈数据结构,来模拟二叉树的先序递归形式。

思想:(以栈数据结构举例,如果这里使用队列也可以,但是队列更像二叉树的层序遍历)

将要排序的空间首位成对入栈,需要排序时再成对出栈,每排完一次,如果符合条件,就将区间首位再次压入栈中,直到迭代至栈为空。

void QuickSort(int* arr, int begin, int end)
{Stack sk;StackInit(&sk);StackPush(&sk, end);StackPush(&sk, begin);while (!StackEmpty(&sk)){int left = StackTop(&sk);StackPop(&sk);int right = StackTop(&sk);StackPop(&sk);//三种方法int key = //PartSort1(arr, left, right);//左右交换法//PartSort2(arr, left, right);//挖坑法PartSort3(arr, left, right);//前后指针法if (key < right){StackPush(&sk, right);StackPush(&sk, key + 1);}if (left < key){StackPush(&sk, key - 1);StackPush(&sk, left);}}
}

三.时间复杂度/优化(避免最坏情况)/小区间优化/稳定性

1.时间复杂度

这是一张快速排序的逻辑图

时间复杂度:O(N*logN)

空间复杂度:O(logN)

讨论最坏情况:从此图可以看出每次排序好之后,接近二分,但如果是完全有序或者完全逆序

左右的情况就不再是n/2,拿完全有序来说,就是一个左1且右n-1的情况。这样排序下去就会有n层。

最坏情况:一组数据完全有序或者完全逆序,快排的时间复杂度是O(N^2)

2.优化(避免最坏情况)

为了尽可能的避免这种最坏情况的发生,在选key的时候,就不能默认从最左开始选了。

方法:可以采用三数取中,就是从最左,最右,中间,三个数中选出一个不大也不小的数当做key

把这个不大也不小的数的下标记录下来,与最左侧的数交换一下值。这样上面三种方法的代码不需

要很大的改动,仍然选取最左侧为key值,只不过我们把最左侧的这个数换为了一个最合理的数。

    int midi = GetMidIndex(arr, left, right);Swap(&arr[midi], &arr[left]);
int GetMidIndex(int* arr, int begin, int end)
{int mid = begin + (end - begin) / 2;if (arr[begin] > arr[end]){if (arr[mid] > arr[begin])return begin;else if (arr[end] > arr[mid])return end;elsereturn mid;}else //arr[begin] < arr[end]{if (arr[mid] > arr[end])return end;else if (arr[begin] > arr[mid])return begin;elsereturn mid;}
}

3.小区间优化

仍然使用这张图来解释一下,小区间优化问题。

我们解决了快排的最坏情况之后,快排时间复杂度为O(N*logN)

但是仔细思考一下,可不可以再进一步优化呢?让效率更优一点

递归的最大缺点就是有一个很强的限制性因素,当数据量太大时,递归很容易栈溢出,从而导致程序崩溃!

既然快排所使用的递归是一个类似于二叉树的前序遍历,那么我们从二叉树的角度出发来看一下!

例如:一个n层的二叉树,总结点个数是2^n-1,最后一层的节点个数是2^(n-1)

经过数学计算,2^n-1看作2^n,2^n = 2^(n-1) * 2,最后一层的节点个数就占了总结点个数的一半

当递归划分小区间,区间比较小的时候,就不再递归划分去排序这个小区间。可以考虑直接用其他排序对小区间的处理,比如使用插入排序,假设小区间小于10时,不在递归排序小区间。将会减少至少50%以上的递归次数

总体来说,小区间优化主要是来解决大量数据时递归的栈溢出问题,以及多次开辟栈帧的资源消耗问题。

4.稳定性

先说下结论:快排不稳定。

如果有一组数据中相同的元素在排序之后,更改他们的相对顺序,就认为这个排序不稳定。

在日常生活中,某些特定场合,使用的排序的稳定性及其重要。比如说两位同学分数相同,但是一个先交卷,一个后交卷,按照常理来说,先交卷的同学应该排在后交卷的同学之前,但如果经过排序后,先交卷的同学排到了后交卷同学之后,这就非常不合理了。

原因:

1.比如说有这样一组数据:4,5(1),5(2),5(3),8。

在三数取中时中间的5就会和4交换位置:5(2),5(1),4,5(3),8

2.或者这样一组数据:4,7(1),7(2),2,3,5

在第一次排序结束之后(左右交换法):2,3,4,7(2),7(1)

【数据结构】快速排序相关推荐

  1. 数据结构----快速排序

    数据结构----快速排序 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> int quickSort(int a[], ...

  2. 用数据结构c语言写成绩排序,C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 # ...

  3. 数据结构——快速排序

    系列文章:数据结构与算法系列--从菜鸟到入门 描述 快速排序是基于分治模式的,下面按分治模式来进行分析: 分解: 数组 A[p..r]被划分成两个(可能空)子数组,A[p..q-1]和 A[q+1.. ...

  4. [数据结构]快速排序

    一.问题描述 内部排序是一件具有重大意义的问题,许多项目的实现中都需要用到排序. 我们知道,排序的算法有许多种,每种排序算法的时间复杂度和空间复杂度不尽相同.在解决实际问题时,往往需要根据实际需要选择 ...

  5. 数据结构 快速排序的三种实现 (hoare版本 挖坑法 前后指针版本)与非递归实现

    快速排序 hoare版本 挖坑法 前后指针版本 非递归实现快速排序 快速排序:快速排序算法通过多次比较和交换来实现排序 基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另 ...

  6. 数据结构--快速排序

    快速排序是效率比较高的一种排序算法,其思想主要是递归 package mainimport "fmt"func QuickSort(left int, right int, arr ...

  7. python算法与数据结构-快速排序算法

    设定一个中间值,如下所示: low从开始位置找low是找比54小的,26比54小合格  high是从末尾位置找比54大的,如下所示: low和high重合后第一轮循环结束 第一轮递归后的结果如下所示: ...

  8. python算法与数据结构-快速排序算法(36)

    阅读目录 一.快速排序的介绍 二.快速排序的原理 三.快速排序的步骤 四.快速排序的图解 五.快速排序的python代码实现 六.快速排序的C言语代码实现 七.快速排序的时间复杂度 八.快速排序的稳定 ...

  9. 数据结构 快速排序(详解)

    快速排序 1:快速排序的思想 快速排序运用了分治的思想,即通过一趟排序 将序列分为两部分,根据选取的基准, 将比基准小的数放在基准前面,将比基准大的数放在的数放在基准后面:然后对两部分进行递归处理,以 ...

  10. 重学数据结构——快速排序,二分法查找

    每次提起快排,内心中都有点隐隐作痛. 当时腾讯的那个面试官让我写快排的前两遍排序结果,结果,我当时居然没写上来-- 这个,就是所谓的关键时刻掉链子吧,这么经典的快排都不会,真是丢死人了-- 今天在实验 ...

最新文章

  1. 综合评价模型的缺点_浅谈交通影响评价中不同交通预测方法的特性
  2. 成功解决Exception “unhandled ModuleNotFoundError“No module named ‘face_recognition.cli‘
  3. 判断是否是ie浏览器 前端js_JS判断是否是IE浏览器
  4. Linux 中识别 USB 设备名字的 4 种方法
  5. debian文本配置网络备忘:/etc/network/interfaces
  6. 笨办法学C 练习28:Makefile 进阶
  7. mysql ssd优化_mysql ssd 优化
  8. [阅读笔记]专访刘毅:阿里巴巴云计算平台运维故障分析与排查
  9. ab153x-ut apk 洛达1562a蓝牙检测工具 真)
  10. SQL字符串首字母大写
  11. 怎样让自己变得更优秀?职场精英是如何炼成的?怎样成为行业精英
  12. 多目标优化算法:多目标黄金搜索优化算法MOGSO(提供MATLAB代码)
  13. 达芬奇调色DaVinci Resolve Studio18v18.1.4 2023中文版更新发布,支持intel/M1/M2芯片
  14. Composer开启多线程组件改变composer缓慢问题
  15. 银行卡编码规则及检验算法详解
  16. 詹姆斯·西蒙斯 - 金融业的数学大师
  17. MZOJ #82 总统竞选
  18. PDF怎么编辑文字?教你两招非常实用的方法
  19. 云原生架构总览,发展定义架构及趋势
  20. 基于PHP+MySQL的仓库管理系统

热门文章

  1. Delphi XE4,C++ Builder XE4,RAD Studio XE4 v18.0.4905.60485(With Update 1) 官方下载激活
  2. Android多国语言文件夹命名
  3. Orical配置的过程步骤
  4. 计算机音乐一决高下,节奏大师详细讲解 用音乐来一决高下吧!
  5. javaweb房屋中介系统设计要求
  6. sshfs(基于ssh挂载远程目录)
  7. atmega8 例程:12864例程
  8. 戴尔官网BIOS转.bin文件教程
  9. 基于单片机的热电偶测温仿真设计(#0004)
  10. 前端架构-分层而治,铁打的MV流水的C