快速排序

  • hoare版本
  • 挖坑法
  • 前后指针版本
  • 非递归实现快速排序

快速排序:快速排序算法通过多次比较和交换来实现排序
基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
适用情况:数据量大,要求的效率也高,应用广
时间复杂度和空间复杂度和稳定性:O(nlogn)~O(n2) ,O(logn)~O(n) 不稳定 (这里提到空间复杂度是因为其他排序都是O(1))

快排有三种实现方法,且都比较重要。
将区间按照基准值划分为左右两半部分的常见方式有:
1. hoare版本
2. 挖坑法
3. 前后指针版本

hoare版本

1、定义两个指针begin(指向首元素)和end(指向尾元素),找一个基准值key(一般三数取中法),置于序列的第一个元素,也就是begin的位置。
2、end从后往前走找比基准值小的元素(找到后也停下),begin从前往后走找比基准值key大的元素(找到后停下),
然后,交换arr[begin]和arr[end],依次循环操作。
3、当begin与end相遇,将array[begin]或array[end]与基准值交换。

详解:(建议和代码一起看)

代码

//获取基准值:三数取中法:起始, 中间, 结束
int GetMid(int* arr, int begin, int end)
{int mid = begin + (end - begin) / 2;if (arr[begin] > arr[mid]){if (arr[mid] > arr[end])return mid;else if (arr[begin] > arr[end])return end;elsereturn begin;}else{if (arr[mid] < arr[end])return mid;else if (arr[begin] < arr[end])return end;elsereturn begin;}
}//返回基准值位置
int Partion(int* arr, int begin, int end)
{//获取基准值的位置int mid = GetMid(arr, begin, end);//把基准值放到起始位置Swap(arr, begin, mid);int key = arr[begin];int start = begin;while (begin < end){//从后向前先找小于基准值的位置while (begin < end && arr[end] >= key){--end;}//从前向后再找大于基准值的位置while (begin < end && arr[begin] <= key){++begin;}Swap(arr, begin, end);}//交换相遇位置的数据和基准值Swap(arr, start, begin);return begin;
}//数据有序时,没有优化可能会导致栈溢出(代码优化,重新找基准值)
void QuickSort(int* arr, int begin, int end)
{if (begin >= end)return;//div:一次划分之后,基准值的位置int div = Partion(arr, begin, end);//左右两部分进行快速排序QuickSort(arr, begin, div - 1);QuickSort(arr, div + 1, end);
}

运行结果

挖坑法

挖坑法和hoare版本很像
1、定义begin和end分别指向数据的第一个元素和最后一个元素,找一个基准值key(一般三数取中法),置array[begin]的位置上的值为基准值,并为第一个坑。
2、end从后往前走,找比key小的值,找到之后,将array[end]赋值给array[begin],填充begin位置的坑,此时end位置为一个新的坑
3、begin从前往后走,找比key大的值,找到之后,将array[begin]赋值给array[end],填充end位置的坑,此时begin位置为一个坑
4、此类方法依次填坑,当begin和end相遇,则循环结束,将key的值填最后一个坑。

详解:(建议和代码一起看)


代码

//获取基准值:三数取中法:起始, 中间, 结束
int GetMid(int* arr, int begin, int end)
{int mid = begin + (end - begin) / 2;if (arr[begin] > arr[mid]){if (arr[mid] > arr[end])return mid;else if (arr[begin] > arr[end])return end;elsereturn begin;}else{if (arr[mid] < arr[end])return mid;else if (arr[begin] < arr[end])return end;elsereturn begin;}
}
//挖坑法
int Partion2(int* arr, int begin, int end)
{//获取基准值的位置int mid = GetMid(arr, begin, end);//把基准值放到起始位置Swap(arr, begin, mid);int key = arr[begin];while (begin < end){//从后向前先找小于基准值的位置while (begin < end && arr[end] >= key){--end;}//填坑arr[begin] = arr[end];//从前向后再找大于基准值的位置while (begin < end && arr[begin] <= key){++begin;}//填坑arr[end] = arr[begin];}//相遇位置存放基准值arr[begin] = key;return begin;
}
void QuickSort(int* arr, int begin, int end)
{if (begin >= end)return;//div:一次划分之后,基准值的位置int div = Partion2(arr, begin, end);//左右两部分进行快速排序QuickSort(arr, begin, div - 1);QuickSort(arr, div + 1, end);
}

