排序算法之初级排序

该系列是我看人民邮电出版社的《算法》这本书的一些理解,不写下来感觉很快就忘掉,代码来自原书。

 

初级排序主要包括简单的选择排序和插入排序,以及插入排序的一个优化:希尔排序。

 

这里所说的排序,都是基于在原数组上的操作,比较和交换,不是新建一个数组来保存排序的结果。

 

一:选择排序

总的思想非常简单,就是在当前序列中,找到最小的元素,然后与首位元素交换位置。然后将无序的序列逐渐缩小。

假如有n个元素。

1、在n个元素中找到最小的元素,与第一个交换位置。

2、在n-1个元素中找到最小的元素,与第二个交换位置。

3、与此类推,直到最后一个元素。

 

public static void sort(int a[]){
		
		int N = a.length;
		
		//每个循环交换一个,既是每个位置只交换一次,共N次
		for(int i=0;i<N;i++){
			
			int min = i;
			
			//将a[i]和a[i+1]到a[n]中最小的元素交换
			//每一次for循环,可以获得i+1到n中最小元素的下标
			for(int j=i+1;j<N;j++){
				
				
				if(a[j]<a[min])
					min=j;
				
			}
			
			//当前序列最小的值,与i交换位置
			Example.exch(a, i, min);
			
			
		}

 

这里寻找最小的元素是通过记录下标的形式,所以整个排序只需要N次的交换即可,是所有排序之中最少的。

 

时间复杂度:平方级

 

稳定性:不稳定

举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

当然,要是你新开一个数组来保存结果,每找到一个当前最小的就放到新数组里面,那肯定是稳定的,所以不要太纠结这个。

 

二:插入排序

 

假定前面都是已经有序的数组,后面是无序的,后面的元素需要一个个插入到有序数组里面合适的位置中去。

当然这里的插入不是平时理解的插入,不然每插入一次,后面的元素都要不断的往后挪,太过复杂,当然你用链表可以这样做。

简便的做法是,当前元素不断交换位置来“插入”到一个合适的位置。

因为前面的序列都是有序的,所以只要一直比较大小,就能找到合适的位置。而更明智的比法是从后面比起(当前排序是增序),为什么呢?从右边起,数组里面的小,就交换位置,然后一直遇到一个不小的值,就可以确定位置了。说明当前位置,你比后一个小,不小于前一个。

如果是从左边比起,不说了太麻烦了。

 

public static void sort(int a[]){
		
		int N = a.length;
		
		for(int i=0;i<N;i++){
			
			//将a[i]插入到a[i-1],a[i-2]....a[0]里面合适的位置
			for(int j=i;j>0&&a[j]<a[j-1];j--)
				Example.exch(a, j, j-1);
			
		}
		
	}

 

时间复杂度:平方级

稳定性:稳定,因为元素都是挨个来比较和交换的,并且交换的条件是小于,所以同样大小的数不会交换到前面去。当然,交换条件是小于等于就不行了。

 

三:希尔排序

 

选择一个增量(间隔)h,将大数组分为 间隔为h的元素组成的 h个数组,然后对这些数组分别进行插入排序

例如:h=5

然后h不断对半减小,直至为1,排序结束,例如5、2、1

 

public static void sort(int a[]){
		
		int N = a.length;
		int h = N/2;//增量,这里选择总元素的一半作为增量
		
		while(h>=1){
			
			//原数组被分为h个数组,每相隔h个元素组成一个数组,对每个数组进行插入排序
			
			//对每个数组的元素轮流遍历,可以避免三层循环
			for(int i=h;i<N;i++)
				for(int j=i;j>=h&&a[j]<a[j-h];j-=h)
					Example.exch(a, j, j-h);
			
			
			h=h/2;
			
			
		}
		
		
		
	}

 

这个代码非常巧妙,一开始说,原数组被分为h个数组,每相隔h个元素组成一个数组,对每个数组进行插入排序

但是完全这样做的话,会变成3个for的嵌套,我们来看看百度百科上的代码:

 

但是我提供的代码是从h开始的,然后对后续的所有元素进行间隔为h的插入排序即可,简洁高效。

 

时间复杂度:大于n,小于n的平方,与增量序列的选择有关,N/2并不是最快的,但是便于理解这里使用N/2,关于什么增量最快,还在研究。

稳定性,不稳定。

 

这是我写给排序算法的辅助类,包括了初始化数组,打印数组,和交换元素

public class Example {
	
	
	//初始化数组
	public static void initArray(int[] a){
		
		int N = a.length;
		
		for(int i=0;i<N;i++)
			a[i] = (int)(Math.random()*10);
			
	}
	
	//打印数组
	public static void print(int[] a){
		
		int N = a.length;
		
		for(int i=0;i<N;i++)
			System.out.print(a[i]);
		
		
		System.out.println();
			
		
	}
	
	
	
	//交换
	public static void exch(int[]a,int i,int j){
		
		int temp = a[i];
		a[i] = a[j];
		a[j] = temp;
		
	}

}

 

所以在直接使用的时候避免了很多的重复代码:

public static void main(String args[]){
		
		//创建数组
		int []a=new int [10];
		
                //初始化,赋值
		Example.initArray(a);

		//排序前
		Example.print(a);

		//调用排序算法
		insertSort.sort(a);

		//排序后
		Example.print(a);
		
		
		
	}

 

posted @ 2016-12-06 18:02  wzb的QQ空间  阅读(329)  评论(0编辑  收藏  举报