快速排序平均时间是T(n) =knln(n),其中n为待排序序列中记录个数,k为某个常数。通常,快速排序被认为是,在所有同数量级(O(nlogn))的排序方法中,其平均性能最好,但是,若初始记录按关键字有序或者基本有序,快速排序将蜕变为冒泡排序,其时间复杂度为O(n^2)。

快速排序听起来挺简单,实现代码看起来也很简单。但简单只是相对而言,如果不理解其思路的话,现场写代码还是有一定难度,因此需要理解其思想再去写代码。

快速排序答题思路是先将排序序列按照枢轴值分成两个子集,左子集中的元素都小于枢轴值,右子集的元素都大于枢轴值,这里的关键是找到枢轴值的位置。然后使用递归对子集在进行同样的分割操作,递归终点就是子集中元素个数为一。枢轴值可以从序列中随机去一个出来,根据经验,“三者取中”的法则可最大改善快速排序在最坏情况下的性能。“三者取中”即在 arr[ i ],  arr[ j ],  arr[(i + j) / 2]取中值。

举个例子,序列array,长度为N,取枢轴值pivotKey = array[n/2], pivotLoc = n/2,设置两个指针low 和 high,从low开始,找到大于pivotKey的值,假设在i位置(i <  pivotLoc && array[i] > pivotKey),交换pivotLoc和i的值,这时候pivotLoc位置就变为i,这时候从high往前搜索,找到小于pivotLoc的值,然后和pivotLoc交换。如果low < high的话重复上述过程,直到low == high,这时候low == high == pivotLoc。得到两个子集array[0 ~ pivotLoc] 和 array[ pivotLoc +1, high],递归调用即可,直到子集中元素个数为1返回。

c代码如下:

