1.选择排序
选择排序的基本原理是:每次找出最小的元素放到前面,具体的:
对于一组给定的记录a1,a2,a3.......an;
第一次循环:从a2开始到an,依次和a1进行比较,找到小于a1当中最小的一个,与a1进行交换,否则不交换;
第二次循环:从a3开始到an,依次和a2进行比较,找到小于a2当中最小的一个,与a2进行交换,否则不交换;
......
一直到循环结束。
egg{2 4 6 7 1 3 8 9}
第一次1 2 [6 7 4 3 8 9]
第二次1 2 3 [7 4 6 8 9]
第三次1 2 3 4 [7 6 8 9]
第四次1 2 3 4 6 [7 8 9]
第五次1 2 3 4 6 7 [8 9]
第六次1 2 3 4 6 7 8 [9]
第七次1 2 3 4 6 7 8 9
代码如下:
#include <iostream> using namespace std; void Swap(int &a,int &b) { int temp=a; a=b; b=temp; } //选择排序算法 时间复杂度为o(n*n) void SelectSort(int a[], int length) { int i=0;//外层循环 从0到length-2 int j=0;//内层循环 从i+1到length-1 int MinPos=0;//每次比较的最小元素位置 for (i=0;i<length-1;i++)//从第一个到倒数第二个元素 { MinPos=i; for (j=i+1;j<length;j++) { if (a[j]<a[MinPos]) { MinPos=j; } } if(MinPos!=i)//找到了比a[i]更小的元素 交换 { Swap(a[i],a[MinPos]); } } }
2.插入排序
插入排序算法原理:假设前k个元素是有序排列的,新插入元素依次和前k个元素比较,插入到合适的位置,具体地:
给出一组记录,a1,a2,a3…….an;
第一次循环:假定a1有序,插入a2,如果a2小于该元素,则交换a1,a2,否则不交换,此时a1 a2有序;
第二次循环:插入a3,如果a3小于a2,交换a3,a2,再将a2与a1比较,如果小则换,此时a1 a2 a3有序;
......
第n-1次循环:插入a[n],依次和前面所有元素比较,交换。
egg:{4,2,6,7,1,3,8,9}
第一次[2 4]6 7 1 3 8 9
第二次[2 4 6]7 1 3 8 9
第三次[2 4 6 7]1 3 8 9
第四次[1 2 4 6 7]3 8 9
第五次[1 2 3 4 6 7]8 9
第六次[1 2 3 4 6 7 8]9
第七次[1 2 3 4 6 7 8 9]
void InsertSort(int a[],int length) { int i=0; int j=0; for (i=0;i<length-1;i++) { for (j=i+1;j>=1;j--) { if (a[j]<a[j-1]) { Swap(a[j],a[j-1]); } } } }
3.冒泡排序:
冒泡排序算法原理:从第一个记录开始依次对相邻的两个元素进行比较,如果前面的元素大于后面的元素就交换,这样,第一轮下来,最大的数已经排到了末尾,然后再对前n-1个元素进行如上交换。
egg:{4 2 6 7 1 3 9 8}
第一次:[2 4 6 1 3 7 8]9
第二次:[2 4 1 3 6 7]8 9
第三次:[2 1 3 4 6]7 8 9
第四次:[1 2 3 4]6 7 8 9
第五次:[1 2 3]4 6 7 8 9
第五次:[1 2]3 4 6 7 8 9
第六次:[1]2 3 4 6 7 8 9
第七次: 1 2 3 4 6 7 8 9
void BubbleSort( int a[] , int length ) { int i,j; for (i = 0; i < length-1; i++) { for (j = 1; j < length - i; j++) { if (a[j -1]> a[j]) { Swap(a[j],a[j-1]); } } } }
4.希尔排序
算法原理:将无序记录分为若干个子列,子列是间隔抽取的,间隔个数为增量,对各个子列进行插入排序;然后选一个更小的增量,重新划分子列,再次进行插入排序......;最后选择增量为1,也就是整个序列作为一个序列,进行插入排序,是最终记录成为有序。
增量选择,首选Incre=length/2,每次缩小一倍,直到Incre为1;
egg:{4,2,6,7,1,3,8,9}
Incre=8/2=4
第一个子列4,1 排序后 1,4
第二个子列2,3 排序后 2,3
第三个子列6,8 排序后 6,8
第四个子列7,9 排序后 7,9
第一次排序完后
1 2 6 7 4 3 8 9
增量选为4/2=2
第一个子列1 6 4 8 排序后 1 4 6 8
第二个子列2 7 3 9 排序后 2 3 7 9
第二次排序完后
1 2 4 3 6 7 8 9
增量选为1
第三次排序完后
1 2 3 4 6 7 8 9
代码如下:
void ShellSort(int a[], int length) { int i=0; int j=0; int temp=0; for (int Incre = length / 2; Incre > 0; Incre /= 2) for (i = Incre; i < length; ++i) { temp = a[i]; for(j = i -Incre; j >= 0 ; j -=Incre) { if (temp<a[j]) { a[j+Incre] = a[j]; } else break; } a[j+Incre] = temp; } }
5.快速排序:
快速排序是一个就地排序,分而治之,大规模递归的算法。从本质上来说,它是归并排序的就地版本。快速排序可以由下面四步组成。
(1) 如果不多于1个数据,直接返回。
(2) 一般选择序列最左边的值作为支点数据。
(3) 将序列分成2部分,一部分都大于支点数据,另外一部分都小于支点数据。
(4) 对两边利用递归排序数列。
快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,但是就通常情况而言,没有比它更快的了。快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。
初始化关键字: [49 38 65 97 76 13 27 49]
第一趟排序之后[27 38 13 ]49 [76 97 65 49]
第二趟排序之后[13] 27 [38] 49 [49 65] 76 [97]
第三趟排序之后13 27 38 49 49 [65] 76 97
最后结果: 13 27 38 49 49 65 76 97
void QuickSort(int array[],int low ,int high)//调用的时候 high的位置要填length-1 { int i; int j; int idex; if (low>=high) { return; } i=low; j=high; idex=array[i]; while (i<j)//执行完后 一趟排序 第一个元素将所有元素分成左右两等分 { while (i<j&&array[j]>=idex)//从右向左寻找第一个小于idex的数 { j--; } if (i<j)//如果找到了 交换两个数 并且i右移一下 { array[i]=array[j]; i++; } while (i<j&&array[i]<idex)//从左向右寻找第一个大于idex的数 { i++; } if (i<j) { array[j]=array[i]; j--; } } array[i]=idex;//中间划分位置元素 QuickSort(array,low,i-1);//左半部分递归 QuickSort(array,i+1,high);//右半部分递归 }
6.归并排序:
归并排序先分解要排序的序列,从1分成2,2分成4,依次分解,当分解到只有1个一组的时候,就可以排序这些分组,然后依次合并回原来的序列中,这样就可以排序所有数据。合并排序比堆排序稍微快一点,但是需要比堆排序多一倍的内存空间,因为它需要一个额外的数组。
初始关键字: [49] [38] [65] [97] [76] [13] [27]
一趟归并后:[38 49] [65 97] [13 76] [27]
两趟归并后:[38 49 65 97] [13 27 76]
三趟归并后:[13 27 38 49 65 76 97
void copyArray(int source[], int dest[],int len,int first) { int i; int j=first; for(i=0;i<len;i++) { dest[j] = source[i]; j++; } } void merge(int a[],int left,int right) { int begin1 = left; int mid = (left+right)/2 ; int begin2 = mid+1; int k=0; int newArrayLen = right-left+1; int *b = (int*)malloc(newArrayLen*sizeof(int)); while(begin1<=mid && begin2<=right) { if(a[begin1]<=a[begin2]) b[k++] = a[begin1++]; else b[k++] = a[begin2++]; } while(begin1<=mid) b[k++] = a[begin1++]; while(begin2<=right) b[k++] = a[begin2++]; copyArray(b,a,newArrayLen,left); free(b); } void mergeSort(int a[],int left,int right) { int mid;// 保证至少有两个元素 if(left < right) { mid = (left+right)/2; mergeSort(a,left,mid ); mergeSort(a,mid +1,right); merge(a,left,right); } }
7.堆排序http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html
堆排序适合于数据量非常大的场合(百万数据)。
堆排序不需要大量的递归或者多维的暂存数组。这对于数据量非常巨大的序列是合适的。比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法,在数据量非常大的时候,可能会发生堆栈溢出错误。
堆排序会将所有的数据建成一个堆,最大的数据在堆顶,然后将堆顶数据和序列的最后一个数据交换。接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。
void AdjustMaxHeap(int a[],int pos ,int len) { int temp; int child; for (temp=a[pos];2*pos+1<=len;pos=child) { child=2*pos+1;//根节点的左孩子 if (child<len&&a[child]<a[child+1])//如果左孩子小于右孩子 将右孩子的值作为交换 { child++; } if (a[child]>temp)//如果值大的孩子大于根节点,把该孩子节点值赋给父节点 { a[pos]=a[child]; } else //如果两个孩子都不大于根节点的值 不交换 break; } a[pos]=temp;//将父节点的值赋给子节点 } void HeapSort(int a[],int len) { int i; for (i=len/2-1;i>=0;i--)//建立堆 { AdjustMaxHeap(a,i,len-1); } for (i=len-1;i>=0;i--)//堆排序 { Swap(a[0],a[i]); AdjustMaxHeap(a,0,i-1); } }
排序方法 |
最好时间 |
平均时间 |
最坏时间 |
辅助存储 |
稳定性 |
备注 |
选择排序 |
O(n2) |
O(n2) |
O(n2) |
O(1) |
不稳定 |
n小好 |
插入排序 |
O(n) |
O(n2) |
O(n2) |
O(1) |
稳定 |
大部分有序好 |
冒泡排序 |
O(n) |
O(n2) |
O(n2) |
O(1) |
稳定 |
n小好 |
希尔排序 |
O(n) |
O(nlogn) |
O(ns)1<s<2 |
O(1) |
不稳定 |
S是所选分组 |
快速排序 |
O(nlogn) |
O(nlogn) |
O(n2) |
O(logn) |
不稳定 |
n大好 |
堆排序 |
O(nlogn) |
O(nlogn) |
O(nlogn) |
O(1) |
不稳定 |
n大好 |
归并排序 |
O(nlogn) |
O(nlogn) |
O(nlogn) |
O(n) |
稳定 |
n大好 |