广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >快速排序详解(递归实现与非递归实现)
  • 724
分享到

快速排序详解(递归实现与非递归实现)

排序算法算法数据结构c++ 2023-10-24 20:10:38 724人浏览 薄情痞子
摘要

目录 一、快速排序的基本思想 二、将序列划分成左右区间的常见方法 2.1hoare版本(动图+解释+代码实现) 2.2挖坑法 2.3前后指针法 三、快速排序的初步实现 四、快速排序的优化实现 4.1快排的特殊情况 4.2对区间划分代码的

目录

一、快速排序的基本思想

二、将序列划分成左右区间的常见方法

2.1hoare版本(动图+解释+代码实现)

2.2挖坑法

2.3前后指针法

三、快速排序的初步实现

四、快速排序的优化实现

4.1快排的特殊情况

4.2对区间划分代码的优化

4.3小区间优化

五、快速排序的非递归实现

六、总结


一、快速排序的基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

// 假设按照升序对array数组中[left, right)区间中的元素进行排序void QuickSort(int array[], int left, int right){ if(right - left <= 1) return;  // 按照基准值对array数组的 [left, right)区间中的元素进行划分 int div = partion(array, left, right);  // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right) // 递归排[left, div) QuickSort(array, left, div);  // 递归排[div+1, right) QuickSort(array, div+1, right);}
上述为快速排序递归实现的主框架,会发现与二叉树前序遍历规则非常像,先取中间,递归左区间,再递归右区间。

二、将序列划分成左右区间的常见方法

从上面的主框架就可以看出,我们需要一个partion函数,将序列分为左区间和右区间。下面我将介绍三种划分左右区间的方法。我通常是选取最左边的数作为基准值,将比它小的数都换到它的左边,比它大的数都换到它的右边。

2.1hoare版本(动图+解释+代码实现)

hoare版本的思想就是选取最左边的数作为基准值,一个左标志一个右标志,左标志指向序列的第一个元素,右标志指向序列的最后一个元素。让右标志先开始往左走,找比基准值小的数,找到了就停下来;再让左标志开始往右走,找比基准值大的数,找到了就停下来,然后交换左右标志所指向的值然后重复红色字体的过程,直到左标志和右标志相遇,最后交换基准值和相遇标志所指向的值。基准值就出现在了它最后应该出现的地方,它的左区间都是小于等于它的值,右区间都是大于等于它的值。这时可能有人就会问了,相遇标志换到最前面了,一定能保证相遇标志对应的值比基准值小吗?答案是一定的。因为如果是左标志停下来了右标志走来跟左标志相遇,左标志对应的值一定是比基准值要小的(交换完以后左标志停下来右标志继续走,此时左标志对应的值一定是比基准值要小的);如果是右标志停下来左标志走来跟右标志相遇,那右标志一定是找到比基准值小的数才会停下来,同样可以保证相遇标志对应的值比基准值要小。

