【算法】第二章排序

北城以北 提交于 2020-03-09 08:41:38

##目录

  1. 初级排序
    1. 选择排序
    2. 插入排序
    3. 希尔排序
  2. 归并排序
    1. 自顶向下排序
    2. 自底向上排序
  3. 快速排序
  4. 优先队列

##初级排序 ###一、 选择排序

简述:选择排序就是遍历一遍数组把最小的和第一个数字交换。第二遍遍历数组时候选择和第二个交换,一次类推。

//注意不要在for循环中用a.length()不然每次都要获取a.length();
public voiv sort(Comparable[] a){
	//for(int i=0,i<a.length;i++){
	int N = a.length;
	for(int i=0,i<N;i++){
		int min = i;
		for(int j=i+1;j<N;j++){
			//if(a[min]>a[j]){
			if(less(a[j],a[min])){
				min = j;
			}
			//int tmp = a[i];
			//a[i] = a[min];
			//a[min] = tmp;
			exch(a,j,min);
		}
	}
}

###二、 插入排序
简述:类似于打牌时候边抽牌边整理。第二张开始,就最大的开始对比,然后直到不大时候插入。

public void sort(Comparable[] a){
	int N = a.length;
	//下面是我写的,不知道书上有什么优势。有空测一下。
	//for(int i=1;i<N;i++){
	//	if(less(a[i],a[i-1])){
	///		int pos = i;
	//		while(less(a[pos],a[pos-1])&&pos>0){
	//			exch(a,pos,pos-1);
	//			pos--;
	//		} 
	//	}
	//}
	
	for(int i=1,i<N;i++){
		for(int j=i;j>0&&less(a[j],a[j-1]);j--){
			exch(a,j,j-1);
		}
	}
}

####命题C
插入排序的比较次数 大于等于倒置的数量,小于等于倒置数量加上数组大小再减一。
最坏就是全倒置嘛,这时候每倒置一次就要多比一次,然而数组里每个数都要比较,除了第一个数时候和自身不比较,所以是 最差等于倒置数量+数组大小-1。
所以对 部分有序的数组十分高效,也适合小规模数组。

当不考虑交换和比较成本,两个差不多,但是感觉数组规模小的化插入有优势,规模大的化选择有优势。【平均而言】

###三、希尔排序

public static void sort(Comparable a){
	int N = a.length;
	int h = 1;
	//
	while(h<N3){
		h = h * 3 +1
	}
	while(h>=1){
		for(int i=h;i<N;i++){
			for(int j=i;j<N&&less(a[j],a[j-h]);j-=h){
				exch(a,j,j-h);
			}
			h=h/3;
		}
	}
}

平均每个比较次数增幅为N1/5 大的话才比较明显。
没什么可说的,三方比较,希尔排序一直赛高。可以把希尔排序看作套壳的插入排序。

##归并排序 最基础先是合并两个有序表的方法[书上叫 原地归并抽象方法,不知道为甚么] ``` //谨记读数组时候要用copy数组,改写的时候才用原数组 public static void merge(Comparable[] a,int lo,int mid,int hi){ int N = a.length; //copy for(int k = lo;k <= hi;k++){ auk[k]=a[k]; } int i = lo; int j = mid + 1; for(int k=0;k

public static void sort(Comparable[] a,int lo ,int hi){
int mid = lo + (hi - lo)/2;
if(lo>=hi) return;
sort(a,lo,mid);
sort(a,mid+1,hi);
merge(a,lo,mid,hi);
}

因为插入排序适合小数组,据说是 归并 + 插入 赛高。还没有验证。
####自底向上的归并排序

private static Compararble[] aux;
public static void sort(Comparable[] a){
int N = a.length;
//分割,1 ,2 ,4 ,6 ,8
//边界问题最头疼,首先为什么是N 不是N - 1?
//最重要的一点是 我门用sz表达的是分割的数组数字个数,如果是个数自然是按原值N过来。
for(int sz=1;sz < N ;sz = sz + sz){
//这里为什么是N
//暂时不知道,但是我用-1试过好几次也没差。反正就是为了防止过界又防止余下部分太小
for(int lo = 0;lo < N - sz;lo +=2sz){
merge(a,lo,lo + sz -1 ,Math.min(lo + 2
sz - 1,N - 1));
}
}
}

