选择排序
定义:每次迭代过程中,我们都是从剩余元素(remaining entries)中选得最小的元素放在相应的位置上,这里的
不变量(invariant)为
左边已经排好序的部分。
特点:比较次数是 N(N-1)/2 交换次数是 N (each exchange puts an item into its final position, so the number of exchanges is N. Thus, the running time is dominated by the number of compares),数学模型是上三角;每一次寻找最小元素的过程不会为下一趟的寻找提供额外信息,所以对于一个有序的数组,仍然要花和一个随机数组一样长的时间;选择排序中数组访问是和数组尺寸线性相关的,这是其他排序不具有的。
实现:
public
class
SelectionSort {
// This class should not be instantiated.
private
SelectionSort() {
}
// ascending order
public
static
void
sort(Comparable[] a) {
int
N = a.
length
;
for
(
int
i = 0; i < N; i++) {
int
min = i;
for
(
int
j = i + 1; j < N; j++) {
if
(less(a[j], a[min]))
min = j;
}
exch(a, i, min);
assert
isSorted(a, 0, i);
}
assert
isSorted(a);
}
/***********************************************************************
* Helper sorting functions
***********************************************************************/
// is v < w ?
private
static
boolean
less(Comparable v, Comparable w) {
return
(v.compareTo(w) < 0);
}
// exchange a[i] and a[j]
private
static
void
exch(Object[] a,
int
i,
int
j) {
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}
/***********************************************************************
* Check if array is sorted
-
useful for debugging
***********************************************************************/
// is the array a[] sorted?
private
static
boolean
isSorted(Comparable [] a) {
return
isSorted(a, 0, a.
length
- 1);
}
// is the array sorted from a[lo] to a[ hi]
private
static
boolean
isSorted(Comparable [] a,
int
lo,
int
hi) {
for
(
int
i = lo + 1; i <= hi; i++)
if
(less(a[i], a[i - 1]))
return
false
;
return
true
;
}
// print array to standard output
private
static
void
show(Comparable[] a) {
for
(
int
i = 0; i < a.
length
; i++) {
System.
out
.print(a[i] +
" "
);
}
}
public
static
void
main(String[] args) {
Integer[] a = { 10, 45, 0, 8, 7, -34, 20 };
SelectionSort. sort(a);
show(a);
}
}
插入排序
定义:可以联系到扑克牌,每次都是将一个数字插入到一个有序序列的合适位置,为了给其腾出空间,需要后移比他大的元素。
特点:虽然在当前元素(current index)左边都是有序的,但是却不是他们的最终位置;对于一个随机数组,假设每次在插入一个元素时都是移动一半,则#compare=N^2/4,#exchange=
N^2/4
;最坏情况是每次都是插入到第一个位置(和选择排序一样了)是N^2/2,最好情况是已经有序,只需要比较N-1次。
如果数组中倒置的数量小于数组大小的常数倍数,那么称这样的数组是部分有序的,插入排序对于这样的数组很有效。(倒置是一个序列中乱序的键值对的个数,比如AEDC中的倒置有E-D,E-C,D-C,也就是需要变换位置的pair)
实现:
public
class
InsertionSort {
//其他代码同上
// ascending order
public
static
void
sort(Comparable[] a) {
int
N = a.
length
;
for
(
int
i = 0; i < N; i++) {
for
(
int
j = i; j > 0 && less (a[j], a[j - 1]); j--) {
exch(a, j, j - 1);
}
}
}
}
希尔排序
定义:对于大规模乱序数组插入排序很慢,因为它只交换相邻的元素,所以元素只能一步步的从这一端移动到另一端;希尔排序在插入排序的基础上做了改进,先对几个交错的序列进行局部排序(比如步长是h的子数组)这样加快了元素交换的速度,没一次的h-sort都会使得数组越来越有序,然后逐渐改变增长序列,直到最终得到有序数组。希尔排序在数组的尺寸和部分有序直接做了权衡,部分有序的程度取决于增长序列。
特点:
至今没有发现一个最优的增量序列,使用递增序列(3x+1) 1, 4, 13, 40, 121, 364…的希尔排序所需的比较次数不会超出 N 的若干倍
乘以递增序列的长度。
在这个增量序列下,shellsort最多比较次数是N^(3/2).
实现:
public
class
ShellSort {
// This class should not be instantiated.
private
ShellSort() {
}
// ascending order
public
static
void
sort(Comparable[] a) {
int
N = a.
length
;
int
h = 1;
// 找到最大的步长
while
(h < N / 3)
h = 3 * h + 1;
// 1,4,13,40
while
(h >= 1) {
// 用不同的增量来对子数组插入排序
for
(
int
i = h ; i < N; i += h) {
for
(
int
j = i; j > 0 && less (a[j], a[j - h]); j -= h ) {
exch(a, j, j - h );
}
}
h /= 3;
}
}
}
Shuffle Sort
这里是利用排序的场景,洗牌算法
1.为每一张牌随机产生不同的数,而后利用上述的排序算法;
2.
In iteration i, pick integer r between 0 and i uniformly at random. then Swap a[i] and a[r].(这是Knuth发明的)显然是线性复杂度。
实现:
public
class
TestShuffle {
private
static
void
exch(
int
[] a,
int
i,
int
j) {
int
swap = a[i];
a[i] = a[j];
a[j] = swap;
}
public
static
void
shuffle(
int
[] a) {
int
N = a.
length
;
for
(
int
i = 0; i < N; i++) {
int
r = StdRandom.uniform(i + 1);
// 在 1-i之间产生随机数
exch(a, i, r);
}
}
public
static
void
main(String[] args) {
int
[] test = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
shuffle(test);
for
(
int
i = 0; i < test.
length
; i++)
System.
out
.print(test[i] +
" "
);
}
}
归并排序
定义:利用分治思想,将大问题划分为小问题,然后进行合并。
特点:最大的优点是NlgN的复杂度(可以通过公式推导,或画递归树得到)
实现:
public
class
MergeSort {
private
static
Comparable[]
aux
;
// 辅助数组
// This class should not be instantiated.
private
MergeSort() {
}
public
static
void
sort(Comparable[] a) {
aux
=
new
Comparable[a.
length
];
sort(a, 0, a.
length
- 1);
}
// 递归子例程
private
static
void
sort(Comparable[] a,
int
lo,
int
hi) {
if
(hi <= lo)
return
;
int
mid = lo + (hi - lo) / 2;
sort(a, lo, mid);
sort(a, mid + 1, hi);
merge(a, lo, mid, hi);
}
// in-place merge , not to get an output array
public
static
void
merge(Comparable[] a,
int
lo,
int
mid,
int
hi) {
int
i = lo, j = mid + 1, k;
for
(k = lo; k <= hi; k++)
aux
[k] = a[k];
// merge back to the original array a[lo.....hi]
for
(k = lo; k <= hi; k++) {
if
(i > mid)
// left is over
a[k] =
aux
[j++];
else
if
(j > hi)
// right is over
a[k] =
aux
[i++];
else
if
(less(
aux
[i], a[j]))
a[k] =
aux
[i++];
else
a[k] = a[j++];
}
}
}