排序算法
一、准备知识
衡量算法的好坏指标,显然首先这个算法是正确的,除此之外,通常有三个方面的考虑:
(1)算法在执行过程中所消耗的时间;
(2)算法在执行过程中所占资源的大小,例如,占用内存空间的大小;
(3)算法的易理解性、易实现性和易验证性等等。我们经常讨论的时间复杂度是比较常见的评价标准之一。
• 算法的时间复杂度定义:
若有某个辅助函数f(n),当n趋向于无穷大时,如果T(n)/ f(n)的极限为不等于零的常数,则认为T(n)与f(n)是同量级的函数,记作:T(n) =O(f(n)),O(f(n))称为算法的渐进时间复杂度,简称时间复杂度。
• 渐进时间复杂度意义:
(1)在较复杂的算法中,进行精确分析是非常复杂的;
(2)一般来说,我们并不关心T(n)的精确度量,而只是关心其量级。T (n) = O(f (n)) 表示存在一个常数C,当n趋于正无穷大时,总有T (n) ≤ C * f(n),其意义是T(n)在n趋于正无穷大时跟f(n)基本接近,因此完全可以用f(n)来表示T(n)。
在各种不同的算法中,若算法语句的执行次数为常数,则算法的时间复杂度为O(1),按数量级递增排列,常见的时间复杂度量有:
(1)O(1):常量阶,运行时间为常量
(2)O(logn):对数阶,如二分搜索算法
(3)O(n):线性阶,如n个数内找最大值
(4)O(nlogn):对数阶,如快速排序算法
(5)O(n^2):平方阶,如选择排序,冒泡排序
(6)O(n^3):立方阶,如两个n阶矩阵的乘法运算
(7)O(2^n):指数阶,如n个元素集合的所有子集的算法
(8)O(n!):阶乘阶,如n个元素全部排列的算法
• 计算步骤
(1)找出算法中重复执行次数最多的语句的频度来估算算法的时间复杂度;
(2)保留算法的最高次幂,忽略所有低次幂和高次幂的系数;
(3)将算法执行次数的数量级放入大Ο记号中。
二、部分排序算法的实现
排序算法的稳定性:(1)稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;(2)不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
//变量值交换
template<class T>
void swap(T& a, T& b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
//一维数组打印
template<class T>
void printArray(T data[], int n) {
for (int i = 0; i < n; i++) {
std::cout << data[i] << " ";
}
std::cout<<std::endl;
}
1、选择排序及其优化版本
选择排序
//普通选择排序
//每次调动indexOfMax(a,size)需要执行size-1次比较,因此总共的比较次数为n-1+n-2+...+1=n(n-1)/2
//取最高次,则表示为渐进记法为O(n^2)
//最差时间复杂度:O(n^2)
//最优时间复杂度:O(n^2)
//平均时间复杂度:O(n^2)
//稳定性:不稳定
template<class T>
int indexOfMax(T a[], int n) {
if (n <= 0)
return -1;
int max_index=0;
for (int i = 0; i < n; i++) {
if (a[max_index] < a[i])
max_index = i;
}
return max_index;
}
template<class T>
void selectionSort(T a[], int n) {
for (int size = n; size > 1; size--) {
int j = indexOfMax(a, size);
if(j!=size-1)
swap(a[j], a[size - 1]);
}
}
//优化:即时终止选择排序
template<class T>
void selectionSort_(T a[], int n) {
bool sorted = false;
for (int size = n; !sorted && (size > 1); size--) {
int indexOfMax = 0;
sorted = true;
for (int i = 1; i < size; i++) {
if (a[indexOfMax] <= a[i]) indexOfMax = i;
else sorted = false; //检查有序性 false代表无序
}
if(indexOfMax!=size-1)
swap(a[size - 1], a[indexOfMax]);
}
}
2、冒泡排序及其优化版本
//冒泡排序
//在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,
//让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
//对任意数k ,函数bubble(T a[], int n) 比较n-1次,则n-1+n-2+...1=n(n-1)/2
//最差时间复杂度:O(n^2)
//最优时间复杂度:O(n^2)
//平均时间复杂度:O(n^2)
//稳定性:稳定
template<class T>
void bubble(T a[], int n) {
for (int i = 0; i < n - 1; i++) {
if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
}
}
template<class T>
void bubbleSort(T a[], int n) {
for (int i = n; i > 1; i--) {
bubble(a, i);
}
}
//优化:即时终止冒泡排序
template<class T>
bool bubble2(T a[], int n) {
bool swapped = false;
for (int i = 0; i < n - 1; i++) {
if (a[i] > a[i + 1]) {
swap(a[i], a[i + 1]);
swapped = true;
}
}
return swapped;
}
template<class T>
void bubbleSort2(T a[], int n) {
for (int i = n; i > 1 && bubble(a, i); i--);
}
3、插入排序
//插入排序
//插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
//1)将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
//2)从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
//最差时间复杂度:O(n^2)
//最优时间复杂度:O(n)
//平均时间复杂度:O(n ^ 2)
//稳定性:稳定
template<class T>
void insertionSort(T a[], int n){
for (int i = 1; i < n; i++) //i从index=1开始,index=0第一个元素当做最初的单元有序数组
{
int temp = a[i];
int j = i;
while (j > 0 && a[j - 1] > temp)
{
a[j] = a[j - 1];
j--;
}
a[j] = temp;
}
}
4、快速排序
/快速排序:分治法、递归
//1、从数列中取出一个数作为基准数(枢轴,pivot)。
//2、将数组进行划分(partition),将比基准数大的元素都移至枢轴右边,将小于等于基准数的元素都移至枢轴左边。
//3、再对左右的子区间重复第二步的划分操作,直至每个子区间只有一个元素。
//平均时间复杂度: O(nlogn)
//最坏时间复杂度: O(n*n) 比如降序数组排成升序,反之则反之
//最好时间的复杂度: O(nlogn) 二叉树深度logn
//稳定性:不稳定
template<class T>
int partition(T data[], int left,int right) {
int i = 0, j = 0, tmp = 0;
i = left + 1;
j = right;
tmp = data[left];
while (i <= j) {
while (data[i] < tmp)
i++;
while (data[j] > tmp)
j--;
if (i < j)
swap(data[i++], data[j--]);
else
i++;
}
swap(data[left], data[j]);
return j;
}
template<class T>
void quickSort(T data[], int left, int right) {
if (left > right)
return;
int mid = 0;
mid = partition(data, left, right);
quickSort(data, left, mid - 1);
quickSort(data, mid + 1, right);
}
5、待续…
主函数验证:
int main() {
std::cout << "before sort : " << std::endl;
float data[10] = { 1,5,2,4,6,0,8,9,3,7 };
printArray(data, 10);
//bubleSort(data, 10);
//selectiveSort(data, 10);
//selectionSort_(data, 10);
//insertionSort(data, 10);
quickSort(data, 0, 9);
std::cout << "after sort: " << std::endl;
printArray(data, 10);
system("pause");
return 0;
}
参考:
https://blog.csdn.net/yq272393925/article/details/89146451
https://baijiahao.baidu.com/s?id=1609024533531824968&wfr=spider&for=pc
《数据结构、算法与应用》