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

两种框架:递归与非递归

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

但三种思想整体来说就是要找一个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. STL_set集合容器+map映照容器
  2. Python实现有道翻译
  3. 从0开始学习 GitHub 系列之「初识 GitHub」
  4. 快速入门SSM整合配置建立第一个SSM项目模板
  5. linux安装comfast网卡驱动,电脑如何通过usb共享手机网络 Linux安装wifi 无线网络 811AC usb网卡驱动...
  6. opencv 直方图_OpenCV之图像直方图反向投影
  7. RabbitMQ远程不能访问问题的解决
  8. Request_原理
  9. 前端学习(1170):findIndex
  10. 企业主要培养三大方向的思考习惯
  11. 对WITH和from(select ...)的一点比较
  12. Aptana Studio 介绍
  13. OpenCV实现图片素描风(调用摄像头+中值滤波+拉普拉斯边缘检测)
  14. Spark2.1.0——运行环境准备
  15. PHP之Smarty模板学习
  16. 远程办公——如何在外远程控制家里/公司的电脑,利用cpolar内网穿透
  17. python爬虫视频教程
  18. 隐私泄露中的人性剖析
  19. 安氏书店管理软件——您的首选
  20. beanstalkds文档

热门文章

  1. Docker私有镜像仓库
  2. 【安全知识分享】5S管理EHS和质量管理体系阐述(附下载)
  3. Python numpy使用记录4.逻辑运算,与或非,异或
  4. 程序设计基础(CC++) 戴波、张东祥 第五章 指针 编程作业
  5. python将文档转换成pdf_python3如何将docx转换成pdf文件
  6. 【TG-02系列模组①】二次开发环境搭建,快速接入天猫精灵/亚马逊云
  7. qt android刘海屏状态栏,OPPO R15手机正式发布,也用上了刘海屏和 AI 人工智能
  8. linux--redis
  9. Java小游戏捕鱼达人
  10. geany配置python lint_Linux强大IDE——Geany配置说明