【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)

简介: 本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。

前言

       之前我们学习了快速排序算法及其实现:


https://developer.aliyun.com/article/1636641?spm=a2c6h.13148508.setting.14.65434f0eP498IS


不过它的缺陷也很明显:当数组中存在大量相同元素时,那些与基准值相同的元素的划分方法是未定义的,这将导致运行效率的下降。基于此问题,今天给大家介绍快速排序的升级版--三路快排,它能够很大程度地解决大量数据相同的情况。


一、三路快排的整体思路

       所谓三路快排,就是从快速排序的划分上,由原来的两部分变为三部分:左边是比基准值小的数据;中间是与基准值相同的数据;右边是比基准值大的数据。这样划分出来,之后递归细分时,每一次生成的中间部分就不需要再进行划分了,提高了整体的运行效率。


      之前我们的快速排序当中,都是默认将数组首元素作为基准值,如果该值是数组的最大值或者最小值,那么划分操作相当于只是将该值进行了排序,时间复杂度就会退化为O(N^2)。所以接下来我们在选取基准值时,将会使用三数取中法,将首元素、末元素与中间元素进行比较,选取一个中间值作为基准值


       划分的具体步骤如下:


1. 创建两“指针” left 和 right ,分别指向待排区间的两端;找到基准值之后与left位置交换,让其位于首元素位置。


2. 创建“指针” cur,指向left的后一个位置。


3. 当cur遇到比基准值小的元素时,其与left位置交换,然后cur和left各向后走一步;


   当cur遇到比基准值大的元素时,其与right位置交换,然后right向前走一步;


   当cur遇到与基准值相同元素时,cur向后走一步,访问后面的元素。


4. 当cur超过right位置时,划分结束,退出循环。


我们画图模拟一下它的划分过程:



注意划分完成后left和right所处的位置,便于确定下次递归的区间。


二、三路快排的具体实现

       接下来,我们开始实现三路快排。首先写好测试数据、交换两元素的函数与三数取中法:


1.测试数据、交换函数和三数取中法

#include <stdio.h>
 
