排序算法—高级篇

距离上次的排序算法文章已经过了蛮久了,今天终于有时间来写写高级的排序,如有不当请多指教!

  • 归并排序
  • 快速排序

归并排序

定义

  • 要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来

特性

  • 归并排序的时间复杂度是O(N*lgN)
  • 归并排序是稳定的

代码实现

public class MergeSort {

    private static int[] temp;//临时数组
    
    public static void sort(int[] a) {    
        temp=new int[a.length];//避免在递归中频繁新建数组
        sort(a,0,a.length-1);    
  }
    
    private static void sort(int[] a,int low,int hig) {
        
        if (hig<=low)  return;
        int mid=low+(hig-low)/2;
        sort(a, low, mid);//左边排序
        sort(a, mid+1, hig);//右边排序
        merge(a,low,mid,hig);//将两个有序子数组归并
    }
    //合并子数组
    public static void merge(int[] a,int low,int mid,int hig) {
        //i为左序列索引、j为右序列索引
        int i=low,j=mid+1;
        //将a[low..hig]复制到temp[low..hig]
        for (int k = low; k <= hig; k++) {
            temp[k]=a[k];
        }
        
        for (int k = low; k <= hig; k++) {
            if (i>mid)                  a[k]=temp[j++];   //左边用尽则取右边元素
            else if (j>hig)             a[k]=temp[i++];   //右边用尽则取左边元素
            else if (temp[i]

快速排序

简单快速排序

快速排序(Quick Sort)使用分治法策略。 它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都 要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

步骤

  1. 首先取a[low]作为切分元素。
  2. 然后从数组左端开始向右扫描找到一个大于等于它的元素,再从数组右端开始向左扫描找到一个小于等于它的元素,然后交换两个元素位置。
  3. 当两个指针相遇时,将切分元素a[low]和左子数组最右侧元素交换。

快速排序和归并排序正好相反:

  • 归并排序:递归调用发生在处理数组前。
  • 快速排序:递归调用发生在处理数组后。




时间&空间复杂度

平均 最好 最坏 空间 稳定性
O(nlogn) O(nlogn) O(n^2) O(logn) 不稳定

代码实现

public class QuickSort {

    public static void sort(int[] a){    
        shuffle(a);//虚拟方法,打乱数组以加快排序
        sort(a,0,a.length-1);
    }
    private static void sort(int[] a,int low,int hig) {
        
        if (hig<=low)  return;
        int j=partition(a,low,hig);
        sort(a, low, j-1);
        sort(a, j+1, hig);
 }
    private static int partition(int[] a, int low, int hig) {
         
        int i=low,j=hig+1;
        int v=a[low];//切分元素
        while(true){
            while(a[++i]=j)   break; //指针相遇,退出主循环
            //交换i、j的值
            swap(a,i, j);            
        }
        //交换切分元素v和a[j]            
        swap(a, low, j);
        return j;
    }
    private static void swap(int[] a,int i,int j){
        int t;
        t=a[i];
        a[i]=a[j];
        a[j]=t;
    }    
}

优化

  • 对于小数组,快速排序比插入排序慢,因此在排序小数组时应该切换到插入排序

将sort()中的语句

if (hig <= low) return;

替换成

if (hig <= low + M){ InsertSort.sort(a,low,hig); return; }

对于含有大量重复元素的数组,使用三向切分快速排序进行改进

三向切分的快速排序

从左到右遍历数组一次,维护一个指针 lt 使得 a[ low…lt-1 ] 中的元素小于v,一个指针 gt 使得 a[ gt+1…hig ] 中的元素都大于v,一个指针 i 使 a[ lt…i-1 ] 中的元素都等于v,a[ i…gt ] 中的元素都还未确定

我们对a[i]进行三向比较来处理以下情况:

  • a [ i ] 小于 v,将a [ lt ] 和 a [ i ] 交换,将 lt 和 i 加一;
  • a [ i ] 大于 v,将a [ gt ] 和 a [ i ] 交换,将 gt 减一;
  • a [ i ] 等于 v,将 i 加一;

代码实现

public class QuickSort3Way {

    public static void sort(int[] a) {
        sort(a, 0, a.length - 1);
    }

    private static void sort(int[] a, int low, int hig) {
    
        if (hig <= low) return;
        //a[ low..lt-1 ] 中的元素小于v
        //a[ gt+1..hig ] 中的元素都大于v
        //a[ lt..i-1 ] 中的元素都等于v
        int lt = low, i = low + 1, gt = hig;
        int v = a[low];
        
        while (i <= gt) {
            if (a[i] < v) swap(a, lt++, i++);
            else if (a[i] > v) swap(a, i, gt--);
            else i++;
        }
        sort(a, low, lt - 1);
        sort(a, gt + 1, hig);
    }
    //交换数组元素
    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }    
}

参考

《算法4》第四版

你可能感兴趣的:(排序,数据结构和算法)