下一章
1. 排序
下面的代码基本都是使用Comparable 接口,使用这个接口实现了主键的抽象,它给出了实现这个接口数据类型的对象的大小顺序的定义。
但是,不是每次都要使用这个接口,因为数组元素的主键很可能只是每个元素的一小部分
1. 选择排序
-
概念:首先找到数组中最小的元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。其次,在剩下的元素中找到最小的元素,将它和数组的第二个元素交换位置。如此往复,知道将整个数组排序。
-
在这个算法中,最重要的就是交换和比较,,所有算法的时间效率取决于比较的次数
-
对于选择排序,鲜明的特点就是 运行时间和输入无关。
public static void sort(Comparable[] a){ //将a 按升序排列 int N = a.length; for (int i = 0; i< N; i++){ //将a[i] 和 a[i+1,N]中最小的元素进行交换 int min = i; for (int j= i+1; j< N; j++){ if (less(a[j],a[min])){ min = j; } exch(a,i,min); } }
2. 插入排序
- 插入排序就是你将每一张牌插入到已经有序的牌中的适当位置
- 但是在将其余所有元素在插入之前都向右移动一位
- 与上面的选择排序 不同的是, 插入排序所需的时间取决于输入元素中元素的初始顺序
- 所以通过上述的得到:插入排序对应非随机数组的排序,效率会更好
public static void sort(Comparable[] a){
//将a[] 按升序排序
int N = a.length;
for (int i=1; i < N;i++){
//将a[i] 插入到a[i-1],a[i-2],a[i-3] ...之中
for (int j = i; j >0 && less(a[j],a[j-1]);j--){
exch(a,j,j-1);
}
}
}
- 要大幅度提高插入排序的速度 的方法: 只需要在内循环中将较大的元素都向右移动而不总是交换两个元素(这样访问数组的次数就能减少一半)
public static void sort2(Comparable[] a){
//将a[] 按升序排序
int N = a.length;
for (int i=1; i < N;i++){
//将a[i] 插入到a[i-1],a[i-2],a[i-3] ...之中
Comparable temp = a[i];
int j = i;
for (; j>0 && less(temp,a[j-i]);j--){
a[j] = a[j-1];
}
a[j] = temp;
}
}
3. 希尔排序
- 希尔排序是==基于插入排序==的快速的排序算法
- 希尔排序为了加快速度简单的地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序
- 相当于分组排序
public static void sort(Comparable[] a){
int N = a.length;
int h= 1;
while (h < N/3){
h = 3* h + 1;
}
while (h >=3){
for (int i=h;i < N ; i++){
for (int j =i; j>= h && less(a[i],a[j-h]); j -= h){
exch(a,j,j-h);
}
h = h/3;
}
}
}
4. 归并排序
- 归并排序 : 可以先将它分成两半分别排序,然后再将结果归并起来,
- 当然最吸引人的地方是 : 它能够保证将任意长度为N的数组排序所需时间和成正比,
- 主要缺点是: 它所需的额外空间和N成正比
- 归并排序分为自顶向下和自底向上的两种方法,
- 在归并排序中利用了高效算法中的分治思想,,,这是其中最典型的例子
//自顶向下
class Merge {
private static Comparable[] aux;
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 (lo < hi) {
return;
}
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid);
sort(a, mid + 1, hi);
merge1(a, lo, mid, hi);
}
public static void merge1(Comparable[] a, int lo, int mid, int hi) {
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
for (int k = lo; k <= hi; k++) {
if (i < mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (less(aux[j], aux[i])) {
a[k] = aux[i++];
}
}
}
}
//自底向上
class MergeBU {
private static Comparable[] aux;
public static void sort(Comparable[] a){
int N = a.length;
aux = new Comparable[N];
for (int sz = 1; sz < N;sz = sz+sz){
for (int i =0; i < N-sz; i += sz+sz){
merge2(a,i,i+sz-1,Math.min(i+sz+sz-1,N-1));
}
}
}
public static void merge2(Comparable[] a, int lo, int mid, int hi) {
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
for (int k = lo; k <= hi; k++) {
if (i < mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (less(aux[j], aux[i])) {
a[k] = aux[i++];
}
}
}
}
- 上面这个两个方法,,的临界值是 当数组长度为2的幂时,两种归并所用的比较次数和数组访问次数正好相同,只是顺序不同,
- 而且自底向上的排序比较适合链表组织的数据