[数据结构 -- 手撕排序算法第六篇] 递归实现快速排序(集霍尔版本,挖坑法,前后指针法为一篇的实现方法,很能打)1

简介: [数据结构 -- 手撕排序算法第六篇] 递归实现快速排序(集霍尔版本,挖坑法,前后指针法为一篇的实现方法,很能打)1

1、常见的排序算法

1.1 交换排序基本思想

冒泡排序属于交换排序之一,我们先来了解以下冒泡排序思想。
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。



2、快速排序的实现方法

递归实现与二叉树的前序遍历很相似,将区间划分为左右两半部分的常见方式有三种:1.hoare版本;2.挖坑法;3.左右指针法。

2.1 基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中

的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右

子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止 。


所以,在后面很重要的一点就是,左边走找小,右边走找大。

3 hoare(霍尔)版本

3.1 实现思路

我们规定排升序,排序数组名称为a,基准值 key,基准值下标 keyi,左 left,右 right。
1.选出一个key,key可以是需要排序的数组中任意一个元素,我们本篇文章选key为a[left];


2.从数组右端(尾部)往左走,当a[right] < key(a[keyi]),right停下来;


3.然后从数组左端(首部)往右走,当a[left] > key(a[keyi]),left停下来;


4.交换a[left], a[right];


5.循环步骤2、3、4,当 left与right 走到相同位置时,交换此位置的元素与 key,key 的位置就确定了。


6.此时 key 就将数组分为了左右两个区间,我们对左右两个区间分别使用前5步,然后再次确定了左右区间的key,递归左区间,再递归右区间,就实现了排序。


注意:步骤2、3的顺序不能换。至于为什么,我们思路图解后再说。

3.2 思路图解

我们的图解就是按照实现思路来画的。

3.3 为什么实现思路的步骤2、3不能交换

我们可以看到,思路图解里,当最后找到 3 时候是 R 先走,R先遇到 3,如果是 L 先走,左找大,L 遇见 3 不会停下来,会直接走到R的位置,R 位置是 9,这时候相遇,就交换,一旦交换,左区间就不是全部小于 key(6)了,这就会出现错误。因此,key 如果选在左边,右边先走。
Q:hoare版本我们是理解了,如果key选在右边,那是不是左边先走呢?


A:答案是一定的。这里其实就是 L遇到R 还是 R遇到L 的问题。我们依然可以用上面的解法来想这问题,当L遇到R 时,说明 R 已经停下,R 停下就是 a[right]<key,这时 L遇到R,相遇位置就是小于 key 的位置,交换(key,a[left]),这时最后一个小于 key 的元素就放在了最左边,而 key 就到了确定的位置,不用再调了,以key 为中点的左区间全部小于 key,右区间全大于 key;


key在右边,当 R遇到L 时,说明 L 已经停下,L 停下就是 a[left]>key,这时 R遇到L,相遇位置就是大于 key 的位置,交换(a[left],key),这时最后一个大于 key 的元素就放在了最右边,而 key 就到了确定的位置,不用再调了,以 key 为中点的左区间全部小于 key,右区间全大于 key。


所以如果 key 选在左边,就先走右边;key 选在右边,就先走左边。

3.4 hoare版本代码实现

// 快速排序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;
}
void QuickSort(int* a, int left, int right)
{
  if (left >= right)
    return;
  int keyi = PartSort1(a, left, right);
  QuickSort(a, left, keyi - 1);
  QuickSort(a, keyi + 1, right);
}

3.5 hoare版本代码测试

void Swap(int* p1, int* p2)
{
  int tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}
void Print(int* a, int n)
{
  for (int i = 0; i < n; i++)
  {
    printf("%d ", a[i]);
  }
  printf("\n");
}
 //快速排序递归实现
 //快速排序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[left], &a[keyi]);
  return left;
}
void QuickSort(int* a, int left, int right)
{
  if (left >= right)
    return;
  int keyi = PartSort1(a, left, right);
  QuickSort(a, left, keyi - 1);
  QuickSort(a, keyi + 1, right);
}
void test()
{
  int a[] = { 6,3,2,1,5,7,9 };
  QuickSort(&a, 0, sizeof(a) / sizeof(int) - 1);
  Print(&a, sizeof(a) / sizeof(int));
}
int main()
{
  test();
  return 0;
}



4、挖坑法

4.1 实现思路

我们规定排升序,排序数组名称为a,基准值 key,左 left,右 right。

1.选出一个key,key可以是需要排序的数组中任意一个元素,我们依然选key为a[left],将a[left]交给key保存起来,被选为key的元素位置就是坑位;


2.从数组右端(尾部)往左走,当a[right] < key,right停下来,将a[right]放到坑位中,坑位就换为下标为 right 的位置,此时right不动;


