码迷,mamicode.com
首页 > 编程语言 > 详细

常用排序算法总结---Java实现

时间:2015-08-29 12:44:30      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:排序算法   时间复杂度   空间复杂度   稳定性   

各个排序总结,以及时间,空间复杂度分析
一.冒泡排序:

/*
	冒泡排序:
		排序算法思想:进行n-1趟排序,每趟,相邻元素,两两相互比较,将其中如果前一个元素比后一个元素小
	则令其交换。(最后的结果是,小的往后移(从大到小的冒泡))

*/

class BubleSort 
{
	public static void main(String[] args) 
	{
		int[] arr = {1,4,6,3,7,4,9,8};
		bSort_1(arr);
		for (int a: arr)
		{
			System.out.print(a+".");
		}
	}
	/*
			算法的优化:可以设置一个boolean类型的变,初值为false,如果发生交换,则将其改为true,如果某次
		其值没有变化,仍为false,则可以提前结束。
	*/
	//从大到小的冒泡(外层控制趟数,内层)
	public static void bSort(int[] arr)
	{
		boolean flag = false;
		for (int i=0;i<arr.length-1 ;i++ )
		{
			flag = false;
			for (int j=0;j<arr.length-i-1 ;j++ )
			{
				if(arr[j]<arr[j+1])
				{
					flag = true;
					swap(arr,j,j+1);
				}
			}
			if(!flag)
				break;
		}
	}

	//从小到大的冒泡(如果前一个数比后一个数大,则进行交换)
	//2 4 6 7 5 3 4
	//7 
	public static void bSort_1(int[] arr)
	{
		boolean flag = false;
		for (int i=0;i<arr.length-1 ;i++ )
		{
			flag = false;
			for (int j=arr.length-1;j>i+1 ;j-- )
			{
				if (arr[j]<arr[j-1])
				{
					flag = true;
					swap(arr,j,j-1);
				}
				if(!flag)
					break;
			}
		}
	}
	public static void swap(int[] arr,int i,int j)
	{
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
}

二.选择排序

/*
	简单选择排序:
		排序算法的思想:对N个元素的数组,进行n-1趟排序,每趟中默认当前趟的数是最小(大)的,
	然后进行趟内的比较。比较的元素,应该是从趟数+1开始,找出最大的,将其放在数组中已经是拍好	
	序的元素之后(也就是当前趟数的位置,即,将最大值与当前趟数对应元素进行交换位置)。		
*/

class SelectSort 
{
	public static void main(String[] args) 
	{
		int[] arr = {1,4,6,3,7,4,9,8};
		sSort(arr);
		for (int a: arr)
		{
			System.out.print(a+".");
		}
	}
	
	public static void sSort(int[] arr)
	{
		for (int i=0;i<arr.length/*这里可以优化(少排一次):arr.lenth-1*/ ;i++ )
		{
			int index = i;
			for (int j=i+1;j<arr.length ;j++ )
			{
				if (arr[index]>=arr[j])
				{
					index = j;
				}
			}

			if (index!=i)
			{
				swap(arr,index,i);
			}
		}
	}

