排序算法小结

常用的排序算法:
稳定的排序     时间复杂度               空间复杂度
冒泡排序      最差,平均O(n^2);最好O(n)    1
插入排序      最差,平均O(n^2);最好O(n)    1
归并排序      最差,平均O,最好O(nlogn)     O(n)
桶排序       O(n)                   O(k)
基数排序      O(dn)                O(n)
二叉树排序     O(nlogn)             O(n)
图书馆排序      O(nlogn)            (1+k)n

不稳定的排序   时间复杂度               空间复杂度
选择排序      最差平均O(n^2)            1
希尔排序      O(nlogn)              1
堆排序       最差,平均O,最好O(nlogn)      1
快速排序      平均O(nlogn),最坏O(n^2)     O(logn)


待排序序列 A[1..n]
1.选择排序:不稳定,时间复杂度 O(n^2) 选择排序的基本思想是对待排序的记录序列进行n-1遍的处理,第i遍处理是将L[i..n]中最小者与L[i]交换位置。这样,经过i遍处理之后,前i个记录的位置已经是正确的了。ps:选择排序不稳定的原因:对所有序列都是稳定的才算稳定排序。
举个例子,序列5 8 5 2 9,从小到大排序,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
for(int i = 0; i < n-1; i ++)
{
min = i;
for(int j = i+1; i < n; j ++)
{
if(A[j] < A[min])
min = j;
}
swap(i, min);
}

2.插入排序:稳定,时间复杂度 O(n^2) 插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较L[i]和L[i-1],如果L[i-1]≤L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1≤j≤i-1),使得L[j] ≤L[j+1]时为止。
for(int i = 1; i < n; i ++)
{
temp = i;
for(int j = i-1; j >= 0; j ++)
{
if(A[temp] > A[j])
break;
else{
swap(temp, j);
temp = j;
}
}
}


3.冒泡排序:稳定,时间复杂度 O(n^2) 冒泡排序方法是最简单的排序方法。这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。在作第二遍处理时,由于最高位置上的元素已是“最轻”元素,所以不必检查。一般地,第i遍处理时,不必检查第i高位置以上的元素,因为经过前面i-1遍的处理,它们已正确地排好序。
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < n-i; j ++)
{
if(A[j] < A[j+1])
swap(j+1, j);
}
}


4.归并排序:稳定,时间复杂度 O(nlog n) 设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。

mergeSort(int* n, int i, int j)

{

`if(i+1<j){

  int mid = (i+j)/2;

  mergeSort(n, i, mid);

  mergeSort(n, mid+1, j);

     merge(n,i,mid,j);

`}

else{

  if(i != j && n[i] > n[j]) swap(n, i, j);

  }

}

5.快速排序:不稳定,时间复杂度 最理想 O(nlogn) 最差时间O(n^2) 快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。

quickSort(int *n, int i, int j){

  if(i < j){   

    int key = v[i];
    int low =i;
    int high = j;
    while(low < high){
      while(low < high && v[high] > key){
        high--;
      }
       v[low] = v[high];
      while(low < high && v[low] < key){
        low++;
      }
      v[high] = v[low];
    }
    v[low] = key;

    quickSort(n, i, low-1);

    quickSort(n, low+1, j);

  }

}

改进快排性能的方法之一就是灵活选择归纳提交,不必到达1为止,可以改为if(i < j-hold){}else{//插入排序},短序列的插入排序比快排更优。

快排的优点是很多数都在和同一个数进行比较,于是可以把这个数放在寄存器里面,节省时间。

6.堆排序:不稳定,时间复杂度 O(nlog n) 堆排序是一种树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。

7.希尔排序:不稳定,时间复杂度 平均时间 O(nlogn) 最差时间O(n^s) 1<s<2 在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点,并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。

8.计数排序(线性排序)
输入A[1..n],输出B[1..n],临时空间C[0..k],输入数组元素范围0~k
COUNTING-SORT(A,B,k)
for i=0 to k
c[i]=0
for j=1 to A.length
c[A[j]] = c[A[j]]+1
for i=1 to k
c[i]=c[i]+c[i-1]
for j=A.length downto 1
B[C[A[j]]] = A[j]
c[A[j]] -= 1

9.桶排序(线性排序)

原理:将元素分类,放在不同桶中,在扫描每个桶,排序所有元素。

桶排序仅适合事先预知元素属性范围的情况。
BUCKET-SORT(A)
n=A.length
for i=0 to n-1
make B[i] be a new list
for i=1 to n
insert A[i] into list B[A[i]/10]
for i=0 to n-1
sort B[i]
concatenate B[0]..B[n-1]

 

posted @ 2014-03-27 22:35  清尘2010  阅读(185)  评论(0)    收藏  举报