java排序(5):归并排序和基数排序

一.归并排序

1.基本思想

  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
  归并排序就是利用归并的思想实现的排序方法,效率比较高。其基本原理如下:对于给定的一组记录,利用递归与分治技术将数据序列划分成为越来越小的半子表,在对半子表排序,最后再用递归方法将排好序的半子表合并成为越来越大的有序序列。
  

2.思路

(1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
(2)设定两个指针,最初位置分别为两个已经排序序列的起始位置
(3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
(4)重复步骤3直到某一指针达到序列尾
(5)将另一序列剩下的所有元素直接复制到合并序列尾

这里写图片描述

3.代码

public class MergeSort {

	//两个数组合并
	public static void merge(int[] nums, int low, int mid, int high) {
		int[] newArr = new int [high-low+1]; //新合并元素数组
		int i = low;   // 左指针
		int j = mid+1; // 右指针
		int k = 0;
		while(i<=mid && j<=high){   // 把较小的数先移到新数组中
			if(nums[i]<nums[j]){
				newArr[k++] = nums[i++];
			}else{
				newArr[k++] = nums[j++];
			}
		}
		
		while (i <= mid){   // 把左边剩余的数移入新数组中
			newArr[k++] = nums[i++];  
		}
		while (j <= high) {   // 把右边边剩余的数移入新数组中
			newArr[k++] = nums[j++];
		}
		
		 // 把新数组中的数覆盖nums数组
        for (int temp = 0; temp < newArr.length; temp++) {
        	nums[temp + low] = newArr[temp];
        }
		
	}
	
	public static void mergeSort(int[] nums, int low, int high) {
		int mid = low + (high-low)/2;  // 防止数字超出,堆栈溢出
		if(low<high){
			mergeSort(nums, low, mid);  // 左边子数组
			mergeSort(nums, mid+1, high); //右边子数组
			merge(nums, low, mid, high); //左右子数组合并
		}
	}
}

测试:

@Test
public void testMergeSort(){		
	int numbers[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
    MergeSort.mergeSort(numbers, 0, numbers.length - 1);
    for(int i=0;i<numbers.length;i++){  
        System.out.print(numbers[i]+" ");  
    }  
}

结果:

18 20 30 46 50 51 65 77 82 97 

4.优缺点

归并排序需要O(nlogn)的比较和O(nlogn)的数据移动。

(1)优点

①归并排序的效率达到了巅峰
时间复杂度为O(nlogn),这是基于比较的排序算法所能达到的最高境界。
②归并排序是一种稳定的算法
这一点在某些场景下至关重要。
③归并排序是最常用的外部排序方法
当待排序的记录放在外存上,内存装不下全部数据时,归并排序仍然适用,当然归并排序同样适用于内部排序。

(2)缺点

归并排序需要O(n)的辅助空间,而与之效率相同的快排和堆排分别需要O(logn)和O(1)的辅助空间,在同类算法中归并排序的空间复杂度略高

二.基数排序

1.基本思想

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法

2.思路

(1)假设数组中最大位数为d,将数组按照最小位数排序。
(2)按照位数为10^(d-1)位(个位,十位,百位)排序d次,数组即为有序序列了
案例:
存在数组:{73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100}
最大位数:3位
第1次按照个位排序:{100, 81, 22, 73, 93, 43, 33, 14, 55, 65, 28, 39 }
第2次按照十位排序:{100, 14, 22, 28, 33, 39, 43, 55, 65, 73, 81, 93 }
第3次按照百位排序:{ 14, 22, 28, 33, 39, 43, 55, 65, 73, 81, 93, 100}

3.代码

public class RadixSort{
	public static void radixSort(int[] numbers, int d){  //d表示最大的数有多少位
		int n = 1; //取个位/十位/百位
		int[][]temp = new int[10][numbers.length]; //数组的第一维表示可能的余数0-9
		int[]order = new int[10]; //数组order[i]用来表示该位是i的数的个数
	    //i控制键值排序依据在哪一位
		for(int i=0; i<d; i++){
			int m = 0;
			
			for (int j = 0; j < numbers.length; j++) {
				int remainder = (numbers[j]/n)%10; //除10取余
				temp[remainder][order[remainder]] = numbers[j];
				order[remainder]++;
			}
			
			for(int k=0; k<10; k++){
				if(order[k] != 0){
					for (int l = 0; l < order[k]; l++) {
						numbers[m] = temp[k][l];
						m++;
					}
				}
				order[k] = 0;
			}
			
			n *= 10;		
		}
	}
}

测试:

@Test
public void testRadixSort(){
    int[]data = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100};
       RadixSort.radixSort(data, 3);
       for(int i = 0; i < data.length; i++){
           System.out.print(data[i] + " ");
       }
}

结果:

14 22 28 33 39 43 55 65 73 81 93 100 

4.优缺点

(1)优点

基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

(2)缺点

不呈现时空局部性,因为在按位对每个数进行排序的过程中,一个数的位置可能发生巨大的变化,所以不能充分利用现代机器缓存提供的优势。同时计数排序作为中间稳定排序的话,不具有原地排序的特点,当内存容量比较宝贵的时候,还是有待商榷

三.参考

http://blog.csdn.net/jianyuerensheng/article/details/51262984
http://blog.csdn.net/Touch_2011/article/details/6785881

posted @ 2017-03-16 20:39  nowuseeme  阅读(246)  评论(0)    收藏  举报