一.归并排序
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