分治策略之归并排序

归并排序也属于分治策略的实现之一,它完全遵循了分治的模式。直观上的操作如下:

1、分解:分解待排序的n个元素的序列成各自包含n/2个元素的两个子序列;

2、解决:使用递归的方式排序两个子序列;

3、合并:合并两个已排序的子序列以产生排序的答案。

分治都是有边界的,在归并中递归的边界就是只有一个元素,在只有一个元素的时候,本身就是排序好的。

归并排序的重点不在于分解,而在于合并,如何将两个已经排序好的子序列进行合并,产生新的排序好的序列呢?

我们可以通过下面的代码实现,这个算法也是比较典型的算法面试题之一:如何合并两个已经排序好的数组(有的时候数组会变成链表)。

public class MergeSort
{
    public static void main(String[] args)
    {
        int[] nums1 = {1, 4, 5};
        int[] nums2 = {2, 7, 9};
        int[] nums = mergeArray(nums1, nums2);
        for (int i : nums)
        {
            System.out.println(i);
        }
    }
    
    private static int[] mergeArray(int[] first, int[] second)
    {
        if (first == null)
        {
            return second;
        }
        if (second == null)
        {
            return first;
        }
        int[] answers = new int[first.length + second.length];
        
        // 合并两个数组的方式比较简单
        // 1、同时遍历两个数组,对两个数组分别定义两个指针开始跑
        // 2、比较两个指针对应大小的值,然后插入到新的数组(注意新数组也需要一个指针)
        int i = 0, j = 0, k = 0;
        
        while (i < first.length && j < second.length)
        {
            if (first[i] < second[j])
            {
                answers[k++] = first[i++];
            }
            else
            {
                answers[k++] = second[j++];
            }
        }
        // 此时可能还存在问题需要处理:例如first还剩下或者second还剩下
        if (i == first.length) // first已经遍历到头了,剩下的都是second
        {
            while (j < second.length)
            {
                answers[k++] = second[j++];
            }
        }
        else
        {
            while (i < first.length)
            {
                answers[k++] = first[i++];
            }
        }
        return answers;
    }
}

 

但是在归并排序中我们不能直接使用上述方式,还需要做一下转换,输入不是两个数组,而是一个数组,只是通过指针方式进行合并,原理是一样的:

    /**
     * 其中low是起始,middle是第一个数组的终止,middle+1是下个数组的起始,high是下个数组的结束
     * 即有两个排好序的数组nums[low...middle]和nums[middle+1...high]
     * @param nums
     * @param low
     * @param middle
     * @param high
     */
    private static void mergeArray(int[] nums, int low, int middle, int high)
    {
        int[] temp = new int[high - low + 1]; // 创建一个临时的数组,等填充完此数组之后再将结果赋给nums
        
        int i = low, j = middle + 1, k = 0;
        
        while (i <= middle && j <= high)
        {
            if (nums[i] < nums[j])
            {
                temp[k++] = nums[i++];
            }
            else
            {
                temp[k++] = nums[j++];
            }
        }
        // 此时可能还存在问题需要处理:例如first还剩下或者second还剩下
        if (i == middle + 1) // first已经遍历到头了,剩下的都是second
        {
            while (j <= high)
            {
                temp[k++] = nums[j++];
            }
        }
        else
        {
            while (i <= middle)
            {
                temp[k++] = nums[i++];
            }
        }
        
        // 重新赋给nums数组
        for (int n = 0; n < temp.length; n++)
        {
            nums[n + low] = temp[n];
        }
        
    }

 

然后我们利用分解递归的方式进行处理,代码如下:

public class MergeSort2
{
    public static void main(String[] args)
    {
        int[] nums = {9, 1, 5, 7, 2, 6, 3, 8, 10, 4};
        nums = mergeSort(nums, 0, nums.length - 1);
        for (int i : nums)
        {
            System.out.print(i + " ");
        }
    }
    
    private static int[] mergeSort(int[] nums, int low, int high)
    {
        int middle = (low + high) / 2;
        if (low < high) // 边界就是两个相等,相等的时候意味着只有一个元素,直接返回即可
        {
            // 排序左侧的部分
            mergeSort(nums, low, middle);
            
            // 排序右侧的部分
            mergeSort(nums, middle + 1, high);
            
            // 经过两次排序之后,nums是一个有序的两个数组了,需要将其进行合并
            mergeArray(nums, low, middle, high);
        }
        return nums;
    }
    
    /**
     * 其中low是起始,middle是第一个数组的终止,middle+1是下个数组的起始,high是下个数组的结束
     * 即有两个排好序的数组nums[low...middle]和nums[middle+1...high]
     * @param nums
     * @param low
     * @param middle
     * @param high
     */
    private static void mergeArray(int[] nums, int low, int middle, int high)
    {
        int[] temp = new int[high - low + 1]; // 创建一个临时的数组,等填充完此数组之后再将结果赋给nums
        
        int i = low, j = middle + 1, k = 0;
        
        while (i <= middle && j <= high)
        {
            if (nums[i] < nums[j])
            {
                temp[k++] = nums[i++];
            }
            else
            {
                temp[k++] = nums[j++];
            }
        }
        // 此时可能还存在问题需要处理:例如first还剩下或者second还剩下
        if (i == middle + 1) // first已经遍历到头了,剩下的都是second
        {
            while (j <= high)
            {
                temp[k++] = nums[j++];
            }
        }
        else
        {
            while (i <= middle)
            {
                temp[k++] = nums[i++];
            }
        }
        
        // 重新赋给nums数组
        for (int n = 0; n < temp.length; n++)
        {
            nums[n + low] = temp[n];
        }
        
    }
}

 

转载于:https://www.cnblogs.com/bruceshao/articles/7115281.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值