《算法导论》 — Chapter 7 高速排序[通俗易懂]

《算法导论》 — Chapter 7 高速排序

大家好,又见面了,我是全栈君。

高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组。最坏情况执行时间为O(n^2)。

尽管这个最坏情况执行时间比較差。可是高速排序一般是用于排序的最佳有用选择。这是由于其平均性能相当好。期望的执行时间为O(nlgn)。且O(nlgn)中隐含的常数因子非常小。另外它还能够进行就地排序在虚拟环境中也能非常好的工作。

GitHub chapter 7 程序代码下载

原理

高速排序也和合并排序一样,基于分治法,分为分解、解决、合并三个步骤。
分解:数组array[low…high]被分为两个(可能空)子数组array[low…temp-1]和array[temp+1…high]。使得array[low…temp-1]中的每个元素都小于等于array[temp],而array[temp+1…high]中的每个元素都大于array[temp],下标temp也是在这个过程中被计算出来;
解决:通过递归的调用高速排序。对子数组array[low…temp-1],array[temp+1…high]进行排序;
合并:由于两个子数组是就地排序的。将他们的合并不须要操作,整个数组array[low…high]是已经排好序的。

本章介绍了高速排序算法的原理、程序实现(包括随机化版本号)及其性能分析。

快排算法实现

#include <iostream>
#include <ctime>
#include <cstdlib>
#define N 10

using namespace std;

//高速排序的递归算法
void quickSort(int * array, int low, int high);
//求切割点
int partition(int * array, int low, int high);
//交换两个变量的值
void exchange(int &a, int &b);

int main()
{
    //声明一个待排序数组 
    int array[N];
    //设置随机化种子,避免每次产生同样的随机数 
    srand(time(0));
    for (int i = 0; i<N; i++)
    {
        array[i] = rand() % 101;//数组赋值使用随机函数产生1-100之间的随机数 
    }
    cout << "排序前:" << endl;
    for (int j = 0; j<N; j++)
    {
        cout << array[j] << " ";
    }
    cout << endl << "排序后:" << endl;
    //调用高速排序函数对该数组进行排序 
    quickSort(array, 0, N - 1);
    for (int k = 0; k<N; k++)
    {
        cout << array[k] << " ";
    }
    cout << endl;
    return 0;
}//main

void quickSort(int * array, int low, int high)
{
    if (low < high)
    {
        int temp = partition(array, low, high);
        quickSort(array, low, temp - 1);
        quickSort(array, temp + 1, high);
    }
}

