文章目录

  • 1、快排的含义
  • 2、快排的实现
    • 2.1思路讲解
    • 2.2代码实现
  • 3、快排的时间复杂度分析
  • 4、快排的优化

上一篇文章我们介绍了八大排序中的七种,今天这篇文章主要来详细介绍一种比较重要也是常用的一种排序算法——快速排序~

1、快排的含义

快速排序是一种二叉树结构的交换排序方法。相当于是冒泡排序的一种升级,都是属于交换排序类,通过不断比较和移动交换来实现排序。

其基本思想是:任意取待排序元素序列中的某个元素作为基准值,按照这个排序码把待排序集合分割成两个子序列

  • 左子序列中所有元素都小于该基准值
  • 右子序列中所有元素都大于该基准值

然后左右两边子序列又重复该过程,直到所有元素都排列在相应的位置上为止。

2、快排的实现

2.1思路讲解

整个快排的核心就在于选取一个基准值先存放在一个临时变量里面,然后定义两个指针low和high分别指向数组的开头和末尾,并挨个儿和基准值比较。

  • 先从数组的末尾指针high开始向前遍历,查找比基准值小的数字,找到了就将其填充到low指向的位置
  • 再调换方向,从low开始向后遍历,查找比基准值大的数字,找到了就将其填充到high指向的位置
  • 再次循环交替进行遍历查找填充。循环结束的条件就是两个指针都指向了同一个位置。
  • 最后将临时变量存放的基准值填充到最后low指向的位置。
  • 接下来的过程就是一个递归的过程,在各自的子序列里面再进行上述过程。

第一趟快排流程如下:

2.2代码实现

1、对数组作快速排序

因为要进行递归过程,所以要传出low和high指针指向的位置

void QuickSort(int* arr, int n)
{Quick(arr, 0, n - 1);
}

2、递归函数Quick的实现

  • pivot为一次划分返回的基准值
  • 如果low还小于该基准值,说明一次划分后的左边还有元素,继续递归划分
  • 如果high还大于该基准值同理。
void Quick(int* arr, int low, int high)
{int pivot = Partition(arr, low, high);if (low < pivot){Quick(arr, low, pivot - 1);}if (pivot < high){Quick(arr, pivot +1,high);}
}

3、 Partition一次划分的过程
这段代码的核心部分是pivot = Partition(arr,low,high);使得他左边的值都比他小,右边的值都比他大。

【举个栗子】
数组值为[50,10,90,30,70,40,80,60,20]经过Partition(L,0,8)执行后,数组变成{20,10,40,30,50,70,80,60,90},并返回值4给pivot,然后递归调用QSort(L,1,4-1)QSort(L,4+1,9)语句,其实就是在对{20,10,40,30}{70,80,60,90}分别进行同样的Partition操作,直到顺序全部正确为止。

int Partition(int* arr, int low, int high)
{int temp = arr[low];while (low < high){while (low < high && arr[high] >= temp){high--;}arr[low] = arr[high];while (low < high && arr[low] <= temp){low++;}arr[high] = arr[low];}arr[low] = temp;return low;
}

还有另外一种思路参见博客快排详解
整体实现如下:

#include<iostream>
using namespace std;int Partition(int* arr, int low, int high)
{int temp = arr[low];while (low < high){while (low < high && arr[high] >= temp){high--;}arr[low] = arr[high];while (low < high && arr[low] <= temp){low++;}arr[high] = arr[low];}arr[low] = temp;return low;
}
void Quick(int* arr, int low, int high)
{int pivot = Partition(arr, low, high);if (low < pivot){Quick(arr, low, pivot - 1);}if (pivot < high){Quick(arr, pivot +1,high);}
}void QuickSort(int* arr, int n)
{Quick(arr, 0, n - 1);
}void show(int arr[], int n)
{for (int i = 0; i < n; i++){std::cout << arr[i] << " ";}std::cout << std::endl;
}void show(int arr[], int n)
{for (int i = 0; i < n; i++){std::cout << arr[i] << " ";}std::cout << std::endl;
}
int main()
{int arrrr[] = { 10,6,7,1,3,9,4,2 };int n = sizeof(arrrr) / sizeof(arrrr[0]);std::cout << "排序前的数组:";show(arrrr, n);QuickSort(arrrr, n);std::cout << "排序后的数组:";show(arrrr, n);
}

3、快排的时间复杂度分析

