Java实现常见的7种排序算法

主要总结一下以下常见的排序以及其java的实现,包含有:

比较排序

冒泡排序

插入排序

希尔排序

快速排序

堆排序

归并排序

 

1.比较排序:从第0个开始分别与后面的比较,正序不交换,反序就交换。时间复杂度n+(n-1)+.....1=(1+n)n/2=O(n^2)。

 

/**比较排序
 * 2015年10月29日上午9:53:38
 * @param data
 */
public static void CompareSort(int[] data)
{
	for(int i=0;i<data.length;i++)
	{
		for(int j=i+1;j<data.length;j++)
		{
			if(data[i]>data[j])
			{
				int change=data[i];
				data[i]=data[j];
				data[j]=change;
						
			}
		}
	}
}

 

2.冒泡排序:最后一个开始 a[n]与a[n-1]比较,正序不交换,反序交换,接着比较a[n-1]与a[n-2] ....遍历一遍可以最小的排到最上面,如此遍历n遍就可以实现排序,从底到上,如冒泡一样。时间复杂度为O(n^2)

 

 

/**
	 * 冒泡排序 2015年10月29日上午9:53:38
	 * 
	 * @param data
	 */
	public static void bubleSort(int[] data) {

		for (int i = 1; i <= data.length; i++) {

			for (int j1 = data.length - 1, j2 = j1 - 1; j2 >= 0; j2--, j1--) {
				if (data[j1] < data[j2]) {
					int change = data[j1];
					data[j1] = data[j2];
					data[j2] = change;
				}
			}
		}

	}


   改进:去掉已经排序了的部分

 

 

/**
	 * 冒泡排序 2015年10月29日上午9:53:38
	 * 
	 * @param data
	 */
	public static void bubleSort(int[] data) {
		for (int i = 0; i <data.length; i++) {
			for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) {				
				if (data[j1] < data[j2]) {
					int change = data[j1];
					data[j1] = data[j2];
					data[j2] = change;
				}
			}
		}

	}


     进一步改进:假如已经没有可以比较的了,退出循环

 

 

	/**
	 * 冒泡排序 2015年10月29日上午9:53:38
	 * 
	 * @param data
	 */
	public static void bubleSort(int[] data) {
        Boolean noFinishCompare=true;
		for (int i = 0; i <data.length&&noFinishCompare; i++) {
                noFinishCompare=false;
			for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) {				
				if (data[j1] < data[j2]) {
					int change = data[j1];
					data[j1] = data[j2];
					data[j2] = change;
					noFinishCompare=true;//只要有交换就没有完成比较
				}
			}
		}

	}

 

 

 

3.插入排序:将要排序的数组分成两个部分,一部分是已经排序好了,一部分是原来的,一开始默认已经排序好的只有第一个数组下标。接着从第二个数组开始遍历,判断要排序的元素在已经排序的部分的位置,然后插入。比如判断第二个数组比第一个数组小,插入在左边,比它大插入在右边,确定插入位置的依据是比上一个位置小,不大于下一个位置。时间复杂度为O(n^2);

 

 

/**插入排序
	 * 2015年10月29日上午11:09:33
	 * @param data
	 */
	public static void inSertSort(int[] data )
	{
		for(int i=1;i<data.length;i++)
		{
			if(data[i]<data[0])
			{
				int change=data[i];//拿出data[i]		
				
				for(int s=i;s>=1;s--)//从0位起将i前面的后移一位
				{
					data[s]=data[s-1];
				}
				data[0]=change;//将i放到第一位
				
				
			}
			if(data[i]>data[0])
			{
				int change=data[i];//拿出data[i]			
				for(int s=0;s<i;s++ )//遍历已经排序的部分
				{  
					if(change>data[s]&&change<=data[s+1]){//通过大于【i】小于或等于【i+1】来确定要插入的位置
					                                    
					for(int e=i;e>=s+1;e--)
					{
						data[e]=data[e-1];  //右移动留出插入位置
					}	
					data[s+1]=change;  //插入
				}
				}
								
			}
		}
		
	}
	


