【数据结构与算法】快速排序的三种实现方法

简介: 【数据结构与算法】快速排序的三种实现方法

一.基本思想

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

二.Hoare法

假设我们让最左边为keyi(注意这个表示的是下标),且要排升序;

1.若最左边为keyi,则right先走,找比arr[keyi]小的,left后走,找比arr[keyi]大的,然后right与left交换;

  当left和right相遇时,结束循环,最后交换arr[keyi]和arr[left]

  再让keyi=left,接着递归它的左子列和右子列

2.若最右边为keyi,则left先走,找比arr[keyi]大的,right后走,找比arr[keyi]小的,然后right与left交换

  当left和right相遇时,结束循环,最后交换arr[keyi]和arr[left]

  再让keyi=right,接着递归它的左子列和右子列

注意这里的keyi,left,right都是下标

左子列的区间是begin到keyi-1

右子列的区间是keyi+1到end

 

动态演示

 

1. void QuickSort(int* arr, int left,int right)
2. {
3. 
4.  if (left >= right)
5.    return;
6.  int begain = left;
7.  int end = right;
8.  int keyi= left;
9.  while (left < right)
10.   {
11. //这里要先判断left<right,防止越界,下同
12.     while (left<right && arr[right]>=arr[keyi])   //找小
13.     {
14.       right--;
15.     }
16. 
17.     while (left < right && arr[left] <= arr[keyi])  //找大
18.     {
19.       left++;
20.     }
21. 
22.     Swap(&arr[left], &arr[right]);   //交换
23.   }
24. 
25.   Swap(&arr[keyi], &arr[left]);
26.   keyi = left;
27. 
28.   QuickSort(arr, begain, keyi-1);   //递归左子列
29.   QuickSort(arr, keyi+1, end);      //递归右子列
30. }

三.挖坑法

动态演示

上面的Hoare法有很多坑,一不注意就容易写错,而挖坑法就没那么多坑了。

这个方法需要定义一个坑变量(hole),前面的Hoare法是交换两个元素,挖坑法是把值赋给坑位,然后更新一下坑位 。

1. void QuickSort(int* arr, int left, int right)
2. {
3. 
4.  if (left >= right)
5.    return;
6.  int begain = left;
7.  int end = right;
8.  int keyi = left;
9.  int hole = left;   //坑变量
10.   while (left < right)
11.   {
12.     while (left < right && arr[right] >= arr[leyi])   //找小
13.     {
14.       right--;
15.     }
16.     arr[hole] = arr[right];   //赋值给坑位
17.     hole = right;   //更新坑位
18.     while (left < right && arr[left] <= arr[keyi])   //找大
19.     {
20.       left++;
21.     }
22. 
23.     arr[hole] = arr[left];
24.     hole = left;
25.   }
26. 
27.   arr[hole] = key;
28.   keyi = left;
29. //递归左右子列
30.   QuickSort(arr, begain, hole - 1);
31.   QuickSort(arr, hole + 1, end);
32. }

四.前后指针法

动态演示

这个方法可以说是实现快速排序最常用的方法了。

1.定义一个prev和cur,它们都表示数组的下标,当然你定义成指针也没问题,这里以下标为例;

2.cur=prev+1

 注意:

          a.prev要么紧跟着cur,即prev的下一个就是cur;

          b.prev跟cur中间隔着比key大的一段值区间;

3.cur找到比key小的值,prev++,cur和prev位置互换,cur++

4.cur找到比key大的值,cur++

5.当cur>right时结束循环

1. void QuickSort(int* arr, int left, int right)
2. {
3.  if (left >= right)
4.    return;
5.  int begin = left, end = right;
6.  int prev = left, cur = left + 1;
7. 
8.  int keyi = left;
9.  while (cur <= right)
10.   {
11.     if (arr[cur] < arr[keyi])   
12.     {
13.       prev++;
14.       Swap(&arr[cur], &arr[prev]);
15.       cur++;
16.     }
17. 
18.     while (arr[cur] > arr[keyi])
19.     {
20.       cur++;
21.     }
22.   }
23.   Swap(&arr[keyi], &arr[prev]);
24.   keyi = prev;
25.   QuickSort(arr, begin, keyi - 1);
26.   QuickSort(arr, keyi + 1, end);
27. }

五.快速排序优化

在面对有序或是接近有序的情况下,快速排序的效率不高,是O(N^2),那要怎么解决这个问题呢?

 

既然对有序或是接近有序的不行,那我们就打乱它的顺序,这里有两种方法:

1.通过生成区间内的随机下标,让keyi与randi的数据交换,这样就打乱了原来的顺序;

2.三路取中法。

随机下标交换法

1. int randi = left + rand() % (right - left);   //随机key
2. Swap(&arr[keyi], &arr[randi]);

三路取中法

1. int GetMid(Sdatatype* arr, int left, int right)
2. {
3.  int mid = (left + right) / 2;
4.  if (arr[left] < arr[mid])
5.  {
6.    if (arr[mid] < arr[right])
7.      return mid;
8.    else if (arr[right] < arr[left])
9.      return left;
10.     else
11.       return right;
12.   }
13.   else  //arr[left]>arr[mid]
14.   {
15.     if (arr[right] < arr[mid])
16.       return mid;
17.     else if (arr[right] > arr[left])
18.       return left;
19.     else
20.       return right;
21. 
22.   }
23. 
24. }
25. 
26.   if (left != midi)
27.     Swap(&arr[left], &arr[midi]);

六.快速排序的特性

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定


🐬🤖本篇文章到此就结束了,若有错误或是建议的话,欢迎小伙伴们指出;🕊️👻

😄😆希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;🥰🤩

😍😁谢谢你的阅读。😸😼


目录
相关文章
|
11天前
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
38 4
|
1月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
41 3
|
1月前
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
23 3
|
1月前
|
算法 索引
HashMap扩容时的rehash方法中(e.hash & oldCap) == 0算法推导
HashMap在扩容时,会创建一个新数组,并将旧数组中的数据迁移过去。通过(e.hash & oldCap)是否等于0,数据被巧妙地分为两类:一类保持原有索引位置,另一类索引位置增加旧数组长度。此过程确保了数据均匀分布,提高了查询效率。
38 2
|
1月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
21 1
|
1月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
53 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
1月前
|
存储
ES6中的Set数据结构的常用方法和使用场景
ES6中的Set数据结构的常用方法和使用场景
|
1月前
|
存储 算法 索引
HashMap底层数据结构及其增put删remove查get方法的代码实现原理
HashMap 是基于数组 + 链表 + 红黑树实现的高效键值对存储结构。默认初始容量为16,负载因子为0.75。当存储元素超过容量 * 负载因子时,会进行扩容。HashMap 使用哈希算法计算键的索引位置,通过链表或红黑树解决哈希冲突,确保高效存取。插入、获取和删除操作的时间复杂度接近 O(1)。
29 0
|
1月前
|
存储 搜索推荐 算法
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
|
1月前
|
算法 Java C语言
【数据结构】后缀(逆波兰)表达式的计算以及中缀转后缀的方法
【数据结构】后缀(逆波兰)表达式的计算以及中缀转后缀的方法
35 0