3.然后从数组左端(首部)往右走,当a[left] > key,left停下来,将a[left]放到坑位中,坑位就换为下标为 left 的位置,此时left不动;


4.循环步骤2、3,当 left与right 走到相同位置时,这个位置就是最后一个坑位,将key放到这个坑位中,key的最终位置就确定下来了,后面就不用再调整了。


5.此时 key 就将数组分为了左右两个区间,我们对左右两个区间分别使用前4步,然后再次确定了左右区间的key,递归左区间,再递归右区间,就实现了排序。
挖坑法与hoare版本的区别:


1.挖坑法不需要去想最后的 L与R 相遇的位置的元素要小于key,因为最终相遇位置是坑位,直接将 key 放入坑位就好了;


2.不用去想为什么选左边为 key 要右边先走,选右边为 key 要左边先走,当选左边为 key 时,因为需要填左边的坑,所以肯定是右边先走。左边找小,右边找大来理解这句话更合适。

4.2 思路图解



4.3 挖坑法代码实现

// 快速排序挖坑法
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;
  }
  a[hole] = key;
  return hole;
}
void QuickSort(int* a, int left, int right)
{
  if (left >= right)
    return;
  int keyi = PartSort2(a, left, right);
  QuickSort(a, left, keyi - 1);
  QuickSort(a, keyi + 1, right);
}

4.4 挖坑法代码测试

void Swap(int* p1, int* p2)
{
  int tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}
void Print(int* a, int n)
{
  for (int i = 0; i < n; i++)
  {
    printf("%d ", a[i]);
  }
  printf("\n");
}
// 快速排序挖坑法
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;
  }
  a[hole] = key;
  return hole;
}
void QuickSort(int* a, int left, int right)
{
  if (left >= right)
    return;
  int keyi = PartSort2(a, left, right);
  QuickSort(a, left, keyi - 1);
  QuickSort(a, keyi + 1, right);
}
void test()
{
  int a[] = { 6,3,2,1,5,7,9 };
  QuickSort(&a, 0, sizeof(a) / sizeof(int) - 1);
  Print(&a, sizeof(a) / sizeof(int));
}
int main()
{
  test();
  return 0;
}

相关文章
|
9月前
|
存储 编解码 算法
【多光谱滤波器阵列设计的最优球体填充】使用MSFA设计方法进行各种重建算法时,图像质量可以提高至多2 dB,并在光谱相似性方面实现了显著提升(Matlab代码实现)
【多光谱滤波器阵列设计的最优球体填充】使用MSFA设计方法进行各种重建算法时,图像质量可以提高至多2 dB,并在光谱相似性方面实现了显著提升(Matlab代码实现)
289 6
|
9月前
|
机器学习/深度学习 算法 调度
14种智能算法优化BP神经网络(14种方法)实现数据预测分类研究(Matlab代码实现)
14种智能算法优化BP神经网络(14种方法)实现数据预测分类研究(Matlab代码实现)
647 0
|
10月前
|
机器学习/深度学习 数据采集 传感器
具有多种最大功率点跟踪(MPPT)方法的光伏发电系统(P&O-增量法-人工神经网络-模糊逻辑控制-粒子群优化)之使用粒子群算法的最大功率点追踪(MPPT)(Simulink仿真实现)
具有多种最大功率点跟踪(MPPT)方法的光伏发电系统(P&O-增量法-人工神经网络-模糊逻辑控制-粒子群优化)之使用粒子群算法的最大功率点追踪(MPPT)(Simulink仿真实现)
672 0
|
8月前
|
机器学习/深度学习 数据采集 负载均衡
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
401 0
|
8月前
|
机器学习/深度学习 算法 物联网
基于遗传方法的动态多目标优化算法
基于遗传方法的动态多目标优化算法
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
1824 6
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
816 4
|
算法 搜索推荐
快速排序-数据结构与算法
快速排序(Quick Sort)是一种基于分治法的高效排序算法。其核心思想是通过选择基准(pivot),将数组划分为左右两部分,使得左侧元素均小于基准,右侧元素均大于基准,然后递归地对左右两部分进行排序。时间复杂度平均为 O(n log n),最坏情况下为 O(n²)(如数组已有序)。空间复杂度为 O(1),属于原地排序,但稳定性不佳。 实现步骤包括编写 `partition` 核心逻辑、递归调用的 `quickSort` 和辅助函数 `swap`。优化方法有随机化基准和三数取中法,以减少最坏情况的发生。
776 13
|
存储 搜索推荐 Python
用 Python 实现快速排序算法。
快速排序的平均时间复杂度为$O(nlogn)$,空间复杂度为$O(logn)$。它在大多数情况下表现良好,但在某些特殊情况下可能会退化为最坏情况,时间复杂度为$O(n^2)$。你可以根据实际需求对代码进行调整和修改,或者尝试使用其他优化策略来提高快速排序的性能
462 61