改进:由于前部分已经排序了的,那么判断a[i]的插入位置只需要在已经排序的数据中判断就行,就是0~i这个已排序的数据中判断插入位置即可:

 

 

	/**插入排序
	 * 2015年10月29日上午11:09:33
	 * @param data
	 */
	public static void inSertSort(int[] data )
	{
		for(int i=1;i<data.length;i++)
		{
			int ai=data[i];
			int j;			
			for(j=i-1;j>=0&&data[j]>ai;j--)
			{				
				data[j+1]=data[j];
			}
			data[j+1]=ai;
		}
		
	}

 

 

 

4.希尔排序:

 

原理是什么:对于无序数组 a【1】a【2】a【3】a【4】a【5】a【6】

将其划分成子序列: gap=6/2=3;

分别是 :  a【1】a【4】       a【2】a【5】       a【5】a【6】 正序不交换,反序交换

那么通过排序让子序列有序得到:b【1】b【2】b【3】b【4】b【5】b【6】
 可知:b【1】<b【4】  b【2】<b【5】   b【3】<b【6】

接下来 gap=3/2=1;

分别是:b【1】b【2】     b【2】b【3 】  b【3】b【4】     b【4】b【5】    b【5】b【6】

排序后得到序列:c【1】c【2】c【3】c【4】c【5】c【6】

可知c【1】<c【2】  c【2】<c【3】   】 c【3】<c【4】   c【4】<c【5】   c【5】<c【6】

结合b,可得到有序序列。

算法核心:不断的分成几个有序子序列,对子序列进行排序,最后得到基本有序的序列,然后进行一次插入排序。

 

public static void ShellSort(int[] datas)
	{
		int gap=datas.length/2;
		if(datas.length>0)
		gap=datas.length%2==0?gap-1:gap;
		while(gap>=1)
		{
			for(int i=0;i<=datas.length/2;i++)
			{				
				if(datas[i]>datas[i+gap])
				{
					int exchange=datas[i+gap];
					datas[i+gap]=datas[i];
					datas[i]=exchange;
				}
			}
			gap=gap/2;
		}
		inSertSort(datas);
	}
}


希尔排序最终也是用到插入排序,目的就是尽可以有序化,减小插入排序的运算,定义一个变量用于确定它允许的次数:

 

 

/**插入排序
	 * 2015年10月29日上午11:09:33
	 * @param data
	 */
	public static void inSertSort(int[] data )
	{		
		for(int i=1;i<data.length;i++)
		{
			
			int ai=data[i];
			int j;			
			for(j=i-1;j>=0&&data[j]>ai;j--)
			{	System.out.println("-----num"+num++);		
				data[j+1]=data[j];
			}
			data[j+1]=ai;
		}
		
	}
	
	public static void ShellSort(int[] datas)
	{
		int gap=datas.length/3;
		if(datas.length>0)
		gap=datas.length%2==0?gap-1:gap;
		while(gap>=1)
		{
			for(int i=0;i<=datas.length/2;i++)
			{				
				if(datas[i]>datas[i+gap])
				{System.out.println("-----num"+num++);
					int exchange=datas[i+gap];
					datas[i+gap]=datas[i];
					datas[i]=exchange;
				}
			}
			gap=gap/3;
		}
		inSertSort(datas);
	}


测试数据如下:

int[] datas = new int[] {8,15,4,55,98,14,77,35,88,21,546,875,1,65,756,43,4,87,54,11,25,66,78,95,555,423,657,442};

 

 

 

但是调用插入排序,打印出来的num:

 

-----num125


使用希尔排序的话:

 

 

-----num65

而且希尔排序的步长不一样的话运算树也不一样:

 

 

public static void ShellSort(int[] datas)
	{
		int gap=datas.length/3;
		if(datas.length>0)
		gap=datas.length%2==0?gap-1:gap;
		while(gap>=1)
		{
			for(int i=0;i<=datas.length/2;i++)
			{				
				if(datas[i]>datas[i+gap])
				{System.out.println("-----num"+num++);
					int exchange=datas[i+gap];
					datas[i+gap]=datas[i];
					datas[i]=exchange;
				}
			}
			gap=gap/2;
		}
		inSertSort(datas);
	}