快排的时间性能取决于快排递归的深度。可以用递归树来描述递归算法的执行情况。如果排序n个关键字,其递归树的深度就是【log2n】+1,在最优的情况下,快速排序

  • 时间复杂度是O(nlogn)
  • 空间复杂度是O(logn)

4、快排的优化

1、优化选取枢轴
如果我们选取的基准值是处在整个序列的中间位置,那么我们可以将整个序列分成小数集合和大数集合。但是,假如我们选取的pivotkey不是中间数又如何呢?——可能导致整个系列没有一个实质性的变化。所以temp =arr[low];变成了一个潜在的性能瓶颈。

1.1随机化法获取基准值
使用随机树种子产生一个随机值pos下标。先让该下标所指向位置的值和数组的首元素交换,最后还是确定首元素为基准值。
只需要将partition函数的temp = arr[low];语句修改如下:

 int pos = rand() % (high - low + 1) + low;int temp = arr[pos];arr[pos] = arr[low];arr[low] = temp;temp = arr[low];

1.3三数取中法
但是还是会存在数据可能是随机的有序的情况,所以我们针对于随机选取基准点的方法还做出了优化,采用三数取中的方法:把low,mid,high所指的三个数据中,把中间大的数据放在最左边开始位置。
在上面快排代码的基础上进行增加改进的代码实现如下:

void Swap(int arr[], int first, int second)
{int temp = arr[first];arr[first] = arr[second];arr[second] = temp;
}
void GetMiddleMaxNum(int arr[], int low, int mid, int high)
{if (arr[mid] > arr[high]){Swap(arr, mid, high);}if (arr[low] < arr[high]){Swap(arr, low, high);}if (arr[low] > arr[mid]){Swap(arr, low, mid);}
}
void Quick(int* arr, int low, int high)
{int mid = (low + high) / 2;GetMiddleMaxNum(arr, low, mid, high);int pivot = Partition(arr, low, high);if (low < pivot){Quick(arr, low, pivot - 1);}if (pivot < high){Quick(arr, pivot +1,high);}
}

2、优化不必要的交换
我们发现,50这个关键字,其位置变化是1->9->3->6->5,可其实他的最终目标是5,当中的交换其实是不需要的。
所以我们对此进行优化:

int Partition(SqList *L,int low,int high)
{int pivotkey;//这里省略三数取中代码pivotkey = L->r[low];L->r[0] = pivotkey;//将枢轴关键字备份到L->r[0]while(low < high){while(low < high && L->r[high] >= pivotkey)high--;L->r[low] = L->r[high];//采用替换而不是交换的方式进行操作while(low < high && L->r[low] <= pivotkey)low++;L->r[high] = L->r[low];}L->r[low] = L->r[0];return low;//返回枢轴所在位置
}

3、优化小数组时的排序方案
如果数组非常小的情况下,快速排序反而不如直接插入排序来得更好(直接插入排序是简单排序中性能最好的)。
因此改进QSort函数代码实现如下:

void InsertSort(int* arr, int low,int high)
{int i, j, temp;for (i = low+1; i <= high; i++){temp = arr[i];for (j = i - 1; j >= low && arr[j] > temp; j--){arr[j + 1] = arr[j];}arr[j + 1] = temp;}return;
}
void Qsort(int* arr, int low, int high)
{int pivot;while (low < high){if ((high - low) < 20){InsertSort(arr, low, high);}pivot = Partition(arr, low, high);Qsort(arr, low, pivot - 1);Qsort(arr, pivot + 1, high);}
}

4、优化递归操作
递归对性能有一定影响,栈的大小是有限的,每一次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归耗费的口空间也越多。
对QSort实施尾递归优化:

#define MAX_LENGTH_INSERT_SORT 7
void QSort(sqList *L,int low,int high)
{int pivot;//当high-low大于常数时用快排if((high - low) > MAX_LENGTH_INSERT_SORT){while(low<high){pivot = Partition(L,low,high);QSort(L,low,pivot-1);low = pivot + 1;}}elseInsertSort(L);
}

将if改成while后,因为第一次递归以后,变量low就没有用处了,所以可以将pivot + 1的值赋值给low,再循环后Partition(L,low,high)的效果等同于Partition(L,pivot + 1,high)。
因此采用迭代而不是递归的方法可以缩减堆栈深度,从而提高了整体性能。