运行结果:

前后指针版本

1、选择一个基准值key,定义两个指针prev和cur(prev指向pPcur的前一个位置)
2、当cur标记的元素比key小时,prev和cur同时走,当cur标记的元素比key大时,只有cur继续向前走(此时prev停下来),当cur走到标记的元素比key值小时,cur停下,prev向前走一步,此时交换arr[cur]和arr[prev],然后,cur继续往前走。
3、当cur走出界了,将prev位置的值与key交换。

动画:

动画看不理解可以看图

详解:(建议和代码一起看)

代码:

//获取基准值:三数取中法:起始, 中间, 结束
int GetMid(int* arr, int begin, int end)
{int mid = begin + (end - begin) / 2;if (arr[begin] > arr[mid]){if (arr[mid] > arr[end])return mid;else if (arr[begin] > arr[end])return end;elsereturn begin;}else{if (arr[mid] < arr[end])return mid;else if (arr[begin] < arr[end])return end;elsereturn begin;}
}
//前后指针法
int Partion3(int* arr, int begin, int end)
{//获取基准值的位置int mid = GetMid(arr, begin, end);//把基准值放到起始位置Swap(arr, begin, mid);int key = arr[begin];int prev = begin;int cur = begin + 1;while (cur <= end){if (arr[cur] < key && ++prev != cur){//不连续 交换数据Swap(arr, prev, cur);}++cur;}Swap(arr, begin, prev);return prev;
}void QuickSort(int* arr, int begin, int end)
{if (begin >= end)return;//div:一次划分之后,基准值的位置int div = Partion3(arr, begin, end);//左右两部分进行快速排序QuickSort(arr, begin, div - 1);QuickSort(arr, div + 1, end);
}

运行结果:

非递归实现快速排序

在数据结构中,即使递归能解决的,我们也有必要掌握非递归的方法。我们知道,堆空间比栈的空间大得多,当我们在栈中递归调函数,会增加栈的开销,栈帧会多。导致栈溢出程序崩溃。而在堆上递归调用函数,即使大的递归调用也不会使程序崩溃。这在安全方面就解决了栈溢出的问题。

递归就是自上而下,再自下而上。也就满足了我们数据结构中的栈。所以这里我们可以借助栈来实现非递归的快速排序。

这里直接放代码,配合代码+注释+图,能更好理解。

//非递归快排 ,避免栈溢出风险
void QuickSortNor(int* arr, int begin, int end)
{//定义栈Stack st;//初始化栈StackInit(&st);//先将末尾的索引入栈,StackPush(&st, end);//再将开头索引入栈StackPush(&st, 0);//只要栈不为空,就继续执行while (!StackEmpty(&st)){//获得栈顶元素int left = StackTop(&st);//出栈StackPop(&st);int right = StackTop(&st);StackPop(&st);//获取分开后区间的中间值int div = Partion(arr, left, right);//上一轮key值位置的左边[left, div - 1]if (left < div - 1){StackPush(&st, div - 1);StackPush(&st, left);}//上一轮key值位置的右边[div + 1, right]if (div + 1 < right){StackPush(&st, right);StackPush(&st, div + 1);}}
}

运行结果:

