一些常用排序算法
1、插入排序
-
概念
插入排序算法是一个对少量元素进行排序的有效算法,是一种稳定排序算法(不会交换值相同的元素)。
最好时间复杂度为O(n),最坏时间复杂度为O(n2),平均时间复杂度为O(n2),空间复杂度为O(1)。 -
理解
从一本书上看到描述这个算法的一段话,我觉得很贴切,很好理解:
插入排序类似于打牌。摸牌时,刚开始手上没有牌,摸出第一张,随意放在左手上, 以后每一次摸牌,都会按照花色从小到大地将牌插入到合适的位置, 直到所有牌摸完。
例:
// { (4) , 8 , 3 , 9 , 0 , 2 , 7 }
// { (4 , 8) , 3 , 9 , 0 , 2 , 7 }
// { (3 , 4 , 8) , 9 , 0 , 2 , 7 }
// { (3 , 4 , 8 , 9) , 0 , 2 , 7 }
// { (0 , 3 , 4 , 8 , 9) , 2 , 7 }
// { (0 , 2 , 3 , 4 , 8 , 9) , 7 }
// { (0 , 2 , 3 , 4 , 7 , 8 , 9) } -
代码
void InsertSort(int *num,int n){
int i;
for(i=1;i<n;i++){ //遍历数组中的元素
int j=i-1; //当前元素的前一个元素,即 num[i-1]
int t=num[i]; //保存 num[i]
while( j >= 0 && t < num[j]){ //逐个向前比较
num[j+1]=num[j]; //将已比较的元素往后移
j--;
}
num[j+1]=t; //插入相应位置
}
}
- 上述代码注意点
- 没有设置“哨兵”,即用数组num的第一个元素 num[0] 来存储待排序元素。
- 从小到大排序
- 用于整型数据
2、冒泡排序
-
概念
冒泡排序算法是一种交换排序算法,是一种稳定排序算法。
最好时间复杂度为O(n),最坏时间复杂度为O(n2),平均时间复杂度为O(n2),空间复杂度为O(1)。 -
理解
根据我的理解,冒泡排序的核心为 比较+交换。元素之间两两比较,大的一方往后移。这个过程就像水中的气泡一样,重气泡往下沉,轻气泡往上浮。
例:
数组第一次遍历,比较了 n-1 次并根据需要进行了元素交换,这样值最大的元素就“冒”到了数组的最后;
第二次遍历时,只需比较 n-2 次,这样值第二大的元素就“冒”到了数组的倒数第二个位值;
……
以此类推,当只进行了比较,而无发生交换时,则排序终止。 -
代码
void bubbleSort(int *num,int n){
int flag; //交换标志
int i,t,j;
for(i=0;i<n-1;i++){ //进行遍历
flag=0; //重置交换标志
j=0;
while(j<n-i-1){ //两两进行比较
if(num[j]>num[j+1]){
flag=1; //发生交换,将标志设为 1
t=num[j+1];
num[j+1]=num[j];
num[j]=t;
}
++j;
}
if(flag==0) break; //若无发生交换则排序已完毕
}
}
- 上述代码注意点
- 从小到大排序
- 用于整型数据
3、快速排序
-
概念
在快速排序中,元素的比较和移动是从两端向中间进行的。
快速排序是一个递归的过程,是一种不稳定的算法。
最好时间复杂度为O(nlog2n),最坏时间复杂度为O(n2),平均时间复杂度为O(nlog2n)。 -
理解
-
快速排序的基本思想是:
①选一个轴值(即比较的基准),将待排序元素划分为独立的两部分。
②左侧元素均<=轴值,右侧元素均>=轴值
③分别对这两部分重复上述过程,直到整个序列有序。 -
一次划分的基本步骤:
①选轴值。轴值的选取可从序列两端选取,也可从中间选取。(这里选左端
② i -> 左,j -> 右
③ a [ j ] 与轴值(即 a [ i ] )比较,若 a [ j ] <轴值,a [ j ] 与轴值交换位置,循环结束。此时轴值即为a [ j ] ; 否则 j = j - 1,循环继续;
④a [ i ] 与轴值(即 a [ j ] )比较,若 a [ i ] >轴值,a [ i ] 与轴值交换位置,循环结束。此时轴值即为a [ i ] ; 否则 i = i + 1,循环继续;
⑤当 i = j 时,一次划分结束。 -
代码
快速排序一次划分:
int Partition(int *a,int first,int last){
int i=first,j=last;
while(i<j){ // i,j不是指向同一个值时
for(i;i<j;i++) //从左边开始扫描,以 a[j] 为轴值
if(a[i]>a[j]){ //若左边的值比右边的值大,则两个值进行交换
int t=a[i];
a[i]=a[j];
a[j]=t;
break; //左边扫描结束
}
for(j;j>i;j--){ //从右边开始扫描,以 a[i] 为轴值
if(a[j]<a[i]){ //若右边的值比左边的值小,则两个值进行交换
int t=a[j];
a[j]=a[i];
a[i]=t;
break; //右边扫描结束
}
}
}
if(i==j) return i; //返回最后的位置
}
快速排序:
void QuickSort(int *a,int first,int last){
int i=first,j=last;
if(i<j){ //区间长度大于一,进行一次划分,否则递归结束
int t=Partition(a,i,j); //一次划分
QuickSort(a,i,t-1); //递归地对左侧子序列进行快速排序
QuickSort(a,t+1,j); //递归地对右侧子序列进行快速排序
}
}
- 上述代码注意点
- 从小到大排序
- 用于整型数据
4、选择排序
-
概念
选择排序是最基本及简单的排序算法之一,它重复地从待排序的元素里选出最大或最小的元素进行排序。
选择排序是一种不稳定的算法。
最好、最坏和平均时间复杂度都为O(n2)。 -
理解
(1)将整个序列划分为有序区和无序区,初始时有序区为空,无序区含有待排序的所有元素。
(2)在无序区中选取值最小的元素,与无序区的第一个元素交换,使得有序区扩展了一个元素,同时无序区减少了一个元素。
(3)重复(2)步骤,直到无序区只剩下一个元素。此时所有元素已按从小到大的顺序排列。 -
过程示例:
初始序列 :[ 49 27 65 97 76 13 38 ]
第一趟 : 13 [ 27 65 97 76 49 38 ]
第二趟 : 13 27 [ 65 97 76 49 38 ]
第三趟 : 13 27 38 [ 97 76 49 65 ]
第四趟 : 13 27 38 49 [ 76 97 65 ]
第五趟 : 13 27 38 49 65 [ 97 76 ]
第六趟 : 13 27 38 49 65 76 97 -
代码
void selectSort(int *a,int n){
int index=0,i,j;
for(i=0;i<n;i++){ //对 n 个单元进行 n 趟简单选择排序
index=i; //记录最小值的下标
for(j=i+1;j<n;j++){ //在无序区中选取最小值
if(a[j]<a[index]) index=j;
}
if(index!=i){ //当无序区的最小值不是无序区的第一个元素时,两元素交换
int t=a[i];
a[i]=a[index];
a[index]=t;
}
}
}
- 上述代码注意点
- 从小到大排序
- 用于整型数据