最后的几个结论不是很懂,只能二刷时候再看了。
<p id = "3_1"></p>
##快速排序
快速排序也是把问题分开再分开,不同的是它并不是平均分成两组,而是产生一个分开因子。笼统而言和归并差不多吧,但是我测试速度感觉比归并快一点。时间能达到1/2甚至更多

public static void sort(Comparable[] a){
int N = a.length;
sort(a,0,N-1);
}

public static void sort(Comparable[] a,int lo,int hi){
if(lo>=hi) return;
int par = partition(a,lo,hi);
sort(a,lo,par - 1);
sort(a,par + 1 ,hi);
}

//core code
public static void partition(Comparable[] a, int lo,int hi){
Comparable v = a[lo];
i = lo;
j = hi+1;
while(true){
//while(less(a[++i],v)) if(i>=hi) break;
while(less(a[++i],v)) if(ihi) break;
//while(less(v,a[–j])) if(j<=lo) break;
//因为a[lo] = v; 所以 当j = lo时候根本进不来,所以if(j
lo)是个多余的条件
while(less(v,a[–j])) {}/if(j==lo) break;/
if(i>=j) break;
exch(a,i,j);
}
exch(a,j,lo);
return j;
}

//quick3way
public static void sort3way(Comparable[] a,int lo,int hi){
if(lo>=hi) return;

int lt = lo;
int gt = hi;
int i = lo + 1;
Comparable v = a[lo];
//其实就是用 i 去遍历每一个数字,小的从 lt 插入 大的 从 gt插入。此为三向切分
while(i<=gt){
	int cmp = a[i].compareTo(v);
	if(cmp<0) exch(a,lt++,i++);
	//换了之后i不用往下走,因为gt已经把一个陌生值换给它了。
	else if(cmp>0) exch(a,i,gt--);
	//和自己相等,下一个
	else i++;
}
//lt-1 都是比v小的
//gt+1 都是比v大的
//剩下的都是等于v的。
sort3way(a,lo,lt-1);
sort3way(a,gt+1,hi);

}

<p id = "4_1"></p>
###优先队列
优先队列是一种思想,只能说堆排序用了这种思想。
大概就是当一个完全二叉树时候,对于一个节点k(从 1 开始的序号),
他的父节点为k/2
左子节点为 2k
右子节点为  2k + 1
下面的节点总比上面的节点大(根据实际用途,自己要求)
我们可以通过遍历某个节点的
当我们添加一个方法的时候 可以先将插入对象添加到队尾 可以使用 swim方法,让其上浮
####由下至上的对有序化(上浮)

//注意我们这里假设数组从 index 1 开始
public void swim(Comparable[] a,int k){
//k为插入元素index
while(k>=1){
if(less(a[k/2],a[k])){
exch(a,k/2,k);
k = k/2;
}
}
//这样直接上浮到适合的位置为止。
}

书上介绍优先队列用法,就是用来快速删除最大(最小【需另设队列】)元素的。

当我们删除最大节点,先使他下沉,使他子元素补位,
由上至下的堆有序化,下沉

//注意我们这里假设数组从 index 1 开始
public void sink(Comparable[] a,int k,int N){
while(k<=N){
int j = 2 * k;
//让哪个儿子重哪个儿子上
if(j<N && less(a[j],a[j+1])) j=j+1;
if(less(a[j],a[k])) break;
exch(a,k,j);
//换做最大元素,下一回合。
k = j;
}
}


####堆排序

public static void sort(Comparable[] a){
//第一步 排成优先队列
int N = a.length;
//元素先按1开始,等到使用数组的时候,再恢复实际元素
for(int k=N/2;k>=1;k–){
sink(a,k,N);
}

while(N>1){
	//最前面肯定使最大的,所以将其和正确位置置换
	exch(a,0,N-1);
	//最先面的已经不是最轻的了,换人。
	sink(a,1,--N);
}

}

public static void sink(Comparable[] a,int k,int N){
while(2*k<=N){
int j = 2 * k;
//使用时候按实际位置所以要-1.包括less 和 exch方法
if(j < N && less(a[j-1],a[j])) j = j + 1;
//如果不比儿子轻,就此结束,下一回合。
if(!less(a[k-1],a[j-1])) break;
exch(a,j-1,k-1);
k = j;
}
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!