今天去面试,让写一下归并排序,好久没有写排序了,有点忘了。
归并排序采用分治策略,时间复杂度为O(NlogN) 空间复杂度O(N)
给定一个数组,从中间值划分为两部分,假定这两部分是有序的,则执行一个合并过程,将两个有序的数组合并成一个数组。以此类推,划分为的两个子数组是上述过程的子问题,因此可以递归解决。
递归的最后一层为子数组的元素个数只有一个,即有序。然后合并两个单元素数组组成有序的双元素数组,接着继续依次合并。
lo------------ q q+1 -----------hi
假设子数组1 lo--q是有序的 子数组2 q+1到hi是有序的,进行合并操作:
指针i指向lo,变化范围lo---q 当i>q时,说明子数组1已经遍历完毕
指针j指向q+1,变化范围 q+1----hi 当i>hi时,说明子数组2已经遍历完毕
申请额外的空间,将要合并的两个子数组先复制到额外空间中,然后双指针指向两个子数组头,依次递归比较,将较小者放入原先组中位置,若某一个子数组已经全部遍历完毕,则将另一个依次放入即可。
private static int[] num;
public static void mergeSort(int[] arr){
num = new int[arr.length];
mergeSort(arr, 0, arr.length-1);
}
private static void mergeSort(int[] arr, int lo, int hi){
//当子数组中只有一个元素时,直接返回
if(lo>=hi) return;
//取中点值作为划分点,进行左右部分递归调用
int q = (lo + hi) >> 1;
mergeSort(arr, lo, q);
mergeSort(arr, q+1, hi);
merge(arr, lo, q, hi);
}
private static void merge(int[] arr, int lo, int q, int hi) {
for(int k=lo; k<=hi; k++){
num[k] = arr[k];
}
int i = lo;
int j = q + 1;
for(int k=lo; k<=hi; k++){
//子数组1已经全部归并
if(i>q) arr[k] = num[j++];
//子数组2已经全部归并
else if(j>hi) arr[k] = num[i++];
//两个子数组均有数据时,取较小者
else if(num[i]<num[j]) arr[k] = num[i++];
else arr[k] = num[j++];
}
}
归并排序没有频繁的比较操作,因此比较适用于复杂对象的排序。有些排序算法要进行大量的比较运算,如果待排序对象的比较成本较高的话可以采用归并排序。
JDK中Arrays.sort排序方式:
当待排序元素为基本类型: 快速排序
public static void sort(double[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
/**
* Sorts the specified range of the array using the given
* workspace array slice if possible for merging
*
* @param a the array to be sorted
* @param left the index of the first element, inclusive, to be sorted
* @param right the index of the last element, inclusive, to be sorted
* @param work a workspace array (slice)
* @param workBase origin of usable space in work array
* @param workLen usable size of work array
*/
static void sort(double[] a, int left, int right,
double[] work, int workBase, int workLen) {
/*
* Phase 1: Move NaNs to the end of the array.
*/
while (left <= right && Double.isNaN(a[right])) {
--right;
}
for (int k = right; --k >= left; ) {
double ak = a[k];
if (ak != ak) { // a[k] is NaN
a[k] = a[right];
a[right] = ak;
--right;
}
}
/*
* Phase 2: Sort everything except NaNs (which are already in place).
*/
doSort(a, left, right, work, workBase, workLen);
/*
* Phase 3: Place negative zeros before positive zeros.
*/
int hi = right;
/*
* Find the first zero, or first positive, or last negative element.
*/
while (left < hi) {
int middle = (left + hi) >>> 1;
double middleValue = a[middle];
if (middleValue < 0.0d) {
left = middle + 1;
} else {
hi = middle;
}
}
/*
* Skip the last negative value (if any) or all leading negative zeros.
*/
while (left <= right && Double.doubleToRawLongBits(a[left]) < 0) {
++left;
}
/*
* Move negative zeros to the beginning of the sub-range.
*
* Partitioning:
*
* +----------------------------------------------------+
* | < 0.0 | -0.0 | 0.0 | ? ( >= 0.0 ) |
* +----------------------------------------------------+
* ^ ^ ^
* | | |
* left p k
*
* Invariants:
*
* all in (*, left) < 0.0
* all in [left, p) == -0.0
* all in [p, k) == 0.0
* all in [k, right] >= 0.0
*
* Pointer k is the first index of ?-part.
*/
for (int k = left, p = left - 1; ++k <= right; ) {
double ak = a[k];
if (ak != 0.0d) {
break;
}
if (Double.doubleToRawLongBits(ak) < 0) { // ak is -0.0d
a[k] = 0.0d;
a[++p] = -0.0d;
}
}
}
归并排序:
public static void sort(Object[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);
else
ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);
}
private static void legacyMergeSort(Object[] a,
int fromIndex, int toIndex) {
Object[] aux = copyOfRange(a, fromIndex, toIndex);
mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
}
private static void mergeSort(Object[] src,
Object[] dest,
int low,
int high,
int off) {
int length = high - low;
// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
for (int i=low; i<high; i++)
for (int j=i; j>low &&
((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
swap(dest, j, j-1);
return;
}
// Recursively sort halves of dest into src
int destLow = low;
int destHigh = high;
low += off;
high += off;
int mid = (low + high) >>> 1;
mergeSort(dest, src, low, mid, -off);
mergeSort(dest, src, mid, high, -off);
// If list is already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
System.arraycopy(src, low, dest, destLow, length);
return;
}
// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
dest[i] = src[p++];
else
dest[i] = src[q++];
}
}
/**
* Swaps x[a] with x[b].
*/
private static void swap(Object[] x, int a, int b) {
Object t = x[a];
x[a] = x[b];
x[b] = t;
}
新版JDK中Arrays.sort排序方式感觉变化好大,并不是单纯的某一种单一排序方式,有时间把TimSort这个类研究一下。
576

被折叠的 条评论
为什么被折叠?



