剑指Offer上的快速排序的Partition函数与我在数据结构书上学到的不一样,因此就想要探索下这两种不同的处理方式。

基本思想

快速排序的基本思想是基于分治法。在待排序表L[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两部分L[1...k-1]和L[k+1...n],使得L[1...k-1]中所有元素小于pivot,L[k+1...n]中所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上。而后递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

剑指Offer上的Partition实现

  1. // Partition为分割函数, length表示data数组的长度
  2. int Partition(int data[], int length, int low, int high) {
  3. if (data == nullptr || length <= 0 || low < 0 || high >= length) {
  4. return 1;
  5. }
  6. // 在[low, high]区间中随机取一个基准值,并将其换到区间最末尾
  7. int index = RandomInRange(low, high);
  8. Swap(&data[index], &data[high]);
  9. // small在low的前面一位
  10. int small = low - 1;
  11. for (index = low; index < high; ++index) {
  12. if (data[index] < data[high]) {
  13. // ++small后表示大于基准值的第一个数的下标
  14. // 如果不存在大于基准值的数,则表示当前比较的数字下标
  15. ++small;
  16. // 交换后的small表示小于基准值的最后一个数字的下标
  17. if (small != index) {
  18. Swap(&data[index], &data[small]);
  19. }
  20. }
  21. }
  22. // ++small后表示大于基准值的第一个数的下标
  23. ++small;
  24. Swap(&data[small], &data[high]);
  25. return small;
  26. }

快速排序其它函数实现如下:


  1. // 生成一个随机数,范围在[low,high),是一个前闭后开的区间
  2. int RandomInRange(int low, int high) {
  3. return rand() % (high - low) + low;
  4. }
  5. // 交换两个整数
  6. void Swap(int* left, int* right) {
  7. int temp = *left;
  8. *left = *right;
  9. *right = temp;
  10. }
  11. // 快速排序函数
  12. void QuickSort(int data[], int length, int low, int high) {
  13. if (low == high) {
  14. return;
  15. }
  16. int index = Partition(data, length, low, high);
  17. if (index > low) {
  18. QuickSort(data, length, low, index - 1);
  19. }
  20. if (index < high) {
  21. QuickSort(data, length, index + 1, high);
  22. }
  23. }
  24. // 数据打印
  25. void PrintData(int data[], int length) {
  26. for (int i = 0; i < length; ++i) {
  27. std::cout << data[i] << " ";
  28. }
  29. std::cout << std::endl;
  30. }
  31. int main() {
  32. // 以系统时间作为随机函数种子
  33. srand((unsigned)time(nullptr));
  34. int const length = 10;
  35. int data[length];
  36. // 生成随机数
  37. for (int i = 0; i < length; ++i) {
  38. data[i] = RandomInRange(1, length*length);
  39. }
  40. // 打印生成的随机数
  41. PrintData(data, length);
  42. // 调用快排函数进行排序
  43. QuickSort(data, length, 0, length - 1);
  44. // 打印快速排序后的数
  45. PrintData(data, length);
  46. return 0;
  47. }

初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:

步骤 small index
0 49 38 65 97 76 13 27
1 27 38 65 97 76 13 49
2 0 0 27 38 65 97 76 13 49
3 1 1 27 38 65 97 76 13 49
4 1 2 27 38 65 97 76 13 49
5 1 3 27 38 65 97 76 13 49
6 1 4 27 38 65 97 76 13 49
7 2 5 27 38 13 97 76 65 49
8 3 6 27 38 13 49 76 65 97

第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 65, 97}都比49大。

一般教材上的Partition实现

  1. // Partition为分割函数
  2. int Partition(int data[], int low, int high) {
  3. // 将当前表中第一个元素设为基准值,对表进行划分
  4. int pivot = data[low];
  5. // 循环跳出条件
  6. while (low < high) {
  7. while (low < high && data[high] >= pivot) {
  8. --high;
  9. }
  10. // 将比基准值小的元素移动到左端
  11. data[low] = data[high];
  12. while (low < high && data[low] <= pivot) {
  13. ++low;
  14. }
  15. // 将比基准值大的元素移动到右端
  16. data[high] = data[low];
  17. }
  18. // 基准元素存放到最终位置
  19. data[low] = pivot;
  20. // 返回存放基准值的最终位置
  21. return low;
  22. }

快速排序其它函数实现如下:

  1. void QuickSort(int data[], int low, int high) {
  2. // 递归跳出条件
  3. if (low < high) {
  4. // 将data[low...high]一分为二,pivotpos是基准值
  5. int pivotpos = Partition(data, low, high);
  6. // 对左子表进行递归排序
  7. QuickSort(data, low, pivotpos - 1);
  8. // 对右子表进行递归排序
  9. QuickSort(data, pivotpos + 1, high);
  10. }
  11. }

初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:

步骤 low high
0 0 6 49 38 65 97 76 13 27
1 2 6 27 38 65 97 76 13 27
2 2 6 27 38 65 97 76 13 65
3 2 5 27 38 13 97 76 13 65
4 3 5 27 38 13 97 76 97 65
5 3 3 27 38 13 49 76 97 65

第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 97, 65}都比49大。

总结

可以看出,上述这两种思想导致的运行过程是不同的,前者是用small指代小于标准元素的最右位置,在遍历过程中进行交换操作来保证small左边都是小于标准元素的元素,而后者是用low,high分别表示小于和大于基准值的集合边界位置,所以后者更浅显易懂。


个人主页:

www.codeapes.cn

剑指Offer-快速排序的更多相关文章

  1. 剑指 Offer 40. 最小的k个数 + 优先队列 + 堆 + 快速排序

    剑指 Offer 40. 最小的k个数 Offer_40 题目描述 解法一:排序后取前k个数 /** * 题目描述:输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7. ...

  2. [读]剑指offer

    研二的开始找工作了,首先祝愿他们都能够找到自己满意的工作.看着他们的身影,自问明年自己这个时候是否可以从容面对呢?心虚不已,赶紧从老严那儿讨来一本<剑指offer>.在此顺便将自己做题所想 ...

  3. 剑指OFFER——调整数组顺序使奇数位于偶数前面

    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 剑指offer书里的版本, ...

  4. 剑指Offer——顺丰笔试题+知识点总结

    剑指Offer--顺丰笔试题+知识点总结 情景回顾 时间:2016.10.16 19:00-20:40 地点:山东省网络环境智能计算技术重点实验室 事件:顺丰笔试 知识点总结 快排 霍尔排序(快排) ...

  5. 剑指Offer——知识点储备-常用算法

    剑指Offer--知识点储备-常用算法 快速排序 注:若排序是有序的,采用快排,则退化为冒泡排序. 解决这个问题,采用两个选取基准的方法 (1)随机选取基数(在这个区间内随机取一个数) 出现的恶劣情况 ...

  6. 剑指Offer——好未来视频面知识点储备+面后总结

    剑指Offer--好未来视频面知识点储备+面后总结 情景介绍 时间:2016.10.12 13:00- 地点:宿舍 事件:好未来视频面 知识点储备 数据结构 单链表反转 public class Li ...

  7. 剑指Offer——分治算法

    剑指Offer--分治算法 基本概念 在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更 ...

  8. 剑指offer:调整数组顺序使奇数位于偶数前面

    题目 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 分析 事实上,这个题比较简单,很多种方式都可以实现,但是其时间复杂度或空间复 ...

  9. 关于数据结构,剑指offer上面的

    我很喜欢那些javascript解决的编程题,感觉非常的有意思.我在博客园上面看到了一个同学的博客,他一共发了34篇剑指offer的编程题,还给出了非常详细的解答. 接下来的工作,我做的就是搬运工,不 ...

  10. 《剑指offer》内容总结

    (1)剑指Offer——Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常 ...

随机推荐

  1. java application.properties 密码加密

    main方法 public static void main(String[] args) { BasicTextEncryptor textEncryptor = new BasicTextEncr ...

  2. PTA(Basic Level)1027.打印沙漏

    本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个"*",要求按下列格式打印 ***** *** * *** ***** 所谓"沙漏形状",是指每行 ...

  3. linux centos 7.3 编译安装mysql5.7

    #安装依赖 yum update yum install -y gcc gcc-c++ make libtool zlib zlib-devel openssl openssl-devel pcre ...

  4. cmd_操作MySQL数据库

    建议用 TXT 文档来写代码 然后粘贴至cmd命令直接运行创建students库,表名为student,表包含以下字段: id(主键) name(姓名) age(年龄) sex(性别) sc(综合积分 ...

  5. java执行bat代码

    java执行bat代码.txt public static void runbat(String path,String filename) { String cmd = "cmd /c s ...

  6. Linux内核、mysql内核、Tcp/Ip内核、java等知识书籍

    LINUX <linux内核设计与实现>(2011年出版,链接:https://pan.baidu.com/s/107hriLNVt05A8egeU8Du-g  密码:0cgn) < ...

  7. jQuery选择器引入

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  8. 梳理common-io工具包

    title: 梳理common-io工具包 comments: false date: 2019-08-28 14:21:58 description: 对common-io工具包中的常用类进行整理, ...

  9. react + antd Form表单校验

    非空限制 {getFieldDecorator('name', { rules: [{ required: true, message: '名称不能为空', }],})( <Input plac ...

  10. Nginx配置,请求到tomcat中

    一.web服务器分为两类 1.web服务器 1)Apache服务器 2)Nginx 3)IIS 2.web 应用服务器 1)tomcat 2)resin 3)jetty 区分:web服务器不能解析js ...