int PartSort1(int* a, int left, int right){int keyi = left;//取最左边的数为基准值while (left < right){//找小while (left < right && a[right] >= a[keyi]){right--;}//找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);return left;}

2.2挖坑法

挖坑法虽然与hoare的方法实现上有差异,但思想上其实差别不大。

坑位就是最终key要去的位置。

int PartSort2(int* a, int left, int right){int key = a[left];int hole = left;while (left < right){//找小while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;//找大while (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}//左右标志相遇在坑位上,相遇时坑位的左边都小于等于key,右边都大于等于key,//坑位就是key应该去的位置。a[hole] = key;return hole;}

2.3前后指针法

prev最终在的位置的值一定会比key的值要小,prev最终在的位置也就是key最终要去的位置。交换到最后prev位置以及它前面的值都是比key要小的,后面的值都是大于等于key的。

int PartSort3(int* a, int left, int right){int prev = left;int cur = prev + 1;int keyi = left;while (cur <= right){if ( a[keyi] > a[cur]  && ++prev != cur)//a[keyi] > a[cur]找小,但如果a[keyi] > a[cur]一直成立,//++prev 会一直== cur不会拉开差距。只有a[keyi] <= a[cur],++prev 才会和cur拉开差距。Swap(&a[prev], &a[cur]);cur++;}Swap(&a[keyi], &a[prev]);return prev;}

三、快速排序的初步实现

void QuickSort(int* a, int left, int right){if (left >= right)//区间只有一个值或区间不存在时就返回return;int keyi = PartSort1(a, left, right);    //这里用PartSort1/PartSort2/PartSort3都可以,时间效率都是一样的QuickSort(a, left, keyi-1);QuickSort(a, keyi+1, right);//不断递归左区间和右区间}

四、快速排序的优化实现

4.1快排的特殊情况

上面的写法面对绝大多数情况的排序已经可以实现时间复杂度接近O(n*logn),但面对某些特殊的情况,比如说你要将一个序列排成一个升序序列,然而这个序列本身就是一个升序序列,那就会造成keyi的左边没有数而其他数都在它的右边的情况,这样就会造成时间复杂度变成O(n^{2}),影响了快排的效率。看下图解释,红色矩形代表keyi的位置,如果是一个完全无序的序列进行排序,keyi所处的为值一般不会特别靠左也不会特别靠右,能保证排序的时间复杂度接近O(n*logn)

但如果是下面这种情况keyi一直位于最左侧,就会使时间复杂度为 O(n^{2})(每一次遍历时间复杂度为O(n),遍历n次)。

 

所以为了防止这种特殊情况的出现,可以对划分区间的代码进行一点优化。

4.2对区间划分代码的优化

我们还是可以取最左边的数作为key,只是要将最左边的数换成一个在序列中不是那么大也不是那么小的数,在此我们引进了一段三数取中代码。

int getMid(int* a, int left,int right){int mid = (left + right) / 2;if (a[left] > a[right]){if (a[mid] > a[left]){return left;}else if (a[mid] > a[right]){return mid;}else{return right;}}else{if (a[mid] > a[right])return right;else if (a[mid] > a[left])return mid;elsereturn left;}}

这段代码负责找到序列中最左边,最右边,中间三个数中第二大的那个数。区间划分代码加入三数取中后,就可以规避掉刚才所说的那种特殊情况了。

int PartSort1(int* a, int left, int right){int mid = getMid(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;//取最左边的数为基准值while (left < right){//找小while (left < right && a[right] >= a[keyi]){right--;}//找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);return left;}

这里以hoare版本为例,其它两种区间划分方法也可以加上三数取中

4.3小区间优化

因为在递归到后期时,有的小序列已经接近有序,使用直接插入排序效率就会很高。(直接插入排序在希尔排序那篇博客已有实现,在这里就不再赘述)

void QuickSort(int* a, int left, int right){if (left >= right)return;//小区间优化if ((right - left + 1) > 10)//当区间长度大于10时{int keyi = PartSort1(a, left, right);QuickSort(a, left, keyi-1);QuickSort(a, keyi+1, right);}else//区间长度小于10时{InsertSort(a + left, right - left + 1);}}

五、快速排序的非递归实现

快排使用到了递归的思想和方法,但是递归如果递归太深的话就会有爆栈的风险,所以在这里也介绍一下快速排序的非递归实现方法。因为快排是先处理左边的序列再处理右边的序列,这里就需要用到数据结构中的栈了,先入右再入左,进行排序。(栈的结构在之前的博客中已经实现过了,在这里同样不赘述)

void QuickSortNonR(int* a, int left, int right){Stack st;StackInit(&st);StackPush(&st, right);StackPush(&st, left);while (StackEmpty(&st)){int begin = StackTop(&st);StackPop(&st);int end = StackTop(&st);StackPop(&st);int keyi = PartSort1(a, begin, end);        //不断把大区间化成小区间处理if (keyi + 1 < end){StackPush(&st, end);StackPush(&st, keyi+1);}if (begin < keyi - 1){StackPush(&st, keyi-1);StackPush(&st, begin);}}StackDestroy(&st);}

六、总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序 2. 时间复杂度:O(N*logN) 3. 空间复杂度:O(logN) 4. 稳定性:不稳定

来源地址:https://blog.csdn.net/m0_74265792/article/details/133612517

--结束END--

本文标题: 快速排序详解(递归实现与非递归实现)

本文链接: https://www.lsjlt.com/news/441786.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 快速排序详解(递归实现与非递归实现)
    目录 一、快速排序的基本思想 二、将序列划分成左右区间的常见方法 2.1hoare版本(动图+解释+代码实现) 2.2挖坑法 2.3前后指针法 三、快速排序的初步实现 四、快速排序的优化实现 4.1快排的特殊情况 4.2对区间划分代码的...
    99+
    2023-10-24
    排序算法 算法 数据结构 c++
  • c语言递归和非递归排序怎么实现
    本篇内容主要讲解“c语言递归和非递归排序怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“c语言递归和非递归排序怎么实现”吧!递归代码流程归并就是把两个或多个序列合并,这里只介绍二路归并,就...
    99+
    2023-06-30
  • PHP怎么实现快速排序的非递归算法
    介绍快速排序是一种高效的排序算法,它通过不断地将一个数组分成两个子数组来实现排序。在快速排序算法中,一个基准值(pivot)被选出并所有小于基准值的元素放在其左侧,而所有大于基准值的元素放在其右侧。然后,这个过程被递归地应用在左右两侧的子数...
    99+
    2023-05-14
  • C语言递归实现归并排序详解
    归并排序递归实现还是比较难理解的,感觉涉及递归一般理解起来都会比较有难度吧,但是看了b站视频,然后照着打下来,然后自己写了点注释,就发现不知不觉都大概懂了。 这里的归并讲的是升序排序...
    99+
    2022-11-13
  • 详解C语言通过递归与非递归实现蛇形矩阵
    前言: 本次蛇形矩阵我将以两种方法来实现,即非递归和递归 非递归的实现: #define right 1 #define down 2 #define left 3 #defin...
    99+
    2022-11-13
  • C语言非递归算法解决快速排序与归并排序产生的栈溢出
    目录1、栈溢出原因和递归的基本认识2、快速排序(非递归实现)3、归并排序(非递归实现)建议还不理解快速排序和归并排序的小伙伴们可以先去看我上一篇博客​​​​​​哦!C语言超详细讲解排...
    99+
    2022-11-13
  • python非递归全排列实现方法
    刚刚开始学习python,当前看到了函数这一节。结合数组操作,写了个非递归的全排列生成。原理是插入法,也就是在一个有n个元素的已有排列中,后加入的元素,依次在前,中,后的每一个位置插入,生成n+1个新的全排...
    99+
    2022-06-04
    递归 排列 方法
  • 原:八皇后问题的递归和非递归Java实现
    原:八皇后问题的递归和非递归实现八皇后问题是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名 的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线...
    99+
    2023-06-03
  • python递归实现链表快速倒转
    本文实例为大家分享了python递归实现链表快速倒转的具体代码,供大家参考,具体内容如下 案例:实现如下链表进行倒转 源代码: ''' Node 用于表示队列中的节点;它包含两个域...
    99+
    2022-11-10
  • 【数据结构】论如何拿捏快速排序?(含非递归)
    目录 一,快速排序(递归) 1,快排思想 2,霍尔排序 3,挖坑法 4,前后指针法 5,快速排序优化 1,三数取中法选key 2,小区间优化 二,快速排序(非递归) Stack.h Stack.c 三,快速排序源代码 一,快速排序(...
    99+
    2023-10-07
    算法 数据结构 排序算法 c语言
  • C++递归实现选择排序算法
    目录基本思想举例完整代码基本思想 每次找出最小元素,通过交换实现将其放在乱序的首位,直到所有元素都已经排好序。 举例 以 A[10] = { 3,1,6,4,8,2,10,7,9,5...
    99+
    2022-11-12
  • C语言并查集的非递归实现详解
    目录【算法分析】【算法代码】并查集压缩路径非递归写法参考文章总结【算法分析】 经典的递归实现的并查集,在数据规模过大时,可能会爆栈,因此有了并查集的非递归实现。核心代码如下: i...
    99+
    2022-11-12
  • 【数据结构与算法】如何对快速排序进行细节优化以及实现非递归版本的快速排序?
    君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,国庆长假结束了,无论是工作还是学习都该回到正轨上来...
    99+
    2023-10-22
    算法 数据结构 排序算法
  • Python3实现快速排序、归并排序、堆
    # -*- coding: utf-8 -*- # @Time : 2019-03-26 16:46 # @Author : Jayce Wong # @ProjectName : leetcode # @Fi...
    99+
    2023-01-31
    快速
  • C++实现二叉树非递归遍历算法详解
    目录一、二叉树的前序遍历二、二叉树的中序遍历三、二叉树的后序遍历3.1 方法一3.2 方法二一、二叉树的前序遍历 题目链接 我们可以把任何一棵树看成左路节点,左路节点和右子树。先访问...
    99+
    2023-05-17
    C++二叉树非递归遍历 C++非递归遍历 C++二叉树遍历
  • kotlin实现快递与号码归属地查询案例详解
    目录一.快递查询开发二.号码地查询开发一.快递查询开发 此效果展示: 1.新建CourierActivity,编写界面交互代码: <xml version="1.0" enc...
    99+
    2023-02-16
    kotlin快递查询 kotlin号码归属地查询
  • java中如何实现递归排列
    递归排列递归,俗称“我 调 我 自 己”,如果从数据结构的角度来理解,其实就是栈。假如我们要求得到A、B、C的排列,流程大概如下:(0)初始状态,栈内无数据。此时栈外:A、B、C(1)将A放入栈底。此时栈外:B、C(2)将B放入栈中。此时栈...
    99+
    2020-04-05
    java 递归 排列
  • golang归并排序,快速排序,堆排序的实现
    归并排序 归并排序使用经典的分治法(Divide and conquer)策略。分治法会将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得...
    99+
    2022-11-13
  • java非递归实现之二叉树的前中后序遍历详解
    二叉树的前中后序遍历 核心思想:用栈来实现对节点的存储。一边遍历,一边将节点入栈,在需要时将节点从栈中取出来并遍历该节点的左子树或者右子树,重复上述过程,当栈为空时,遍历完成。 前序...
    99+
    2022-11-12
  • 怎么用python递归实现链表快速倒转
    这篇文章主要介绍“怎么用python递归实现链表快速倒转”,在日常操作中,相信很多人在怎么用python递归实现链表快速倒转问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用python递归实现链表快速倒转...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作