数据结构---快速排序的三种实现方式

简介: 数据结构---快速排序的三种实现方式

快速排序

快速排序是所有排序算法中速度最快的一个排序算法(在数据量很庞大的前提下),因此,很多库中自带的sort都是用快速排序做底层实现的,例如qsort和STL中的sort,因此,学习好它是很有必要的

首先说明它的基本思想

基本思路是,选定一个元素为key,经过一系列算法让原本数组中比key小的数据在key的左边,比key大的数据在key的右边,然后递归进入key的左边,在递归函数中重复这个操作,最后就能完成排序,那么第一个关键点就是如何实现让以key为分界点,左右分别是比它大和比它小的?

关于这个算法有很多版本,我们一一介绍

hoare版

快速排序的创始人就是hoare,作为快速排序的祖师爷,hoare在研究快速排序自然写出了对应的算法,那么我们当然要先学习最经典的算法

下面展示hoare算法的示意图

在这里插入图片描述
在这里插入图片描述
看完演绎图和上面的流程图,大概可以理解hoare算法的基本思路,但其实还有一些问题,比如最后交换的元素(上图中为3) 一定比key小吗?比key大难道不会让大的元素到key的左边吗?

解释上述问题的原因

==其实问题的原因就在于left和right谁先走的问题,在上面算法中是让right先走,这是为什么?==
我们假设中间的元素不是3,而是8 (大于key的都可以) 那么,当right继续向前走的时候就会跳过8,继续向前找,最后最坏的结果会找到left,而left对应的是和前面交换后的比key小的元素,因此这里只要是right先走,最终和right和left相遇的位置一定比key小!

这个算法其实并不好写,需要控制的变量和问题很多,实现过程如下

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;
}

==注意点==

  1. keyi的选取是left而不是0,因为后面递归的时候最左边的元素下标不一定是0
  2. while循环向前/向后寻找时要随时判断left有没有小于right,防止越界
  3. 返回的是左值,这个左值就是下一次的左边界或右边界、

快速排序的实现

void QuickSort(int* a, int begin, int end)
{
   
   
    if (begin >= end)
    {
   
   
        return;
    }
    int keyi = PartSort1(a, begin, end);

    QuickSort(a, begin, keyi - 1);
    QuickSort(a, keyi + 1, end);
}

后续的三种写法只需要替换掉PartSort1即可

挖坑法

在这里插入图片描述
在这里插入图片描述
代码实现如下:

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 begin, int end)
{
   
   
    if (begin >= end)
    {
   
   
        return;
    }
    int keyi = PartSort2(a, begin, end);

    QuickSort(a, begin, keyi - 1);
    QuickSort(a, keyi + 1, end);
}

这个实现很简单,没有需要额外注意,相较第一个算法来说更容易理解一些

前后指针法

实现原理如下图所示

在这里插入图片描述
在这里插入图片描述
代码实现逻辑如下

int PartSort3(int* a, int left, int right)
{
   
   
    int cur = left+1;
    int prev = left;
    int keyi = left;
    while (cur <= right)
    {
   
   
        if (a[cur] < a[keyi])
        {
   
   
            ++prev;
            Swap(&a[prev], &a[cur]);
        }
        cur++;
    }

    Swap(&a[prev], &a[keyi]);

    return prev;
}

void QuickSort(int* a, int begin, int end)
{
   
   
    if (begin >= end)
    {
   
   
        return;
    }
    int keyi = PartSort3(a, begin, end);

    QuickSort(a, begin, keyi - 1);
    QuickSort(a, keyi + 1, end);
}

实际上是prev找cur,如果cur指针对应的值小于key,那么就++prev再交换,否则cur就继续前进,这样就能使得cur和prev之间的数据全部为比key大的数据

相关文章
|
1天前
|
存储 算法 搜索推荐
【初阶数据结构篇】冒泡排序和快速排序(中篇)
与直接插入排序法相比,比较次数一致,但冒泡排序的交换需要执行三次,而直接插入排序因为使用了tmp临时变量存储要插入的数据,只用执行一次,所以直接插入排序法效率明显更高。
|
1月前
|
搜索推荐
【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】
【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】
【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】
|
2月前
|
算法
数据结构与算法-快速排序
数据结构与算法-快速排序
18 1
|
3月前
|
存储 搜索推荐 算法
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)
|
2月前
|
算法 搜索推荐
数据结构和算法——快速排序(算法概述、选主元、子集划分、小规模数据的处理、算法实现)
数据结构和算法——快速排序(算法概述、选主元、子集划分、小规模数据的处理、算法实现)
20 0
|
2月前
|
搜索推荐
深入理解数据结构第五弹——排序(2)——快速排序
深入理解数据结构第五弹——排序(2)——快速排序
18 0
[数据结构]-玩转八大排序(二)&&冒泡排序&&快速排序
[数据结构]-玩转八大排序(二)&&冒泡排序&&快速排序
|
3月前
|
算法 索引
【数据结构与算法】:非递归实现快速排序、归并排序
上篇文章我们详细讲解了递归版本的快速排序,本篇我们来探究非递归实现快速排序和归并排序
【数据结构与算法】:非递归实现快速排序、归并排序
|
3月前
|
存储 算法 搜索推荐
【数据结构与算法】:选择排序与快速排序
欢迎来到排序的第二个部分:选择排序与快速排序!
【数据结构与算法】:选择排序与快速排序
|
3月前
|
算法 数据处理 C语言
【数据结构与算法】快速排序(详解:快排的Hoare原版,挖坑法和双指针法|避免快排最坏时间复杂度的两种解决方案|小区间优化|非递归的快排)
【数据结构与算法】快速排序(详解:快排的Hoare原版,挖坑法和双指针法|避免快排最坏时间复杂度的两种解决方案|小区间优化|非递归的快排)