我们可以看出,无论是递归还是非递归,我们都需要借助O(nlogn)的大小空间。(递归,栈的开销)(非递归,数据结构栈需要开空间)

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

  1. 快速排序常见3种方法(hoare、挖坑法、前后指针法)以及改进。

    快速排序 快速排序的思路: 通过一趟快速排序:找到基准值正确的索引位置,将序列划分为2部分,左侧序列小于基准值,右侧序列大于基准值.然后再对左右两侧的序列分别进行递归处理,最终左右两侧的序列均为有序序 ...

  2. 数据结构与算法 | 快速排序:Hoare法, 挖坑法,双指针法,非递归, 优化

    前两章讲解了排序中的选择排序和插入排序,这次就来讲一讲交换排序中的快速排序. 快速排序时间复杂度:平均 O(nlogn) 最坏 O(n2) 快速排序,顾名思义,它的排序的效率是非常高的,在数据十分杂乱 ...

  3. 【算法】基于hoare快速排序的三种思想和非递归,基准值选取优化【快速排序的深度剖析-超级详细的注释和解释】你真的完全学会快速排序了吗?

    文章目录 前言 什么是快速排序 快速排序的递归实现 快速排序的非递归实现 单趟排序详解 hoare思想 挖坑法 前后指针法 快速排序的优化 三数取中 小区间优化 快速排序整体代码 尾声 前言 先赞后看 ...

  4. 二叉树----数据结构:二叉树的三种遍历及习题

    二叉树----数据结构:二叉树的三种遍历,利用递归算法. 关于二叉树的遍历,应用非常广泛,不单单是访问打印结点,还可以进行一系列的操作,如赋值.删除.查找.求二叉树的深度等等. 有递归和非递归两种算法 ...

  5. 快速排序的三种实现以及应用场景

    好了,废话不多说,直接上干货. 1.快速排序的概念: 快速排序(Quicksort)是对冒泡排序的一种改进. 快速排序由C. A. R. Horno在1962年提出.它的基本思想是:通过一趟排序将要排 ...

  6. 【c语言】快速排序的三种实现以及优化细节

    目录 前言 一.hoare版本 1.思想 2.代码 二.挖坑版本 1.思想 2.代码 三.前后指针版本 1.思想 2.代码 四.分治思想: 五.两种优化 1.三数取中 2.小区间优化 六.非递归实现快 ...

  7. 快速排序(三种方法实现)

    文章目录 1. 快速排序 <1>hoare法(左右指针法) <2>挖坑法 <3>前后指针法 <4>快速排序优化 <5>非递归实现 2.特性 ...

  8. 快速排序的三种实现方法

    文章目录 1.快速排序的思想 2.基本实现 2.1第一种排序 2.2第二种排序 2.3第三种排序(挖坑法) 2.4代码测试 3.代码优化 4.快速排序小结 1.快速排序的思想 快速排序的过程可以分为以 ...

  9. 快速排序的三种分区方法(整理)

    快排的三种分区Partition方法 之前在课程上对于快速排序的认知还不够深入,虽然知道大概是每一次都把小于主元以及大于主元的元素分别排在主元的两侧,之后不断的递归缩小范围达到整体有序,但是甚至到正式 ...

最新文章

  1. 摄像头模组(CCM)与镀膜
  2. 关于Session接口的update方法主要有如下几点要注意
  3. 当一百万名记者都嚷嚷着“Facebook 很糟糕”......
  4. python适用范围_Python应用范围总结概览
  5. cuSPARSE库:(九)cusparseSetStream()
  6. 【2012百度之星资格赛】J:百度的新大厦
  7. Delphi XE5教程1:语言概述
  8. php版本的code review软件
  9. vscode下载太慢,修改下载地址提速
  10. 超级简单的html转换为pdf格式方法
  11. 公众号关键字自动回复内容点击跳转小程序方法
  12. mysql fatch array_辩别WEB服务程序,,了解常见的几种脚本和数据库之间的搭配组合及特点...
  13. 实战案例:抽屉自动点赞与爬取汽车之家新闻
  14. Python之手机ua
  15. 网易互联网雷火互娱22届校招及社招内推
  16. 外卖CPS用抖音高权重号评论引流,日引万粉
  17. 红色荧光染料AF 594活性酯,Alexa Fluor 594 NHS ester,CAS:295348-87-7
  18. 利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器
  19. Python OpenCV基于颜色通道分离法去除图片中的红色印章
  20. unity LeapMotion 手势旋转,位移,缩放

热门文章

  1. notepad 没有plugin manager_和平精英巅峰赛正式开启,热度却没有象中那么高!
  2. java spliterator,Java 8 之Stream Spliterator
  3. 2020山东省计算机专科学校排名,2021山东专科学校排名 最好的高职院校排行榜
  4. linux shell ${}简单用法,Linux shell ${}简单用法
  5. 初一数学下册电子课本_七年级数学下册人教版-数学七年级下册电子课本-精品下载...
  6. matlab自带同步电机数学模型,永磁同步电机的数学模型及方程
  7. Cron 触发器及相关内容 (第一部分)
  8. php版本控制在线推送升级,ThinkPHP5:app版本控制,版本升级接口开发
  9. 单片机学校实训老师上课需要的工具以及源码分享
  10. 基于JAVA+SpringMVC+MYSQL的健身房管理系统