1.基本類型(以int為例)
源碼中的快速排序,主要做了以下幾個方面的優化:
1)當待排序的數組中的元素個數較少時,源碼中的閥值為7,采用的是插入排序。盡管插入排序的時間復雜度為0(n^2),但是當數組元素較少時,插入排序優於快速排序,因為這時快速排序的遞歸操作影響性能。
2)較好的選擇了划分元(基准元素)。能夠將數組分成大致兩個相等的部分,避免出現最壞的情況。例如當數組有序的的情況下,選擇第一個元素作為划分元,將使得算法的時間復雜度達到O(n^2).
3)根據划分元 v ,形成不變式 v* (
源碼中選擇划分元的方法:
1)當數組大小為 size=7 時 ,取數組中間元素作為划分元。int n=m>>1;(此方法值得借鑒)。
2)當數組大小size大於7小於等於40時,取首、中、末三個元素中間大小的元素作為划分元。
3)當數組大小 size>40 時 ,從待排數組中較均勻的選擇9個元素,選出一個偽中數做為划分元。
普通的快速排序算法,經過一次划分后,將划分元排到素組較中間的位置,左邊的元素小於划分元,右邊的元素大於划分元,而沒有將與划分元相等的元素放在其附近,這一點,在Arrays.sort()中得到了較大的優化。
舉例:15、93、15、41、6、15、22、7、15、20
舉例:15、93、15、41、6、15、22、7、15、20
因size大於7小於等於40 ,所以在15、6、和20 中選擇v = 15 作為划分元。
經過一次換分后: 15、15、7、6、41、20、22、93、15、15. 與划分元相等的元素都移到了素組的兩邊。
接下來將與划分元相等的元素移到數組中間來,形成:7、6、15、15、15、15、41、20、22、93.
最后遞歸對兩個區間進行排序[7、6]和[41、20、22、93].,所以在15、6、和20 中選擇v = 15 作為划分元。
2.Object類型
優化的歸並排序既快速(nlog(n))又穩定。
對於對象的排序,穩定性很重要。比如成績單,一開始可能是按人員的學號順序排好了的,現在讓我們用成績排,那么你應該保證,本來張三在李四前面,即使他們成績相同,張三不能跑到李四的后面去。
而快速排序是不穩定的,而且最壞情況下的時間復雜度是O(n^2)。
另外,對象數組中保存的只是對象的引用,這樣多次移位並不會造成額外的開銷,但是,對象數組對比較次數一般比較敏感,有可能對象的比較比單純數的比較開銷大很多。歸並排序在這方面比快速排序做得更好,這也是選擇它作為對象排序的一個重要原因之一。
排序優化:實現中快排和歸並都采用遞歸方式,而在遞歸的底層,也就是待排序的數組長度小於7時,直接使用冒泡排序,而不再遞歸下去。
分析:長度為6的數組冒泡排序總比較次數最多也就1+2+3+4+5+6=21次,最好情況下只有6次比較。而快排或歸並涉及到遞歸調用等的開銷,其時間效率在n較小時劣勢就凸顯了,因此這里采用了冒泡排序,這也是對快速排序極重要的優化。
快排部分代碼如下:
package com.util;
public class ArraysPrimitive {
private ArraysPrimitive() {}
/**
* 對指定的 int 型數組按數字升序進行排序。
*/
public static void sort(int[] a) {
sort1(a, 0, a.length);
}
/**
* 對指定 int 型數組的指定范圍按數字升序進行排序。
*/
public static void sort(int[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
sort1(a, fromIndex, toIndex - fromIndex);
}
private static void sort1(int x[], int off, int len) {
/*
* 當待排序的數組中的元素個數小於 7 時,采用插入排序 。
*
* 盡管插入排序的時間復雜度為O(n^2),但是當數組元素較少時, 插入排序優於快速排序,因為這時快速排序的遞歸操作影響性能。
*/
if (len < 7) {
for (int i = off; i < len + off; i++)
for (int j = i; j > off && x[j - 1] > x[j]; j--)
swap(x, j, j - 1);
return;
}
/*
* 當待排序的數組中的元素個數大於 或等於7 時,采用快速排序 。
*
* Choose a partition element, v
* 選取一個划分元,V
*
* 較好的選擇了划分元(基准元素)。能夠將數組分成大致兩個相等的部分,避免出現最壞的情況。例如當數組有序的的情況下,
* 選擇第一個元素作為划分元,將使得算法的時間復雜度達到O(n^2).
*/
// 當數組大小為size=7時 ,取數組中間元素作為划分元。
int m = off + (len >> 1);
// 當數組大小 7<size<=40時,取首、中、末 三個元素中間大小的元素作為划分元。
if (len > 7) {
int l = off;
int n = off + len - 1;
/*
* 當數組大小 size>40 時 ,從待排數組中較均勻的選擇9個元素,
* 選出一個偽中數做為划分元。
*/
if (len > 40) {
int s = len / 8;
l = med3(x, l, l + s, l + 2 * s);
m = med3(x, m - s, m, m + s);
n = med3(x, n - 2 * s, n - s, n);
}
// 取出中間大小的元素的位置。
m = med3(x, l, m, n); // Mid-size, med of 3
}
//得到划分元V
int v = x[m];
// Establish Invariant: v* (<v)* (>v)* v*
int a = off, b = a, c = off + len - 1, d = c;
while (true) {
while (b <= c && x[b] <= v) {
if (x[b] == v)
swap(x, a++, b);
b++;
}
while (c >= b && x[c] >= v) {
if (x[c] == v)
swap(x, c, d--);
c--;
}
if (b > c)
break;
swap(x, b++, c--);
}
// Swap partition elements back to middle
int s, n = off + len;
s = Math.min(a - off, b - a);
vecswap(x, off, b - s, s);
s = Math.min(d - c, n - d - 1);
vecswap(x, b, n - s, s);
// Recursively sort non-partition-elements
if ((s = b - a) > 1)
sort1(x, off, s);
if ((s = d - c) > 1)
sort1(x, n - s, s);
}
/**
* Swaps x[a] with x[b].
*/
private static void swap(int x[], int a, int b) {
int t = x[a];
x[a] = x[b];
x[b] = t;
}
/**
* Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
*/
private static void vecswap(int x[], int a, int b, int n) {
for (int i=0; i<n; i++, a++, b++)
swap(x, a, b);
}
/**
* Returns the index of the median of the three indexed integers.
*/
private static int med3(int x[], int a, int b, int c) {
return (x[a] < x[b] ? (x[b] < x[c] ? b : x[a] < x[c] ? c : a)
: (x[b] > x[c] ? b : x[a] > x[c] ? c : a));
}
/**
* Check that fromIndex and toIndex are in range, and throw an
* appropriate exception if they aren't.
*/
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex
+ ") > toIndex(" + toIndex + ")");
if (fromIndex < 0)
throw new ArrayIndexOutOfBoundsException(fromIndex);
if (toIndex > arrayLen)
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
歸並部分代碼如下:
package com.util;
import java.lang.reflect.Array;
public class ArraysObject {
private static final int INSERTIONSORT_THRESHOLD = 7;
private ArraysObject() {}
public static void sort(Object[] a) {
//java.lang.Object.clone(),理解深表復制和淺表復制
Object[] aux = (Object[]) a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
public static void sort(Object[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
Object[] aux = copyOfRange(a, fromIndex, toIndex);
mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
}
/**
* Src is the source array that starts at index 0
* Dest is the (possibly larger) array destination with a possible offset
* low is the index in dest to start sorting
* high is the end index in dest to end sorting
* off is the offset to generate corresponding low, high in src
*/
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;
/*
* >>>:無符號右移運算符
* expression1 >>> expresion2:expression1的各個位向右移expression2
* 指定的位數。右移后左邊空出的位數用0來填充。移出右邊的位被丟棄。
* 例如:-14>>>2; 結果為:1073741820
*/
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++];
}
}
/**
* Check that fromIndex and toIndex are in range, and throw an appropriate
* exception if they aren't.
*/
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex
+ ") > toIndex(" + toIndex + ")");
if (fromIndex < 0)
throw new ArrayIndexOutOfBoundsException(fromIndex);
if (toIndex > arrayLen)
throw new ArrayIndexOutOfBoundsException(toIndex);
}
public static <T> T[] copyOfRange(T[] original, int from, int to) {
return copyOfRange(original, from, to, (Class<T[]>) original.getClass());
}
public static <T, U> T[] copyOfRange(U[] original, int from, int to,
Class<? extends T[]> newType) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
T[] copy = ((Object) newType == (Object) Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
/**
* 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
輔助理解
package com.lang;
public final class System {
//System 類不能被實例化。
private System() {}
//在 System 類提供的設施中,有標准輸入、標准輸出和錯誤輸出流;對外部定義的屬性
//和環境變量的訪問;加載文件和庫的方法;還有快速復制數組的一部分的實用方法。
/**
* src and dest都必須是同類型或者可以進行轉換類型的數組.
* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
*/
public static native void arraycopy(Object src, int srcPos, Object dest,
int destPos, int length);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.lang.reflect;
public final class Array {
private Array() {}
//創建一個具有指定的組件類型和維度的新數組。
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
private static native Object newArray(Class componentType, int length)
throws NegativeArraySizeException;
}