//两个数值互换
void swap(int *a, int *b)
{if (a == b)return;*a = *a ^ *b;*b = *a ^ *b;*a = *a ^ *b;
}void quickSort(int *arr, int low, int high)
{int pivotKey, pivotLoc;int left = low;int right = high;if (low < high){//保存枢轴点和枢轴值pivotLoc = (low + high) / 2;pivotKey = arr[pivotLoc];while (low < high){while (low < pivotLoc && arr[low] <= pivotKey)low++;if (low < pivotLoc){swap(arr+low, arr+pivotLoc);pivotLoc = low;}while(high > pivotLoc && arr[high] >= pivotKey)high--;if (high > pivotLoc){swap(arr+high, arr+pivotLoc);pivotLoc = high;                  }}quickSort (arr, left, pivotLoc);quickSort (arr, pivotLoc + 1, right);}return;
}

非递归版本,通常使用的是递归版本的快速排序,但是有时候也会遇到要求实现非递归版本的快速排序。之前听谁说过,递归的问题都可以使用栈来实现。快速排序也是一样。我们分析递归版本的快速排序,快速排序一遍后得到左右两个子集,这时候分别对两个左右子集递归调用快速排序。复又得到左右子集。知道子集中边界元素为1为止。这个过程,可以用栈来模拟,比如,一次排序后,左右子集的边界入栈。如果栈不为空,取出栈的子集边界,调用快速排序。复又得到左右子集边界,进栈。重复此过程,直到栈空。整个过程和树的宽度优先遍历类似。代码如下:

//快速排序,返回枢轴点
int quickSort(int *nums, int begin, int end)
{int pivot, i = begin, j = end;pivot = nums[i];while (i < j){while (j > i && nums[j] >= pivot)j--;if (j > i)swap (nums+j, nums+i);elsebreak;while (i < j && nums[i] <= pivot)i++;if (i < j)swap (nums+j, nums+i);elsebreak;     }return i;
}void main()
{int top = 0, low = 0, high, pivot, stack[100] = {0};int nums[] = {10,3,1,8,4,5,2,9,8,6,5};int size = sizeof(nums)/sizeof(int);high = size - 1;//第一次快速排序pivot = quickSort (nums, 0, size - 1);//子集边界入栈if (pivot - 1 > low){stack[top++] = pivot - 1;stack[top++] = low;}if (pivot + 1 < high){stack[top++] = high;stack[top++] = pivot + 1;}//从栈中取出边界继续进行快速排序while (top > 0){low = stack[--top];high = stack[--top];//复又得到左右边界,继续入栈pivot = quickSort (nums, low, high);if (pivot - 1 > low){stack[top++] = pivot - 1;stack[top++] = low;}if (pivot + 1 < high){stack[top++] = high;stack[top++] = pivot + 1;}}
}

参考资料:

1. 数据结构 : C语言版/ 严蔚敏,吴伟民编著

=============================================================================================

Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

快速排序 递归版本和非递归方法 c代码相关推荐

  1. 数据结构--Avl树的创建,插入的递归版本和非递归版本,删除等操作

    AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个结点的左右子树的高度之差的绝对值最多为1(空树的高度为-1). 也就是说,AVL树,本质上是带了平 ...

  2. python实现冒泡排序算法的非递归版本_冒泡排序以及python代码实现(递归+非递归)...

    一.冒泡排序 比较简单的排序算法,适合小规模数据集,效率较低. 依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面. 每进行一趟排序,就会少比较一个数 python代码(非递归): fro ...

  3. 二叉树的中序遍历(递归和非递归版本)

    难易程度:★★ 重要性:★★★★★ 树结构是面试中的考察的重点,而树的遍历又是树结构的基础.中序遍历的非递归版本要求重点理解掌握. /*** 非递归版本的中序遍历* node指向待处理的节点,在中序遍 ...

  4. 给出TREE_INSERT过程的非递归版本(算法导论第三版12.3-1)

    给出TREE_INSERT过程的非递归版本(算法导论第三版12.3-1) template<typename T> void insert_recursive(BinaryTree< ...

  5. 二叉树的前序,中序,后续(非递归版本)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.前序 二.后序 三.中序 总结 前言 二叉树的非递归版本的前序中序后序在面试中也经常考,必须熟练掌握! 下面将会讲 ...

  6. 数据结构(七)图的遍历(递归非递归方法)

    图的遍历(递归非递归方法) #include<iostream> #include<stdio.h> #include<stack> #include<que ...

  7. 数据结构(六)二叉树的遍历(递归非递归方法)

    数据结构(六)二叉树的遍历(递归非递归方法) 一.递归方法 1.先序遍历 void PreOrder(BiTree T) {visit(T);PreOrder(T->LChild)PreOrde ...

  8. 快速排序--QuickSort()--递归版本

    一.快速排序(递归版本) 1.快速排序模板(初始版本) void QuickSort(int* a, int begin, int end) {// 区间不存在,或者只有一个值则不需要在处理if (b ...

  9. 【好记性不如烂笔头】快速排序(三)非递归实现随机快排

    快速排序(三)非递归实现随机快排 前言

最新文章

  1. volatile关键字之全面深度剖析
  2. 李开复:走向全球的两大路径,中美何以各占其一
  3. 牛客 - Final Exam(贪心)
  4. Hibernate关联关系配置(一对多、一对一和多对多)
  5. Android 自定义View绘制的基本开发流程 Android自定义View(二)
  6. oc基础-protocol协议的使用
  7. 单纯学python能干啥_如何高效学习Python编程,转行的朋友可以过来看看,单纯的经验分享...
  8. 16QAM的理论误码率仿真
  9. 在企业ceph运维中问题处理解决方案---持续更新
  10. CodeForces 669A Little Artem and Presents
  11. Android Provision 的作用
  12. 赵一新:通勤研究与城市治理
  13. 简单Java的商品模块功能
  14. 自己编写自动同步脚本
  15. 人工智能细分领域受关注|人工智能|领域|智能
  16. http://jingyan.baidu.com/article/0eb457e5208cbb03f0a9054c.html
  17. 求生之路官方服务器延迟过高,《求生之路2》服务器tickrate的作用与网络参数的优化...
  18. Spring5 框架
  19. 云计算厂商决战2020:虽分高下,但不决生死
  20. CENTOS 8上安装极点五笔(自已测过)

热门文章

  1. 获取当前时间日期并格式化--JS
  2. 俄亥俄州立大学计算机科学排名,俄亥俄州立大学CS专业排名2020年
  3. 第10章 图与网络优化
  4. 【Paper】2015_Active fault-tolerant control system design with trajectory re-planning against actuator
  5. 4.8 代价函数-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  6. uboot中的中断macro宏
  7. 【PC工具】200412更新百度网盘下载工具——最终最简单百度网盘下载方法及注意事项...
  8. 360 开源企业级 Kubernetes 多集群管理平台 Wayne
  9. 深度解析HashMap
  10. 老男孩Linux运维第41期20171016第六周学习重点课堂记录