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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值