归并排序
归并排序是将两个排序的子序列合并,形成一个排序数据序列,又称两路归并排序。属于分治法策略。
算法描述:关键字序列 {97,82,75,53,17,61,70,12,61,58,26} 的归并排序(升序)过程如下图,{}表示子序列。将n个元素的数据序列看成由n个长度为1的排序子序列组成,反复降相邻的两个子序列归并成一个排序子序列,直到合并成一个序列,则排序完成。
归并排序算法分析:
- n个元素归并排序,每趟比较n-1次,数据移动n-1次,进行log2n趟,时间复杂度为O(n*log2n)。
- 归并排序需要O(n)容量的额外空间,与数据序列的存储容量相等,空间复杂度为O(n)。
- 归并排序稳定。
归并排序算法实现:
package book;
/**
* 归并排序:将两个排序的子序列合并,形成一个排序数据序列
* 写的过程:1、先写一个方法用于排序: 对两个已知的排好序的子序列合并到一个序列中
* 2、让方法支持 可选 边界
* 3、进行归并排序,递归调用方法
*/
public class mergeSort {
public static void main(String[] args) {
int[] keys1 = {1,2,4,7,3,5,8};
//merge1(keys1);
int[] keys2 = {1,2,4,7,3,5,8};
//merge2(keys2, 0, 4, keys2.length-1);
int[] keys3 = {97,82,75,53,17,61,70,12,61,58,26};
mergeSort(keys3, 0, keys3.length-1);
print(keys3);
}
/**
* 归并排序
* @param keys 待排序数组
* @param left 数组左边界
* @param right 数组右边界
*/
public static void mergeSort(int[] keys, int left, int right) {
if (left >= right) return;
//分成两半
int mid = left + (right - left)/2;
//左边排序
mergeSort(keys, left , mid);
//右边排序
mergeSort(keys, mid+1, right);
//对两个已排好序的子序列 合并到一个序列中
merge3(keys, left, mid+1, right);
}
/**
* @param keys 待排序数组
* @param leftPtr 第一个序列的开始位置
* @param rightPtr 第二个序列的开始位置
* @param rightBound 数组的右边界
*/
static void merge3(int[] keys, int leftPtr, int rightPtr, int rightBound){
int[] newarr = new int[rightBound - leftPtr + 1];
int mid = rightPtr-1;
int i = leftPtr;
int j = rightPtr;
int k = 0;
while (i <= mid && j <= rightBound) {
newarr[k++] = keys[i] <= keys[j] ? keys[i++] : keys[j++];
}
//将剩下不用比较的,直接插入到newarr中
while (i <= mid) newarr[k++] = keys[i++];
while (j <= rightBound) newarr[k++] = keys[j++];
//将排好顺序的数组复制回原数组
for (int l = 0; l < newarr.length; l++) {
keys[leftPtr + l] = newarr[l];
}
}
//对merge1() 方法进行改造:让其可以选择边界
static void merge2(int[] keys, int leftPtr, int rightPtr, int rightBound){
int[] newarr = new int[rightBound - leftPtr + 1];
int mid = rightPtr-1;
int i = leftPtr;
int j = rightPtr;
int k = 0;
while (i <= mid && j <= rightBound) {
//再优化:三元运算符:
newarr[k++] = keys[i] <= keys[j] ? keys[i++] : keys[j++];
}
while (i <= mid) newarr[k++] = keys[i++];
while (j <= rightBound) newarr[k++] = keys[j++];
print(newarr);
}
//对两个已排好序的子序列合并到一个序列中,例如{1,2,4,7} {3,5,8}
static void merge1(int[] keys){
int[] newarr = new int[keys.length];
int mid = keys.length/2;
int i = 0;
int j = mid+1;
int k = 0;
while (i <= mid && j < keys.length) {
//再优化:三元运算符: newarr[k++] = keys[i] <= keys[j] ? keys[i++] : keys[j++];
if (keys[i] <= keys[j]) {
newarr[k++] = keys[i++];
//等同于下面:
//newarr[k] = keys[i];
//i++;
//k++;
} else {
newarr[k++] = keys[j++];
}
}
while (i <= mid) newarr[k++] = keys[i++];
while (j < keys.length) newarr[k++] = keys[j++];
print(newarr);
}
public static void print(int[] keys) {
for (int key : keys) {
System.out.print(key+" ");
}
}
}
java对象排序
java对象排序要求稳定
对于对象的排序,看源码:
Arrays.java:
/**
* Sorts the specified array of objects according to the order induced by
* the specified comparator. All elements in the array must be
* <i>mutually comparable</i> by the specified comparator (that is,
* {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
* for any elements {@code e1} and {@code e2} in the array).
*
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techniques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* @param <T> the class of the objects to be sorted
* @param a the array to be sorted
* @param c the comparator to determine the order of the array. A
* {@code null} value indicates that the elements'
* {@linkplain Comparable natural ordering} should be used.
* @throws ClassCastException if the array contains elements that are
* not <i>mutually comparable</i> using the specified comparator
* @throws IllegalArgumentException (optional) if the comparator is
* found to violate the {@link Comparator} contract
*/
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
//改进的归并排序(多路归并排序)
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
TimSort.java:
/**
* Sorts the given range, using the given workspace array slice
* for temp storage when possible. This method is designed to be
* invoked from public methods (in class Arrays) after performing
* any necessary array bounds checks and expanding parameters into
* the required forms.
*
* @param a the array to be sorted
* @param lo the index of the first element, inclusive, to be sorted
* @param hi the index of the last element, exclusive, to be sorted
* @param c the comparator to use
* @param work a workspace array (slice)
* @param workBase origin of usable space in work array
* @param workLen usable size of work array
* @since 1.8
*/
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
T[] work, int workBase, int workLen) {
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
/**
* March over the array once, left to right, finding natural runs,
* extending short natural runs to minRun elements, and merging runs
* to maintain stack invariant.
*/
TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi, c);
// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
}
// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse();
// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}