int main()
{
    int arr[] = { 9,4,1,5,5,6,2,2,2,8,4,4,0,3,3,8,7 };//测试数据
    int sz = sizeof(arr) / sizeof(arr[0]);//计算出数组大小
 
    //这里排序数据
 
    for (int i = 0; i < sz; i++)//打印数组
    {
        printf("%d ", arr[i]);
    }
    return 0;
}
//交换两元素
void Swap(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
//三数取中法,返回中间值的下标
int MidOfThree(int* arr, int a, int b, int c)
{
    if (arr[a] > arr[b])
    {
        if (arr[b] > arr[c])
        {
            return b;
        }
        else if (arr[a] > arr[c])
        {
            return c;
        }
        else
        {
            return a;
        }
    }
    else
    {
        if (arr[a] > arr[c])
        {
            return a;
        }
        else if (arr[b] > arr[c])
        {
            return c;
        }
        else
        {
            return b;
        }
    }
}

2.三路快排函数

接下来,我们尝试实现三路快排的划分以及递归:

//三路快排
void QuickSort(int* arr, int left, int right)
{
    if (left >= right)//划分的区间达到最小,停止递归
    {
        return;
    }
 
    //记录左右边界与中间元素的下标
    int begin = left;
    int end = right;
    int mid = (begin + end) / 2;
 
    //确定基准值,并将其与首元素交换
    Swap(&arr[MidOfThree(arr, begin, mid, end)], &arr[left]);
 
    int key = arr[left];//记录基准值
    int cur = left + 1;
 
    //遍历数组,开始划分
    while (cur <= right)
    {
        if (arr[cur] < key)//比基准值小的情况
        {
            Swap(&arr[cur++], &arr[left++]);
        }
        else if (arr[cur] > key)//比基准值大的情况
        {
            Swap(&arr[cur], &arr[right--]);
        }
        else//与基准值相等的情况
        {
            cur++;
        }
    }
 
    //划分完成后,递归遍历左区间和右区间,中间部分不需要再参与排序
    //注意此时left和right的位置
    QuickSort(arr, begin, left - 1);
    QuickSort(arr, right + 1, end);
}

测试结果:



可以看到,数组排序成功了。

三、程序全部代码

三路快排的相关程序全部代码如下:

#include <stdio.h>
 
//交换两元素
void Swap(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
 
//三数取中法,返回中间值的下标
int MidOfThree(int* arr, int a, int b, int c)
{
    if (arr[a] > arr[b])
    {
        if (arr[b] > arr[c])
        {
            return b;
        }
        else if (arr[a] > arr[c])
        {
            return c;
        }
        else
        {
            return a;
        }
    }
    else
    {
        if (arr[a] > arr[c])
        {
            return a;
        }
        else if (arr[b] > arr[c])
        {
            return c;
        }
        else
        {
            return b;
        }
    }
}
 
//三路快排
void QuickSort(int* arr, int left, int right)
{
    if (left >= right)//划分的区间达到最小,停止递归
    {
        return;
    }
 
    //记录左右边界与中间元素的下标
    int begin = left;
    int end = right;
    int mid = (begin + end) / 2;
 
    //确定基准值,并将其与首元素交换
    Swap(&arr[MidOfThree(arr, begin, mid, end)], &arr[left]);
 
    int key = arr[left];//记录基准值
    int cur = left + 1;
 
    //遍历数组,开始划分
    while (cur <= right)
    {
        if (arr[cur] < key)//比基准值小的情况
        {
            Swap(&arr[cur++], &arr[left++]);
        }
        else if (arr[cur] > key)//比基准值大的情况
        {
            Swap(&arr[cur], &arr[right--]);
        }
        else//与基准值相等的情况
        {
            cur++;
        }
    }
 
    //划分完成后,递归遍历左区间和右区间,中间部分不需要再参与排序
    //注意此时left和right的位置
    QuickSort(arr, begin, left - 1);
    QuickSort(arr, right + 1, end);
}
 
int main()
{
    int arr[] = { 9,4,1,5,5,6,2,2,2,8,4,4,0,3,3,8,7 };//测试数据
    int sz = sizeof(arr) / sizeof(arr[0]);//计算出数组大小
 
    //这里排序数据
    QuickSort(arr, 0, sz - 1);
 
    for (int i = 0; i < sz; i++)//打印数组
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

总结

       快速排序是一种高效且常用的排序算法,但是传统的快排并没有对与基准值相同的数据进行明确划分,造成运行效率的降低。因此出现了三路快排,它按照基准值将数组分成了三份:左边是比基准值小的数据;中间是与基准值相同的数据;右边是比基准值大的数据。这样,与基准值相同的数据就不需要再次划分,提高了整体的运行效率。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

相关文章
|
18天前
|
存储 搜索推荐 Python
用 Python 实现快速排序算法。
快速排序的平均时间复杂度为$O(nlogn)$,空间复杂度为$O(logn)$。它在大多数情况下表现良好,但在某些特殊情况下可能会退化为最坏情况,时间复杂度为$O(n^2)$。你可以根据实际需求对代码进行调整和修改,或者尝试使用其他优化策略来提高快速排序的性能
114 61
|
17天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
46 1
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
90 8
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
93 7
|
1月前
|
机器学习/深度学习 人工智能 算法
青否数字人声音克隆算法升级,16个超真实直播声音模型免费送!
青否数字人的声音克隆算法全面升级,能够完美克隆真人的音调、语速、情感和呼吸。提供16种超真实的直播声音模型,支持3大AI直播类型和6大核心AIGC技术,60秒快速开播,助力商家轻松赚钱。AI讲品、互动和售卖功能强大,支持多平台直播,确保每场直播话术不重复,智能互动和真实感十足。新手小白也能轻松上手,有效规避违规风险。
|
2月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
27 1
|
2月前
|
搜索推荐 Java Go
深入了解快速排序算法
深入了解快速排序算法
48 2
|
2月前
|
存储 搜索推荐 算法
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
|
2月前
|
算法 Python
Python算法编程:冒泡排序、选择排序、快速排序
Python算法编程:冒泡排序、选择排序、快速排序
30 0
|
2月前
|
搜索推荐 C语言 C++
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)