int partition(int * array, int low, int high)
{
    int i = low - 1;
    //默认将划分段的最后一个元素为主元
    int x = array[high];

    for (int j = low; j<high; j++)
    {
        if (array[j] <= x)//在array[i]左边都是小于x即array[high]的数,右边均是大于它的数
        {
            i += 1;
            exchange(array[i], array[j]);
        }
    }
    exchange(array[i + 1], array[high]);
    return i + 1;//所以循环完成后。i+1就是该数组的切割点
}
void exchange(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

高速排序的随机化版本号

在上面介绍的高速排序算法实现中,Partition(A , p , r)总是默认A[r]为主元,作为比較标准。假设能够採用随机取样的随机化技术的话。将会使得分析更加简单。以下是随机化版本号的高速排序算法实现:

#include <iostream>
#include <ctime>
#include <cstdlib>
#define N 10

using namespace std;

//高速排序的递归算法
void quickSort(int * array, int low, int high);
//求切割点
int partition(int * array, int low, int high);

//以low ~ high 之间的一个随机元素作为主元 , 求切割点
int randomPartition(int *array, int low, int high);

//交换两个变量的值
void exchange(int &a, int &b);

int main()
{
    //声明一个待排序数组 
    int array[N];
    //设置随机化种子,避免每次产生同样的随机数 
    srand(time(0));
    for (int i = 0; i<N; i++)
    {
        array[i] = rand() % 101;//数组赋值使用随机函数产生1-100之间的随机数 
    }
    cout << "排序前:" << endl;
    for (int j = 0; j<N; j++)
    {
        cout << array[j] << " ";
    }
    cout << endl << "排序后:" << endl;
    //调用高速排序函数对该数组进行排序 
    quickSort(array, 0, N - 1);
    for (int k = 0; k<N; k++)
    {
        cout << array[k] << " ";
    }
    cout << endl;

    system("pause");

    return 0;
}//main

void quickSort(int * array, int low, int high)
{
    if (low < high)
    {
        int temp = randomPartition(array, low, high);
        quickSort(array, low, temp - 1);
        quickSort(array, temp + 1, high);
    }
}

int partition(int * array, int low, int high)
{
    int i = low - 1;
    //默认将划分段的最后一个元素为主元
    int x = array[high];

    for (int j = low; j<high; j++)
    {
        if (array[j] <= x)//在array[i]左边都是小于x即array[high]的数。右边均是大于它的数
        {
            i += 1;
            exchange(array[i], array[j]);
        }
    }
    exchange(array[i + 1], array[high]);
    return i + 1;//所以循环完成后,i+1就是该数组的切割点
}

int randomPartition(int *array, int low, int high)
{
    //找到low ~ high 之间的一个随机位置
    int i = rand() % (high - low + 1) + low;

    //交换该随机主元至尾部,
    exchange(array[i], array[high]);

    return partition(array, low, high);
}

void exchange(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

随机版本号的快排与普通快排差别并非非常大,修改的不过求切割点步骤中的主元选取,也就是添加了randomPartition函数,选定好主元元素下标i后。将该元素交换至段尾,依旧调用partition函数求切割点。

高速排序性能分析

高速排序的执行时间与划分是否对称有关。而后者又与选择了哪一个元素进行划分有关。假设划分是对称的,那么本算法在渐近意义上与合并排序一样快。假设划分是不正确称的那么本算法在渐进意义上与插入排序一样慢。以下分别讨论高速排序的最坏情况划分、最佳情况划分、平衡的划分。
最坏情况划分:高速排序的最坏情况划分行为发生在划分过程中产生的两个区域分别包括n-1个元素和0个元素的时候。假设算法每次递归调用都出现了这样的不正确称划分。划分的时间代价为O(n)。由于对一个大小为0的数组进行递归调用后,返回了T(n)=O(1),故算法的执行时间可递归的表示为:
T(n) = T(n-1) + T(0) + O(n) = T(n-1) + O(n)
从直观上来看。假设将每一层递归的代价加起来,就能够得到一个算术级数(等式(array,2)其和值的量极为O(n^2))利用代换法能够比較直接的证明递归式 T(n) = T(n-1) + O(n)的解为 T(n) = O(n^2)。
因此假设在算法的每一层递归上,划分都是最大程度不正确称的。那么算法的执行时间为O(n^2),亦即高速排序算法的最坏情况执行时间不如插入排序的好。

此外当输入数组全然排好序时,高速排序的执行时间是O(n^2),而插入排序的执行时间为O(n)。
最佳情况划分:在Partition可能做的最平衡划分中,得到的两个子问题的大小都不可能大于[n/2],由于若当中一个子问题的大小为[n/2]。则另外一个子问题的大小必定为[n/2]-1。在这样的情况下。高速排序的执行速度要快得多。这时表达其执行时间的递归式为:
T(n) <= 2T(n/2) + O(n)
解该递归式可得T(n) = O(nlgn)。由于在每一层递归划分的两边都是对称的。因此从渐进意义上来看。算法执行的就更快了。

平衡的划分: 高速排序的平均情况执行时间与其最佳情况执行时间非常接近,而不是非常接近与其最坏情况执行时间(证明原因具体參考《算法导论》原书第二版P88),由于不论什么一种按常数比例进行划分都会产生深度为O(lgn)的递归树,当中每一层的代价都是O(n),因而每当依照常数比例进行划分时,总的执行时间都是O(nlgn)。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116443.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • tess4j验证码识别

    tess4j验证码识别tess4j的安装和使用参考:https://www.cnblogs.com/cmyxn/p/6993422.htmltess4j提高识别率1.对称近邻均值滤波参考:http://blog.csdn.net/fangbinwei93/article/details/505624492.指定config为digits,并修改tessdata\configs\digits文件,将白名单中设置…

  • 第五章:redis持久化,包括rdb和aof两种方式[通俗易懂]

    第五章:redis持久化,包括rdb和aof两种方式[通俗易懂]第五章:redis持久化,包括rdb和aof两种方式

  • 2021最新最细致的IDEA集成SVN工具的使用 (入门到精通)

    2021最新最细致的IDEA集成SVN工具的使用 (入门到精通)SVN教程1、SVN常见操作发布项目(shareproject)项目组长将本机项目第一次发布到中央仓库中下载项目(检出项目checkout)组员将中央仓库中的项目第一次下载到本地提交(commit)将本地修改的内容同步到服务器中(本地=>服务器)编写完一个小功能之后、每天下班前一定要及时提交更新(update)将服务器中最新的代码同步到本地(服务器=>本地)编写功能之前,每天上班前一定要及时更新2、SVN安装2.1svn服务端

    2022年10月17日
  • 手机分辨率大小自适应功能怎么关闭_显示器自适应分辨率

    手机分辨率大小自适应功能怎么关闭_显示器自适应分辨率分辨率,自适应,媒体查询

  • onedrive个人版免费扩容_onedrive会员

    onedrive个人版免费扩容_onedrive会员这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好!这是你第一次使用Markdown编辑器所展示的欢迎页。如果你想学习如何使用Mar

  • 找jaeger_CQB初探

    找jaeger_CQB初探导读:有一天我们接到这样一条客诉“你们的收银软件最近特别慢,严重影响我们的收银效率,再不解决我们就不用了”,我相信大家应该都遇到过这种问题,即使现在没遇到,将来一定会遇到的,那遇到了怎么办呢?就这个话

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号