距离上次的排序算法文章已经过了蛮久了,今天终于有时间来写写高级的排序,如有不当请多指教!
归并排序
快速排序
归并排序
定义
- 要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来
特性
- 归并排序的时间复杂度是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)使用分治法策略。 它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都 要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
步骤
- 首先取a[low]作为切分元素。
- 然后从数组左端开始向右扫描找到一个大于等于它的元素,再从数组右端开始向左扫描找到一个小于等于它的元素,然后交换两个元素位置。
- 当两个指针相遇时,将切分元素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》第四版