打印出来的是:

 

 

-----num61

 

 

 

5.快速排序:

 

快速排序也叫分治法,以一个基准数为标准将其分为两个部分,一个是比这个数小的部分,一个是比这个数大的部分,然后让这两个部分按照这样的法则递归下去。

分成两个有序的部分思想是:

【1】【2】【3】【4】....【k】...【n-1】【n】

先以key=【1】为基准数,从n下标往左遍历,获取到第一个比key小的【k】,交换位置:【k】【2】【3】..【m】.....【1】.。。【n-1】【n】

然后从2开始往左遍历,找到第一个比key大的【m】,交换k下标以2下标:【k】【2】【3】..【1】..【m】...【n-1】【n】

可以知道:【1】前面的数都比【1】小,【m】后面的数都比【m】大

然后继续按照这个法则,从【1】下标到【m】的下标进行排序,一直到左边的下标等于右边的下标,最终分成两个部分....【1】....

前面的比【1】小,后面的比【1】大。。

然后分成两个部分递归,从0下标到【1】前一个下标,从【1】的后一个下标到【n】下标。以此下去最终通过实现排序。

 

/**快速排序
	 * 2015年10月30日下午1:50:53
	 * 
	 * @param data
	 */
	public static void FastSortToHalf(int[] data, int leftFirstPosition,int rightLastPosition) {		
		int size = rightLastPosition;
		if (leftFirstPosition < rightLastPosition) {
			while (leftFirstPosition < rightLastPosition) {//通过循环分成两个部分
				int key = data[leftFirstPosition];
				while (rightLastPosition > leftFirstPosition&& data[rightLastPosition] >= key) {
					rightLastPosition--;
				}

				if (leftFirstPosition < rightLastPosition) {
					data[leftFirstPosition] = data[rightLastPosition];
					data[rightLastPosition] = key;
				}

				while (leftFirstPosition < rightLastPosition&& data[leftFirstPosition] <= key) {
					leftFirstPosition++;
				}

				if (leftFirstPosition < rightLastPosition) {
					int changge = data[leftFirstPosition];
					data[leftFirstPosition] = data[rightLastPosition];
					data[rightLastPosition] = changge;
					
					
				}

			}
			FastSortToHalf(data, 0, leftFirstPosition - 1);//对这两个部分进行递归
			FastSortToHalf(data, leftFirstPosition + 1, size);
		}

	}

 

 

 

6.堆排序:

堆排序最好最坏以及平均的情况下时间复杂度都是O(nlongn),性能上比比较排序、冒泡排序、插入排序好。

思想:需要构建与保持一个最大堆,然后利用最大堆的性质实现排序。

原理:将要排序的数组看成一个完全二叉树,对于第i个位置,如果他有parent,那么parent位置为i/2,如果他有左子树,那么左子树位置为2*i,如果他有右子树,那么右子树位置为2*i+1,构建最大堆的原理是:如果父节点比它的子节点小,那么将父节点与它子节点的最大节点交换,子节点以此类推,达到父节点永远大于子节点,如此从下往上递归,最终根节点是最大的数。创建了大项堆后,将更节点的值与最后的值交换,将0-n-1下标的项重新构造大项堆,如此递归下去,最终排序。

所以步骤有:1.构造大项堆 2.将根节点与最后项交换 3.递归。

 

 

