《算法导论》 — Chapter 7 高速排序
序
高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组。最坏情况执行时间为O(n^2)。
尽管这个最坏情况执行时间比較差。可是高速排序一般是用于排序的最佳有用选择。这是由于其平均性能相当好。期望的执行时间为O(nlgn)。且O(nlgn)中隐含的常数因子非常小。另外它还能够进行就地排序在虚拟环境中也能非常好的工作。
原理
高速排序也和合并排序一样,基于分治法,分为分解、解决、合并三个步骤。
分解:数组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)。
《算法导论》 — Chapter 7 高速排序的更多相关文章
- 基于visual Studio2013解决算法导论之055拓扑排序
题目 拓扑排序 解决代码及点评 // 拓扑排序.cpp : 定义控制台应用程序的入口点. // // 深度优先.cpp : 定义控制台应用程序的入口点. // // 图的邻接表表示.cpp : ...
- 基于visual Studio2013解决算法导论之012计数排序
题目 计数排序 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #in ...
- 排序算法的c++实现——计数排序
任何比较排序算法的时间复杂度的上限为O(NlogN), 不存在比o(nlgN)更少的比较排序算法.如果想要在时间复杂度上超过O(NlogN)的时间复杂度,肯定需要加入其它条件.计数排序就加入了限制条件 ...
- 《算法导论》 — Chapter 8 线性时间排序
序 到目前为止,关于排序的问题,前面已经介绍了很多,从插入排序.合并排序.堆排序以及快速排序,每一种都有其适用的情况,在时间和空间复杂度上各有优势.它们都有一个相同的特点,以上所有排序的结果序列,各个 ...
- 【从零学习经典算法系列】分治策略实例——高速排序(QuickSort)
在前面的博文(http://blog.csdn.net/jasonding1354/article/details/37736555)中介绍了作为分治策略的经典实例,即归并排序.并给出了递归形式和循环 ...
- 排序算法之高速排序(Java)
//高速排序 public class Quick_Sort { // 排序的主要算法 private int Partition(int[] data, int start, int end) { ...
- 《算法导论》 — Chapter 7 快速排序
序 快速排序(QuickSort)也是一种排序算法,对包含n个数组的输入数组,最坏情况运行时间为O(n^2).虽然这个最坏情况运行时间比较差,但是快速排序通常是用于排序的最佳实用选择,这是因为其平均性 ...
- (搬运)《算法导论》习题解答 Chapter 22.1-1(入度和出度)
(搬运)<算法导论>习题解答 Chapter 22.1-1(入度和出度) 思路:遍历邻接列表即可; 伪代码: for u 属于 Vertex for v属于 Adj[u] outdegre ...
- 《算法导论》读书笔记之排序算法—Merge Sort 归并排序算法
自从打ACM以来也算是用归并排序了好久,现在就写一篇博客来介绍一下这个算法吧 :) 图片来自维基百科,显示了完整的归并排序过程.例如数组{38, 27, 43, 3, 9, 82, 10}. 在算法导 ...
随机推荐
- webform中几个常用的控件
一,简单控件 1,Lable——标签:在网页中呈现出来的时候会变成span标签 属性:Text——标签上的文字 BackColor,ForeColor——背景色,前景色 Font——字体 Bold- ...
- ASP.NET Core 中文文档
ASP.NET Core 中文文档 翻译计划 五月中旬 .NET Core RC2 如期发布,我们遂决定翻译 ASP.NET Core 文档.我们在 何镇汐先生. 悲梦先生. 张仁建先生和 雷欧纳德先 ...
- 技术回归01-Windows内存分配工具
很久没有写技术方面的东西了,这半年主要是在学习别人的东西,对自己提高比较大,算是一次技术回笼吧,这次学习之旅目的是结束技术方面的专注,开始向应用方面找突破口,也就是完成技术积累或者为技术的积累做坚实的 ...
- 问题:Excel在“xxx.xlsx”中发现不可读取的内容。是否恢复此工作薄的内容?【原创】
现象: 点"是(Y)" 提示信息中提到的error242440_02.xml文件: 问题重现: package poi; import java.io.FileNotFoundEx ...
- 14.10.3 InnoDB Checkpoints InnoDB 检查点:
14.10.3 InnoDB Checkpoints InnoDB 检查点: 你的log files 变的很大可能会降低磁盘性能在checkpointing的时候, 它通常设置设置log files总 ...
- 史上最全的java随机数/字符串生成算法(转)
代码如下: package com.zuidaima.core.util; import java.util.Random; public class RandomUtil { public stat ...
- 基于Greenplum Hadoop分布式平台的大数据解决方案及商业应用案例剖析
随着云计算.大数据迅速发展,亟需用hadoop解决大数据量高并发访问的瓶颈.谷歌.淘宝.百度.京东等底层都应用hadoop.越来越多的企 业急需引入hadoop技术人才.由于掌握Hadoop技术的开发 ...
- HDU 4350 Card
打表找规律,比赛应付了一下,其实还可以把内存再优化一半掉,下面的0都是手动填充的,可以优化掉 题意: T个测试数据 下面52个数字表示 从栈顶到栈底的52个数 n l r表示 从栈顶下数 [l,r] ...
- 国际化之DateFormat、NumberFormat
之所以在国际化中介绍DateFormat和NumberFormat这两个类,一是因为本身这两个类是地区敏感类,即可用传入Locale对象:二是因为这两个类具有不同的输出模式,而这些模式能在国际化的动态 ...
- <IMG>中UserMap的用法
usemap是<img>标签的一个属性,用作指明所使用的图像地图名. 后面的#Map的Map就是用<map>标签定义的一个图像地图,它的name属性是Map,像下面这样定义: ...