【数据结构】——快排详解相关推荐

  1. 23行代码_动图展示——快排详解(排序最快的经典算法)

    快排 1.快排的实现逻辑: 先从数列中取出一个数作为基准数(通常取第一个数). 遍历序列,将比它小的数与比它大的数分别记录下来,分为两类,最后该数放在这两类数中间(它左边的所有数都比它小,右边的所有数 ...

  2. 二分查找 归并排序 快排 详解C++

    这三个排序算法一直是面试的重点,大多数都是C语言写的,今天整理了一下C++的写法,思想都差不多.这几个排序经常忘记,今天抽空记在这,以便自己以后查阅,不对的地方,也欢迎大家评论,不吝指正,谢谢! 二分 ...

  3. 数据结构与算法详解目录

    数据结构与算法详解是一本以实例和实践为主的图书,主要是经典的数据结构与常见算法案例,来自历年考研.软考等考题,有算法思路和完整的代码,最后提供了C语言调试技术的方法. 后续配套微课视频. 第0章  基 ...

  4. 数据结构--图(Graph)详解(四)

    数据结构–图(Graph)详解(四) 文章目录 数据结构--图(Graph)详解(四) 一.图中几个NB的算法 1.普里姆算法(Prim算法)求最小生成树 2.克鲁斯卡尔算法(Kruskal算法)求最 ...

  5. 数据结构--图(Graph)详解(三)

    数据结构–图(Graph)详解(三) 文章目录 数据结构--图(Graph)详解(三) 一.深度优先生成树和广度优先生成树 1.铺垫 2.非连通图的生成森林 3.深度优先生成森林 4.广度优先生成森林 ...

  6. 数据结构--图(Graph)详解(二)

    数据结构–图(Graph)详解(二) 文章目录 数据结构--图(Graph)详解(二) 一.图的存储结构 1.图的顺序存储法 2.图的邻接表存储法 3.图的十字链表存储法 4.图的邻接多重表存储法 二 ...

  7. 数据结构--图(Graph)详解(一)

    数据结构–图(Graph)详解(一) 文章目录 数据结构--图(Graph)详解(一) 一.图的基本概念 1.图的分类 2.弧头和弧尾 3.入度和出度 4.(V1,V2) 和 < V1,V2 & ...

  8. java语言链栈_Java语言实现数据结构栈代码详解

    近来复习数据结构,自己动手实现了栈.栈是一种限制插入和删除只能在一个位置上的表.最基本的操作是进栈和出栈,因此,又被叫作"先进后出"表. 首先了解下栈的概念: 栈是限定仅在表头进行 ...

  9. 用数据结构c语言写成绩排序,C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 # ...

最新文章

  1. netty4.0.x源码分析—bootstrap
  2. 编程名言(有些趣味性)
  3. jax-rs jax-ws_JAX-RS Bean验证错误消息国际化
  4. java 调用r语言包传参数_Java与R语言的配置,调用
  5. jquery ajax php中 css样式不显示,Chrome浏览器在Ajax同步调用之前不会显示Jquery的动态css Propery更改...
  6. 服务器响应速度是上行速度吗,服务器带宽与速度之间的关系
  7. 阿里云上运行Docker版本的PostgreSQL
  8. PHP水仙花问题解法之一
  9. add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'
  10. 计算机知识提炼,2017计算机考研:操作系统复习策略及重要知识点提炼
  11. U盘文件系统格式快速转换
  12. 2022年危险化学品经营单位安全管理人员考试题库及模拟考试
  13. 泰信科技携手浙大建高校IT运维平台
  14. 魔兽TBC常用WA字符串收集
  15. python模块专题——1.faker
  16. 谷歌字体连接超时解决方案
  17. 飞思卡尔k66,k60无限重启
  18. 计算机和人脑在线阅读,人脑与电脑(原文)
  19. 小程序接入微信视频号配置指南
  20. 手写数字识别--Android Studio 加载tensorflow模型

热门文章

  1. 无法在Web服务器上启动调试。调试器无法连接到远程计算机。原因可能是远程计算机不存在或防火墙阻止与远程计算机通信。
  2. 手机APP软件制作平台搭建(基于windows)
  3. 计算机班级logo设计图案简单,为自己的班级设计一枚标志图案,并说明你所设计图案的含义...
  4. 简历一般用什么底色的照片?一起来了解简历相关知识吧
  5. 一个简单的测试检测阿里的ecs.s6-c1m1.small云主机CPU性能
  6. 征战AIOT市场,realme走心了吗?
  7. 2020版revit机电管线功能及如何快速管线排布
  8. 数字金字塔 (15分)
  9. 第K小元素 时间复杂度n
  10. LED滚动显示算法及实现