一、冒泡排序
冒泡排序是最简单的排序算法。假设数组一共有n个元素,元素最大下标为n-1,冒泡排序的具体做法是:第一趟在序列(A[0]~A[n-1])中从前往后进行两个相邻元素的比较,若前者大,则交换,比较 n-1 次;第一趟排序结束,最大元素被交换到A[n-1]中,下一趟排序只需要在子序列(A[0]~A[n-2])中进行;依次类推,冒泡排序最多进行 n-1 趟。基本的冒泡排序可以利用旗标的方式稍微减少一些比较的时间,当寻访完序列后都沒有发生任何的交换动作,表示排序已经完成,而无需再进行之后的比较与交换动作。
优点:稳定,比较次数已知;
缺点:慢,每次只能移动相邻两个数据,移动数据的次数多。
C语言实现:
#include<stdio.h>
void bubbleSort(int arr[],int n);
void swap(int *a,int *b);
#define N 10
int main(){
int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
bubbleSort(arr,N);
printf("排序后:");
for(i=0;i<N;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
/*冒泡排序*/
void bubbleSort(int arr[],int n){
int i,j,flag=1;
for(i=0;i<n-1&&flag;i++){ // 最多进行n-1趟,加flag可以提前结束比较过程
flag=0;
for(j=0;j<n-1-i;j++){ //减去i,就是剩下的要比较的元素个数
if(arr[j]>arr[j+1]){
swap(&arr[j],&arr[j+1]);
flag=1;
}
}
}
}
/*两数交换*/
void swap(int *a,int *b){
int temp=*a;
*a=*b;
*b=temp;
}
冒泡排序最好的情况下只需进行一趟排序,(n-1)次比较,此时的时间复杂度为O(n),无需移动元素;最坏的情况下进行 n-1 趟排序,时间复杂度为O(n2);冒泡排序是稳定的排序算法。
二、选择排序
原理:将初始序列(A[0]~A[n-1])作为待排序序列,第一趟在待排序序列(A[0]~A[n-1])中找到最小值元素,将其与第一个元素A[0]交换,这样子序列(A[0])已经有序,下一趟在排序在待排序子序列(A[1]~A[n-1])中进行。第i趟排序在待排序子序列(A[i-1]~A[n-1])中找到最小值元素,与该子序列中第一个元素A[i-1]交换。经过 n-1 趟排序后使得初始序列有序。
优点:比较次数与冒泡排序一样,数据移动次数比冒泡排序少;
缺点:相对之下还是慢。
C语言实现:
#include<stdio.h>
void selectSort(int arr[],int n);
void swap(int *a,int *b);
#define N 10
int main(){
int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
selectSort(arr,N);
printf("排序后:");
for(i=0;i<N;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
/*选择排序*/
void selectSort(int arr[],int n){
int i,j;
for(i=0;i<n-1;i++){ //最多进行n-1趟
int m=i;
for(j=i+1;j<n;j++){ //j要取到最后一个元素,即A[n-1]
if(arr[j]<arr[m]) m=j; //m保存值最小的元素的下标
}
if(i!=m) swap(&arr[i],&arr[m]);
}
}
/*两数交换*/
void swap(int *a,int *b){
int temp=*a;
*a=*b;
*b=temp;
}
选择排序的最好、最坏和平均情况的时间复杂度都为O(n2),而且它还需交换元素(n-1)次和移动元素3(n-1)次;它是不稳定的排序算法。
三、插入排序
原理:将初始序列中的第一个元素作为一个有序序列,然后将剩下的 n-1 个元素按关键字大小依次插入该有序序列,每插入一个元素后依然保持该序列有序,经过 n-1 趟排序后使初始序列有序。
优点:稳定,快;
缺点:比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候,但用链表可以解决这个问题。
C语言实现:
#include<stdio.h>
void insertSort(int arr[],int n);
#define N 10
int main(){
int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
insertSort(arr,N);
printf("排序后:");
for(i=0;i<N;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
/*插入排序*/
void insertSort(int arr[],int n){
int i,j,temp,k;
for(i=1;i<n;i++){ //从第2个(下标从0开始)元素开始
temp=arr[i];
j=i-1;
while(temp<arr[j]){ //找插入位置
arr[j+1]=arr[j]; //找到后元素往后移
j--;
if(j==-1) break;
}
arr[j+1]=temp;
}
}
其他说明:插入排序在最好的情况下时间复杂度为O(n),比较次数为(n-1)次,移动元素次数是2(n-1);最坏的情况下时间复杂度为O(n2);插入排序是稳定的排序算法。
四、快速排序
原理:快速排序是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
优点:极快,数据移动少;
缺点:不稳定。
C语言实现:
#include<stdio.h>
void quickSort(int arr[],int left,int right);
#define N 10
int main(){
int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
quickSort(arr,0,N-1);
printf("排序后:");
for(i=0;i<N;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
/*快速排序*/
void quickSort(int arr[],int left,int right){
if(left < right){
int key = arr[left]; // 刚开始以第一个数为标志数据
int low = left;
int high = right;
while(low < high){
while(low < high && arr[high] > key){
high--; //从后面开始找,找到不大于key值的数为止
}
arr[low] = arr[high]; // 将该数放到key值的左边
while(low < high && arr[low] < key){
low++; // 从前面开始找,找到不小于key值的数为止
}
arr[high] = arr[low]; // 将该数放到key值的右边
}
arr[low] = key; // 把key值填充到low位置,下次重新找key值
quickSort(arr,left,low-1); //将key值左边的数递归排序
quickSort(arr,low+1,right); //将key值右边的数递归排序
}
}
其它说明:在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(nlogn),尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlogn)。快速排序是不稳定的排序算法。
五、归并排序
原理:将n个待排序的记录看作由n个长度为1(即由一个记录构成)的子文件组成。每两个相邻的有序子文件分成一组,共有n/2组,对每一组进行排序,这样就可以得到长度为2的n/2个有序子文件。重复这个过程,即可得到长度为n的一个有序文件。
优点:快速、稳定
缺点:对数据的有序性不敏感,如果改造成索引操作,效果就非常好了
C语言实现:
#include<stdio.h>
void mergeSort(int arr[], int left, int right);
void merge(int arr[], int left, int center, int right);
#define N 10
int main(){
int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
mergeSort(arr,0,N-1);
printf("排序后:");
for(i=0;i<N;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
/*归并排序*/
void mergeSort(int arr[], int left, int right){
if (left >= right) return;
// 找出中间索引
int center = (left + right) / 2;
// 对左边数组进行递归
mergeSort(arr, left, center);
// 对右边数组进行递归
mergeSort(arr, center + 1, right);
// 合并
merge(arr, left, center, right);
}
/**
* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
* @param arr 数组对象
* @param left 左数组的第一个元素的索引
* @param center 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
* @param right 右数组最后一个元素的索引
*/
void merge(int arr[], int left, int center, int right){
// 临时数组
int tmpArr[N];
// 右数组第一个元素索引
int mid = center + 1;
// third 记录临时数组的索引
int third = left;
// 缓存左数组第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if (arr[left] <= arr[mid]) {
tmpArr[third++] = arr[left++];
} else {
tmpArr[third++] = arr[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
while (mid <= right) {
tmpArr[third++] = arr[mid++];
}
while (left <= center) {
tmpArr[third++] = arr[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原left-right范围的内容被复制回原数组)
while (tmp <= right) {
arr[tmp] = tmpArr[tmp++];
}
}
其它说明:由于归并排序每趟归并都会使得有序子段的长度增长1倍,即是原有序子段的2倍,所以从长度为1的子段开始,需经过log2n次一趟归并才能产生长度为n的有序段。而每一趟归并至多进行n-1次比较。所以其时间复杂度为O(nlog2n)。最好情况、最坏情况、平均情况下,时间复杂度都是O(nlog2n)。归并排序是稳定的排序算法。归并排序需要和待排序文件容量相同的辅助存储空间。
六、希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是不稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
- 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
原理:已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。发现当n不大时,插入排序的效果很好。首先取一增量d(d<n),将a[1]、a[1+d]、a[1+2d]……列为第一组,a[2]、a[2+d]、a[2+2d]……列为第二组……,a[d]、a[2d]、a[3d]……列为最后一组依此类推,在各组内用插入排序,然后取d'<d,重复上述操作,直到d=1。
优点:快,数据移动少;
缺点:不稳定,d的取值是多少,应取多少个不同的值,都无法确切知道,只能凭经验来取。
C语言实现:
#include<stdio.h>
void shellSort(int arr[],int n);
#define N 10
int main(){
int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
shellSort(arr,N);
printf("排序后:");
for(i=0;i<N;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
/*希尔排序*/
void shellSort(int arr[],int n){
int i, j, gap;
int temp;
for (gap=n/2;gap>0;gap/=2) {// 计算gap大小
for (i=gap;i<n;i++) {// 将数据进行分组
for (j=i-gap;j>=0&&arr[j]>arr[j+gap];j-=gap){//对每组数据进行插入排序
temp=arr[j];
arr[j]=arr[j+gap];
arr[j+gap]=temp;
}
}
}
}
各种排序算法比较: