几种常见的排序算法总结

*****选择排序*****

方法描述:首先找到第一个最小的数,和第一个数交换;然后从剩下的找出最小的与第二个交换,以此类推。
效率: 长度为N的数组,大约N2/2比较,N次交换
特点: 1.运行时间和输入无关,有序数组,全部相等的数组,随机数组所用时间一样,没有充分利用输入的初始状态。
    2.数据移动最少,与输入成线性关系。
代码

sort(int[] a){
  int n = a.length;
  for(int i = 0; i < n; i++){
    int min = i;
     for(int j = i; j < n; j++){
      if(a[j] < a[i])
        swap(a[i],a[j]);
     }
  }
 }

*****插入排序*****


背景:打扑克牌
方法描述:后面的数插入到前面已经有序的子数组中
效率: 平均移动一半元素,所以N2/4次比较和交换;最坏N2/2次比较和交换;最好 N-1 次比较和0次交换
特点: 对于部分有序的数组效率非常高,时间是线性级别的。
  几个典型的部分有序数组:数组中每个元素距离它的最终位置不远;一个有序的大数组接一个小数组;数组中只有几个元素的位置不正确;
代码:

sort(int[] a){
  int n = a.length;
  for(int i = 1; i < n; i++){
    for(int j = i-1; j >= 0 && a[j] < a[i]; j--){
      swap(a[i],a[j]);
    }
  }
}

*****希尔排序*****


背景:大规模的插入排序效率很低,因为它仅仅交换相邻的元素。希尔排序的思想就是:对大数组中间隔为h的子数组排序,将数组大约分成
h个间隔为h的子数组,并逐渐减小h,h=1时其实就是插入排序。这样做的好处是,排序之初,只对间隔为h的子数组排序,排序规模小,速度快;
随着h的减小,数组趋于有序,此时插入排序能充分发挥作用。
方法描述:后面的数插入到前面已经有序的子数组中
效率: 平均移动一半元素,所以N2/4次比较和交换;最坏N2/2次比较和交换;最好 N-1 次比较和0次交换
特点: 对于部分有序的数组效率非常高,时间是线性级别的。
代码

sort(int[] a){
  int n = a.length;
  int h = 1;
  while(h < N/3) h = 3*h+1;
  while(h >= 1){
    for(int i = h; i < n; i++){
      for(int j = i; j >= 0 && a[j] < a[i]; j -= h){
        swap(a[j],a[i]);
      }
    }
    h = h/3;
  }  
}

*****归并排序*****


方法描述:归并思想
效率: NlogN
(1)原地归并代码:

public static void merge(int[] a, int lo, int mid, int hi){
  int i = lo, j = mid+1;
  int[] aux = new int[a.length];
  for(int k = lo; k <= hi; k++){
    aux[k] = a[k];
  }
  for(int k = lo; k <= hi; k++){
    if(i > mid) a[k] = aux[j++];//异常情况一定要放前面,不然可能导致数组越界,报空指针异常
    else if(j > hi) a[k] = aux[i++];
    else if(a[i] < a[j]) a[k] = aux[i++];
    else a[k] = aux[j++];
  }
}
排序:

sort(int[] a, int lo, int hi){
  if(hi < lo) return;
  int mid = lo + (hi -lo)/2;
  sort(a,lo,mid);
  sort(a,mid+1,hi);
  merge(a,lo,mid,hi);
}

改进:1.小规模用插入排序
   2. 测试数组是否有序, a[mid] < a[mid+1] 就认为有序,就跳过merge方法,这样能使得有序的
    子数组时间降为线性。

(2)自底向上归并排序:
方法:先两两归并,再44归并,再88,。。。。。
代码
sort(int[] a){
  int n = a.length;
  aux = new int[n];
  for(int i = 1; i < n; i += i+i){
    for(int j = 0; j< n-i; j+= i+i){
      merge(a,j,j+i-1,Math.min(j+i+i-1,n-1));
    }
  }
}
特点:适合链表组织的数据,只需要重新组织链表链接就能将链表原地排序。

*****快速排序*****


特点:原地排序; nlogn; 内循环小,比较次数少,而且是和固定值进行比较,所以非常快. 例如归并,插入排序,都会在内循环中移动元素,所以慢
   和归并排序相比:归并是递归在前,处理数组在后;快排相反
代码:
public static void sort(int[] a, int lo,int hi){
  if(hi < lo) return;
  int j = partition(a,lo,hi);
  sort(a,lo,j-1);
  sort(a,j+1,hi);
}
static partition(int[] a, int lo, int hi){
  int i = lo, j = hi+1;
  int guard = a[lo];
  while(true){
    while(a[++i] < guard) if(i == hi) break;
    while(a[--j] > guard) if(j == lo) break;
    if(i >= j) break;
    swap(a[i],a[j]);
  }
  swap(a[lo],a[j]);
  return j;
}
改进:基于插入排序对于小数组更加高效这一特点。
1. if(hi <= lo) return -> if(hi <= lo+M) {Insertion.sort(a,lo,hi); return;}

posted on 2016-07-30 19:04  占海涛  阅读(760)  评论(0编辑  收藏  举报

导航