	public static void swap(int[] arr,int index,int i)
	{
		int temp = arr[index];
		arr[index] = arr[i];
		arr[i] = temp;
	}
}

三.快速排序

class QuickSort 
{
	public static void main(String[] args) 
	{
		int[] arr = {50,10,90,30,70,40,80,60,20};
		System.out.println(java.util.Arrays.toString(arr));
		System.out.println("--------排序开始--------");
		quick(arr);
		System.out.println("--------排序完成--------");
		System.out.println(java.util.Arrays.toString(arr));
	}
	/* 
			快排。思想:对于数组中从Start--end索引范围的子序列进行处理,使之满足所有小于分界值的放在左边,所有大于分界值的放在右边(从两头最边缘开始),由于观察到每次都是在左序列或者右序列中操作,那么可以采用递归的方法来做
		*/
	private static void quick(int[] arr){
		subSort(arr,0,arr.length-1);
	}
	private static void subSort(int[] arr,int start,int end){
		//区间存在,说明需要排序
		if(start<end){
			//以第一个元素作为分界值
			int base = arr[start];
			//i从左边搜索,搜索大于分界值的索引
			int i = start;
			//j从右边搜索,搜索小于分界值的索引
			int j = end+1;
			while(true){
				//--i是先运算在使用(所以j要从end+1开始,而i需要从start开始,因为start是基数)
				//找到大于分界值的元素的索引,或者i已经到了end处
				while(i < end && arr[++i] <= base);
				//找到小于分界值的元素的索引,或者j已经到了start处
				while(j>start && arr[--j] >= base);
				if(i<j){
					//说明区间尚未遍历完,将本次找到的两个数交换
					swap(arr,i,j);
				}else{
					//说明该端区间进行一次快排完成
					System.out.println(java.util.Arrays.toString(arr));
					break;
				}
			}
			//将基数base和序列中间值交换
			swap(arr , start , j);
			//递归快排左子序列
			subSort(arr,start,j-1);
			//递归快排右序列
			subSort(arr,j+1,end);
		}
	}
	private static void swap(int[] arr,int i,int j){
		/*不用第三方变量完成两个数交换
			原	理: a = 5 ,b = 3
			a = a + b; //a = 3 + 5;
			b = a - b; //b = 8 - 3 = 5;
			a = a - b; //a = 8 - 5 = 3
		*/
		arr[i] = arr[i] + arr[j];
		arr[j] = arr[i] - arr[j];
		arr[i] = arr[i] - arr[j];
	}
}

四.插入排序

class  InsertSort
{
	public static void main(String[] args) 
	{
		int[] arr = {50,10,90,30,70,40,80,60,20};
		System.out.println(java.util.Arrays.toString(arr));
		System.out.println("--------排序开始--------");
		insertSort(arr);
		System.out.println("--------排序完成--------");
		System.out.println(java.util.Arrays.toString(arr));
	}
	public static void	insertSort(int[] arr){
		//直接插入排序。思:想每次将当前i元素插入到前n-i已经排好序的数组中(需要进行n-1次插入,第一个元素不需要插入)
		for (int i = 1;i<arr.length ;i++ )
		{
			//记录当前的值,保证数组在后移的过程中不会丢失
			int temp = arr[i];
			//因为前i-1个数已经是排好的数列了,i-1最大,如果i比i-1还大那么说明还是有序的不需要插入操作
			if(arr[i]<arr[i-1]){
				int j = i -1;
				//整体整体往后移一格
				while(j>=0 && arr[j]>temp )
				{
					//循环判断和后移,如果当前元素大于temp,说明该位置不是temp该插入的位置,后移。
					//直到找到一个元素,小于temp,说明该位置的后一位正是插入的位置
					arr[j+1] = arr[j];
					j--;
				}
				//找到位置插入
				arr[j+1] = temp;
			}
			System.out.println(java.util.Arrays.toString(arr));
		}
	}
}

五.折半插入


class BinarySearchInsertSort 
{
	public static void main(String[] args) 
	{
		int[] arr = {50,10,90,30,30,70,40,80,60,20};
		System.out.println(java.util.Arrays.toString(arr));
		System.out.println("--------排序开始--------");
		binarySearchInsertSort(arr);
		System.out.println("--------排序完成--------");
		System.out.println(java.util.Arrays.toString(arr));
	}
	/*
		折半插入排序。思想:其实是对直接插入排序的改进。将要把当前元素要插入有序数列位置寻找,使用二分查找法代替
	*/
	public static void binarySearchInsertSort(int[] arr){
		for (int i = 1; i<arr.length ; i++ )
		{
			//同样首先记住当前位置的值。
			int temp = arr[i];
			//同样只要在当前值小于有序列中最大值时需要判断插入位置,否则不需要插入
			if(arr[i]<arr[i-1]){
				//开始二分查找
				int low = 0;
				int high = i - 1;
				//想法:这里对于mid下标的值和temp比较,如果遇到相等的情况,那么可以直接将此时的low值置为 mid.然后跳出循环.这样可以优化比较次数
				while(low <= high){
					int mid = (low + high) / 2;
					if(temp>arr[mid]){
						//限制索引在大于mid那一半找
						low = mid + 1;
					}else if(temp<arr[mid]){
						//限制索引在小于mid那一半找
						high = mid -1;
					}else {
						low = mid;
						break;
					}
				}

				//确定了当前的值的位置(low的位置),后移数组
				for (int j = i; j>low ; j-- )
				{
					arr[j] = arr[j-1];
				}
				//将当前值插入到low位置
				arr[low] = temp;
			}
			System.out.println(java.util.Arrays.toString(arr));
		}
	}
}

六.堆排序

//堆排序:主要分为建堆和,排序调整堆

class  HeapSort
{
	public static void main(String[] args) 
	{
		int[] arr = {9,79,46,30,58,49,22,44,12,23,45};
		System.out.println(java.util.Arrays.toString(arr));
		System.out.println("--------排序开始--------");
		heap(arr);
	}
	public static void heap(int[] arr){
		//循环建堆(每次建好之后,排序完之后,需要调整堆。)
		for (int i = 0;i<arr.length ;i++ )
		{
			//建堆
			buildMaxHeap(arr,arr.length-i-1);
			//交换堆顶和最后一个元素
			swap(arr,0,arr.length-1-i);
			System.out.println(java.util.Arrays.toString(arr));
		}
	}
	//建大根堆
	private static void buildMaxHeap(int[] arr,int lastIndex){
		//从lastIndex节点的父节点开始
		for (int i=(lastIndex-1)/2;i>=0 ;i-- )
		{
			//K保存当前正在判断的节点
			int k = i;
			//如果当前K节点的子节点存在(循环判断两个子节点与当前节点值的大小)(这里为什么要使用while循环,是因为当前堆交换后下面的堆顺序可能会乱,需要再次向下)
			while(k*2+1<=lastIndex){
					//k节点的左子节点的索引
					int biggerIndex = 2*k+1;
					if((biggerIndex+1)<=lastIndex){
						//k节点的右子节点存在
						if(arr[biggerIndex]<arr[biggerIndex+1]){
							//右子节点的值较大(使用biggerIndex记录较大的节点)
							biggerIndex++;
						}
					}
			
				
				//如果K节点的值小于其较大子节点的值
				if(arr[k]<arr[biggerIndex]){
					//交换他们
					swap(arr,k,biggerIndex);
					//将biggerIndex的值赋给K节点,然后开始下一次的while循环(判断该子节点下的子节点有比当前最大节点大的值)
					//从新保证K节点的值大于其左右子节点的值
					k = biggerIndex ; 
				} else{
					break;
				}
			}
		}
	}