public static void HeapSort(int[] data) {
		
        BuildBiggestHeap(data, data.length);//创建大项堆
        for(int i=data.length;i>2;i--)//最后一个长度应该是3个节点
        {  
        	swap(data, 0,i-1);//交换根节点与最后项
        	BuildBiggestHeap(data, i);//重新创建大项堆
        }

	}

	/**创建大项堆
	 * 2015年11月2日下午3:21:02
	 * @param data
	 * @param length
	 */
	public static void BuildBiggestHeap(int[] data,int length) {
		if(length>data.length)
			return;
		for(int i=length/2-1;i>=0;i--){
			int parent_position=i;
			int left_position=i*2+1;
			int right_position=i*2+2;
			
			if(right_position<length)
			{
			
			if(right_position==length-1){//这时只有一个左子节点
				if(data[parent_position]<data[left_position]);
				swap(data, left_position, parent_position);
			}else{//有两个子节点
				int lagerposition=0;
				lagerposition=data[left_position]>data[right_position]?left_position:right_position;
				lagerposition=data[lagerposition]>data[parent_position]?lagerposition:parent_position;
				if(lagerposition!=parent_position)
					swap(data, parent_position, lagerposition);
			}
			}
		}

	}

	     //交换
	    private static void swap(int[] data, int i, int j) {  
	         int tmp=data[i];  
	         data[i]=data[j];  
	         data[j]=tmp;  
	     } 


7.归并排序:

 

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

排序实现思路是:将数组分成左右两个部分,将两个部分分别进行排序,然后将排序了的两个部分合并。

拆分的思路是通过递归进行不断的拆分,最终拆分成长度为2或者3的序列。

归并的思路是:构造一个缓冲数组,数组长度为要归并的两部分的长度,左部分下标为i,右部分下标为j,数组下标为k,如果a【i】<a【j】,那么将a【i】赋予s【k】,并i++,k++,反之j++,k++。当i或者j长度分配完后,应该讲i或者j剩下的部分按顺序富裕k。

所以代码实现的步骤为:1.拆分.  2.合并   并且对拆分进行递归。

 

<strong> </strong>private static void MergeSort(int[] data,int firstposition,int mergeiength)
	    {
	    	if(mergeiength==2)
	    	{
	    		sortlength2(data,firstposition);//长度为2时排序
	    		return;
	    	}
	    	if(mergeiength==3){
	    		sortlength3(data,firstposition);//长度为3时排序
	    		return;
	    	}
            	    	
	    	int mergeleftlength=mergeiength/2;
	    	int mergerightlength=mergeiength-mergeleftlength;
	    	
	    	MergeSort(data, firstposition, mergeleftlength);//拆分左边进行递归
	    	MergeSort(data, firstposition+mergeleftlength, mergerightlength);//拆分右边进行递归
	    	
	    	//拆分排序后将左右两部分合并
	    	int[] temp=new int[mergeiength];//缓存数组,将左右部分合并到缓存数组中
	    	int tempposition=0;	    	
	    	int i=firstposition,j=i+mergeleftlength;
	    	int leftmaxposition=firstposition+mergeleftlength;
	    	int rightmaxposition=firstposition+mergeiength;
	    	while(i<leftmaxposition&&j<rightmaxposition){	    	
	    		if(data[i]>data[j])
	    		 {
	    			temp[tempposition++]=data[j++];
	    			
	    		 }else {
	    			 temp[tempposition++]=data[i++];
	    		 }
	    		
	    	}
	    	 while(tempposition<mergeiength)//合并剩余的部分
	    	 {
	    		 if(i<leftmaxposition)
	    		 {
	    			 temp[tempposition++]=data[i++];
	    		 }
	    		 if(j<rightmaxposition){
	    			 temp[tempposition++]=data[j++];
	    		 }
	    	 }
	    		    	
	    	System.arraycopy(temp, 0, data, firstposition, mergeiength);//拷贝到缓存数组的对应位置中
	    }

	    
	    
	    
		private static void sortlength3(int[] data, int firstposition) {
			sortlength2(data, firstposition);//先排好前两个
			if(data[firstposition+2]<data[firstposition]){
				int s=data[firstposition+2];
				 data[firstposition+2]=data[firstposition+1];
				 data[firstposition+1]=data[firstposition];
				 data[firstposition]=s;
				}
			if(data[firstposition+2]>data[firstposition]&&data[firstposition+2]<data[firstposition+1]){
				int s=data[firstposition+2];
				 data[firstposition+2]=data[firstposition+1];
				 data[firstposition+1]=s;
				}
		}

		private static void sortlength2(int[] data, int firstposition) {
			if(data[firstposition]>data[firstposition+1])
				swap(data, firstposition, firstposition+1);
			
		}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值