今天简单介绍一下TimSort中的binary insertion sort(java 实现)
Code Segment
@SuppressWarnings({"fallthrough", "rawtypes", "unchecked"})
private static void binarySort(Object[] a, int lo, int hi, int start) {
assert lo <= start && start <= hi;
if (start == lo)
start++;
for ( ; start < hi; start++) {
Comparable pivot = (Comparable) a[start];
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
if (pivot.compareTo(a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
/*
* The invariants still hold: pivot >= all in [lo, left) and
* pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the
* first slot after them -- that's why this sort is stable.
* Slide elements over to make room for pivot.
*/
int n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch (n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}
二分法插入排序简介
二分法搜索(Binary Search)
要素:数组S是一个已序的数组;
结果:查找元素q在数组S中的位置;
参数:low 为第一个元素的index,high为最后一个元素的index;
要求:low <= high
-概述:
0.比较元素q和S[mid]的大小,其中mid=(low + high) >>> 1;
1.如果元素q等于S[mid],则位置mid就是返回的结果;
2.如果元素q大于S[mid],则元素q可能出现在[low, mid-1]中;
3.如果元素q小于S[mid],则元素q可能出现在[mid+1, high]中;
4.重复上述步骤,当high < low时,说明数组S中不包含元素q。
@SuppressWarnings({"rawtypes", "unchecked"})
public static int search(Object[] a, int lo, int hi, Comparable key) {
assert a != null && hi <= a.length-1;
if(lo > hi)
return -1;//key not found
int mid = (lo + hi) >>> 1;
if(key.compareTo(a[mid]) == 0) {
return mid;
}
if(key.compareTo(a[mid]) < 0) {
return search(a, lo, mid - 1, key);
}
else {
return search(a, mid + 1, hi, key);
}
}
关于更多的知识,请您看看大神Steven S. Skiena的The Algorithm Design Manual(2nd Edition)种4.9 Binary Search and Related Algorithm。
比较一下binarySort中用的的二分法查找
while (left < right) {
int mid = (left + right) >>> 1;
if (pivot.compareTo(a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
0.不同之处在于有无mid-1,这决定了有无low>high(或者left>right)的可能;
1.一个是采用递归查找,一个是采用while循环查找。
2.作用不一样,search方法是查找key元素在数组的位置,而binarySort中的主要是查找pivot元素插入的位置。
Note: operator >>>代表无符号右移,相当于除以2。
插入排序(Insertion Sort)
插入排序的典型例子就是把扑克牌插入正确的位置。
前提:插入之前的数组已经有序;
步骤:
(0):将大于要插入的元素往右移;
(1):将元素插入已经腾出的位置。
@SuppressWarnings({"rawtypes", "unchecked"})
public static void sort(Comparable[] a) {
assert a != null;
int N = a.length;
// i represents index of insertion element;
for(int i = 1; i < N; i++) {
// this loop equals moving larger items one position to the right
for(int j = i; j > 0 && a[j].compareTo(a[j - 1]) < 0; j--) {
Comparable temp = a[j];
a[j] = a[j -1];
a[j - 1] = temp;
}
}
}
有关插入排序的更多内容请看大神Robert Sedgewick和Kevin Wayna编写的Algorithm(4th Edition)中2.1节的Insertion Sort部分。
binarySort中的排序本质就是Insertion Sort
for ( ; start < hi; start++) {
Comparable pivot = (Comparable) a[start];
...
int n = start - left; // The number of elements to move
// this switch block equals moving larger items one position to the right
switch (n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
Note:使用arraycopy正如注释所说,起到优化的结果。
下一节主要讨论TimSort提到的Run的定义和方法countRunAndMakeAscending(T[] , int , int , Comparator< ? super T>)