	private static void swap(int[] arr,int i,int j){
			int temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;
	}
}

七.Shell排序

class ShellSort 
{
	public static void main(String[] args) 
	{
		int[] arr = {50,10,90,30,70,40,80,60,20,110,800,200,100,12};
		System.out.println(java.util.Arrays.toString(arr));
		System.out.println("--------排序开始--------");
		shellSort(arr);
		System.out.println("--------排序完成--------");
		System.out.println(java.util.Arrays.toString(arr));	
	}
	public static void shellSort(int[] arr){
		/*
			shell排序。思想:shell是直接插入排序的已近改进。只是将直接插入排序的每次插入增量1。改为h(shell值),然后通过一定规律递减h,进行插入排序,直到h<0时。为什么要这样做?因为插入排序有个特点,如果序列基本有序时(小元素在前面,大元素在后面,中等元素在中间),那么我们对于序列的只需要很少移动即可。一般关于h的取值: h = length , h = h / 3 + 1;
		*/
		//确定shell值
		int h = 1 ;
		while (h <= arr.length/3)
		{
			h = h*3 + 1;
		}
		//shell值>0,就继续进行插入排序
		while(h>0){
			//一趟以h为间距的插入排序完成。
			System.out.println("当前h的值" + h);
			for (int i=h;i<arr.length ; i++)
			{
				//以h为间距进行插入排序

				//同样先记住当前要插入的值
				int temp = arr[i];
				//如果当前值比有序列中的最大值(i-1)都要大,说明位置正确,不需要进行插入,否则插入
				if(temp<arr[i-h]){
					int j = i - h;
					//以h为间距后移,如果arr[j]大于temp说明插入位置没有找到,该位置后移
					while (j>=0 && arr[j]>temp)
					{
						arr[j+h] = arr[j];
						j = j - h;
					}
					arr[j+h] = temp;
				}
				System.out.println(java.util.Arrays.toString(arr));
			}	
			h = (h - 1) / 3;
		}
	}

}

八.归并排序

class  MergeSort
{
	public static void main(String[] args) 
	{
		int[] arr = {50,10,90,30,70,40,80,60,20};
		System.out.println(java.util.Arrays.toString(arr));
		System.out.println("--------排序开始--------");
		mergeSort(arr);
		System.out.println("--------排序完成--------");
		System.out.println(java.util.Arrays.toString(arr));	
	}

