一:冒泡排序(Bubble_Sort)
冒泡排序
(百度百科)
冒泡排序动态图
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单基础的排序算法。其基本思路是,对于一组要排序的元素列,依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面,如此继续,直到比较到最后的两个数,将小数放在前面,大数放在后面,重复步骤,直至全部排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。[1]
void Bubble_Sort(ElementType arr[], int N){
for(int p = N - 1; p >= 0; p--){ //进行N - 1次排序
for(int i = 0; i < p; i++){
if(arr[i] > arr[i + 1]){
swap(arr[i], arr[i + 1]); //如果比下一位大 就交换
}
}
}
}
缺点:这样无论原本的序列是怎样 一定会遍历O(N)次,如果在中途已经是完全有序的了 ,我们可以用一个flag标记一下,从而进行优化
#include<iostream>
using namespace std;
typedef int ElementType;
ElementType arr[]={26,45,78,1,32,16,26,5,7,8,3,26};
void Bubble_Sort(ElementType arr[], int N){
int flag = 0; //初始标记
for(int p = N - 1; p >= 0; p--){ //进行N - 1次排序
flag = 0;
for(int i = 0; i < p; i++){ //一次冒泡
if(arr[i] > arr[i + 1]){
swap(arr[i], arr[i + 1]); //如果比下一位大 就交换
flag = 1; //发生了交换
}
}
if(flag == 0){ //全程没有交换,已经是有序的
break;
}
}
}
int main()
{
int N = sizeof(arr) / sizeof(int); //N 就代表数组的总容量
Bubble_Sort(arr, N);
for(int i = 0; i < sizeof(arr) / sizeof(int) ; i++){
cout << arr[i] << " ";
}
}
算法分析:
复杂度:O(N^2) best :O(N) Worst :O(N^2)
稳定性:因为交换时只有严格大于才进行交换,因此算法是稳定的
缺点:时间复杂度过高(对于排序算法来说O^2算是慢的了)
优点:编码简单, 稳定,可用于单链表的排序(这是其他排序算法所不具备的特性)
二:插入排序(Insertion Sort)
大家都应该打过扑克牌吧,其实插入排序就和我们摸牌时候整理手中的牌一个道理(如果你不整理牌,那你就不是一个合格的勾机人【手动狗头】)
基本思想:面前的arr就是牌堆,你一张一张的摸排,在手上进行排序,小的放在上面(小的还是大的看你心情),那我来了一张牌肯定是要给他插个空的,就依次进行比较,直到找到(摸的牌)小于等于手中的牌的位置,就插进去
例如:手中牌:2 5 6 8 9
摸了一张7 从9 开始向上找空 找到了6 和 8 中间 然后放进去
调整的过程就是将arr[i] = arr[i + 1]
void Insertion_Sort(ElementType arr[], int N){
for(int p = 1; p < N; p++) {
int tmp = arr[p];
int i; //在牌堆中摸的牌
for( i = p; i >= 1 && arr[i - 1] > tmp; i--){
arr[i] = arr[i - 1]; //腾出空位
}
arr[i] = tmp; //新牌落位
}
}
算法分析:
复杂度:O(N^2) best :O(N) Worst :O(N^2)
稳定性:因为交换时只有严格大于才进行交换,因此算法是稳定的
缺点:时间复杂度过高(对于排序算法来说O^2算是慢的了)
优点:编码简单, 稳定,可用于单链表的排序(这是其他排序算法所不具备的特性)
简单排序分析(bubble_sort & insertion_sort)
原理就是每一次交换会消除一个逆序对(因为我们想让序列是递增的嘛),而一个序列平均有(N)(N-1)/4个逆序对 所以肯定是不能特别快的
简单排序每次只能是相邻的进行判断,顶多消除一个逆序对,下面我们看一下不是相邻元素判断的sort算法
三:希尔排序
特点就是利用了插入排序的算法特性并且克服了插入排序每次仅仅能够消除一个逆序对的缺点(听起来好像很不戳哦)
希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。[1]
举栗子:
arr [] = {81,94,11,96,12,35,17,95,28,58,41,47,15}
首先进行五间隔排序,五间隔就是每隔5个单位选一次 组成子序列
那么第一个子序列就是81 35 41 (每隔5个元素选取一次)
下面就用插入排序对子序列进行排序 放到对应的位置上
依次进行插入排序 并且放到对应的位置上
下面考虑的子序列就是94 17 75
后面就是依次插入排序
最终得到了五间隔排序的效果
同理依次减小间隔数直到间隔数为1为止
最终就是希尔排序的结果
特点就是 小间隔排序之后并不会改变大间隔的有序性 那么随着间隔数的不断减小 至少有序性不会变差(不是一定变好)
void Shell_Sort(ElementType arr[], int N){
//初始化的间隔数默认是Dm = N / 2 每次间隔数都是除以二 Dk = Dk+1 / 2
int D,p,i;
for(int D = N / 2; D >= 1; D /= 2){ //希尔增量序列
//下面的代码和插入排序基本相同 就是将增量由1 改成了D而已
for(int p = D; p < N; p++) {
int tmp = arr[p];
int i; //在牌堆中摸的牌
for( i = p; i >= D && arr[i - D] > tmp; i-=D){
arr[i] = arr[i - D]; //腾出空位
}
arr[i] = tmp; //新牌落位
}
}
}
复杂度不算很理想 最好最坏都是O(N^2)
原因就是上述所说的,有的间隔不一定会使有序性增加(可能是在做一个无用功)
不难发现 前三个排序竟然一丁点用都没有,真是气人啊
根本原因就是因为间隔数都不互质
因此可以用上述递增序列
void ShellSort( ElementType A[], int N )
{ /* 希尔排序 - 用Sedgewick增量序列 */
int Si, D, P, i;
ElementType Tmp;
/* 这里只列出一小部分增量 */
int Sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};
for ( Si=0; Sedgewick[Si]>=N; Si++ )
; /* 初始的增量Sedgewick[Si]不能超过待排序列长度 */
for ( D=Sedgewick[Si]; D>0; D=Sedgewick[++Si] )
for ( P=D; P<N; P++ ) { /* 插入排序*/
Tmp = A[P];
for ( i=P; i>=D && A[i-D]>Tmp; i-=D )
A[i] = A[i-D];
A[i] = Tmp;
}
}