	public static void mergeSort(int[] arr){
		/*
			归并排序.思路:算法有一种分治思路就是将大问题化为若干个小问题,然后每个小问题的解组合起来,就是大问题的解,归并排序就是采用了这样的思路。对一系列数据排序,不断对半分解,直到每组一个元素,那么直接排。然后将排好序的分组,归并起来。
			所以分两步,分解,合并。关于分解我们可以采用递归取做。
		*/
		sort(arr,0,arr.length-1);
	}
	//将索引从left到right范围的数组元素进行归并排序.(第一步分解)
	private static void sort(int[] arr,int left,int right){
		//存在区间,可以进行排序
		if(left<right){
			//找到中间索引,以中间索引为界,分解
			int center = (left+right) / 2;
			//对左部分数组进行递归排序
			sort(arr,left,center);
			//对右部分数组进行递归排序
			sort(arr,center+1,right);
			//开始归并(当left=right时开始)
			merge(arr,left,center,right);
		}
	}
	//将left到center 和 center+1到right的数组进行合并(第二步合并)
	//将两个数组进行归并,归并前两个数组已经有序,归并之后依然有序。
	private static void merge(int[] arr,int left,int center,int right){
		//需要创建一个同等大小的辅助数组(中间数组),用来保存归并之后的数组
		int[] tempArr = new int[arr.length];
		int mid = center + 1;
		//third记录中间数组(辅助数组)的索引
		int third = left;
		//记录元数组的起始下标(用于最后复制回元数组用)
		int temp = left;

		//两个数组中都还有未遍历到的值
		while(left<=center && mid<=right){
			//从两个数组中取出小的放入到中间数组
			if(arr[left]<arr[mid]){
				tempArr[third++] = arr[left++];
			}else{
				tempArr[third++] = arr[mid++];
			}
		}
		//上面两个数组可能有一个数组还有值没有遍历到。接下来将可能剩余的数组放入到中间数组
		while (left<=center){
			tempArr[third++] = arr[left++];
		}
		while(mid<=right){
			tempArr[third++] = arr[mid++];
		}
		//以上已经将两个数组归并到同一个数组中。
		//将中间数组的内容复制会原数组(原left--right范围的内容被复制回元数组),组成为一个更大有序集合
		while (temp<=right)
		{
			arr[temp] = tempArr[temp++];
		}
		//此时一个小区间归并完毕.
		System.out.println(java.util.Arrays.toString(arr));
	}
	private static void swap(int[] arr, int i ,int j){
	/*
		不借助第三方变量,交换两个变量的值
		a = a ^ b;
		b = a ^ b;// b = (a ^ b) ^ b = a;
		a = a ^ b;// a = a ^ (a ^ b) = b;
	*/
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}
}

九.对常用算法的时空复杂度,稳定性分析。
技术分享



版权声明:本文为博主原创文章,未经博主允许不得转载。

常用排序算法总结---Java实现

标签:排序算法   时间复杂度   空间复杂度   稳定性   

原文地址:http://blog.csdn.net/qq_19776363/article/details/48085941

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!