分治法的基本思想_归并排序-分治思想的典型应用

前面的文章讲了二分查找算法,这种算法使用了一种非常常见的思想–"分治思想"。所谓分治,就是"分而治之",当解决一个大问题时,如果问题太大而无法直接解决,就可以试着把问题分解成很多个相互独立的子问题,将每个子问题各个击破,最后每个子问题得到的结果一合并就得出了原问题的结果,分治法一般都通过递归实现。本文讲解的归并排序就是分治思想的经典应用。

一.基本思想

1. 分解:给出包含若干个数的一段序列。按照分治思想要把序列分成两部分,然后对每个部分,再以相同的方式分成两个更小的序列。就这样一直分下去,到最后每段序列都只有一个数了。无法再分解了,那该怎样排序呢?答案是不用排序了,每个序列都只有一个数还怎么排序呢,换句话说,只有一个数,这本身就是有序的。这就是分治算法的妙处,把一个无序的序列分成了若干个有序的序列。

2. 合并:合并是整个排序算法的难点和重点。阐述起来很简单,就是把分开的子序列两两按序合并。

· 说起来很简单,要想真正的理解并不容易,以图解的形式描述归并的过程更直观一些。

二.图解

分解的步骤很简单。

5bd7cc3f1856a98e8ed6b76a5d398a53.png

把整个序列分成多个仅有一个数的序列后,开始合并。

第一趟合并

0d33ee2b993fb0f060c2833595b0edfa.png

第二趟合并

c10278e5254becb7c4eb36afecc3c947.png

第三趟合并

b186dbeeabe1275623acff5dee4f36b9.png

在不断合并的过程中,序列中的元素就自动排好了序。

三.具体实现

上述就是归并排序的思路,那具体是如何实现的呢?

我们要先定义一个临时数组b[],我们将被排序的数组命名为数组a[]。在两个序列合并的过程中,要先将两个序列中的元素进行比较,再按顺序存放到临时数组b[]中,这样b[]中的元素就是有序的,再将b[]中的数组复制到a[]中。以最后一次合并为例(由于合并的两序列也都经过了多次比较和合并之后才形成,所以也是有序的)

注:下文的比较过程中用的首个元素并不是序列本身的首个元素,序列本身的首个元素是固定不变的,这里的首个元素是序列中未进行比较的元素中的首个元素。

(1)第一次比较,4<7,4放入临时数组,12变成了第一个序列的首个元素。

f0a69ee0f7d9f2d5bc83eae927064cf3.png

(2)第二次比较,7<12,7放入临时数组,16变成了第二个序列的首个元素。

8caeae8a73d3dd5167934ac370904147.png

(3)第三次比较,12<16,12放入临时数组,13变成了第一个序列首个元素。

2174f801dcbee7ce99e320451570a708.png

(4)第四次比较,13<16,13放入临时数组,50变成了第一个序列的首个元素。

e1d4cd4cbc0bacb71a1897022a4244e8.png

(5)第五次比较,16<50,16放入临时数组,25变成了第二个序列的首个元素。

fad42c3f30411200102b0af4e6a72156.png

(6)第六次比较,25<50,25放进临时数组,33变成了第二个序列的首个元素。

51e4a90d09968e6cdfe1b9b3a7aaa071.png

(7)第七次比较,33<50,33放进临时数组,第二个序列的元素全部被安排了。

f9c415af3210dcab07ed9fed4c3843ff.png

(8)第八次比较,发现第二个序列都比完了,这时候就把第一个序列中的剩余元素(其实也就一个了)按序放进数组。

94298ec0ca201d7b110f31481763a632.png

这段过程的实现代码为

private static void Merge(int a[],int low,int mid,int high){        int[] b=new int[high-low+1];//开临时数组        int i=low,j=mid+1,t=0,t1=0;//i,j分别作为两部分的指针        while(i<=mid&&j<=high){//两部分比较,小的进临时数组            if(a[i]>a[j]){                b[t++]=a[j++];            }else {                b[t++]=a[i++];            }        }        while(i<=mid) b[t++]=a[i++];//如果有一方元素比较完了,另一方的元素直接塞进去        while(j<=high) b[t++]=a[j++];        for(i=0;i

四.代码

import java.util.Scanner;public class Sort {    public static void main(String[] args) {        Scanner sc=new Scanner(System.in);        int n=sc.nextInt();        int[] a=new int[n];        for(int i=0;ia[j]){                b[t++]=a[j++];            }else {                b[t++]=a[i++];            }        }        while(i<=mid) b[t++]=a[i++];        while(j<=high) b[t++]=a[j++];        for(i=0;i

如果您觉得这篇文章对您有所帮助,欢迎关注我的微信公众号–【堆栈树图】,阅读更多的类似文章。如果您发现该文章有错误或不足之处,也欢迎批评指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分治法是一种常用的算法设计方法,它将一个大问题分解成若干个相同或类似的子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到原问题的解。归并排序就是一种使用分治法的排序算法。 归并排序思想如下: 1. 将待排序的数组不断地二分,直到每个子数组只有一个元素。 2. 将相邻的两个子数组进行合并,合并时按照从小到大的顺序将元素放入一个临时数组中。 3. 重复步骤2,直到所有的子数组都合并成一个有序的数组。 以下是C++实现归并排序算法的代码示例: ```cpp // 合并两个有序数组 void merge(vector<int>& nums, int left, int mid, int right) { vector<int> temp(right - left + 1); // 临时数组用于存放合并结果 int i = left; // 左子数组的起始位置 int j = mid + 1; // 右子数组的起始位置 int k = 0; // 临时数组的索引 // 将两个子数组中较小的元素依次放入临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { temp[k++] = nums[i++]; } else { temp[k++] = nums[j++]; } } // 将剩余的元素放入临时数组中 while (i <= mid) { temp[k++] = nums[i++]; } while (j <= right) { temp[k++] = nums[j++]; } // 将临时数组中的元素复制回原数组 for (int m = 0; m < k; m++) { nums[left + m] = temp[m]; } } // 归并排序 void mergeSort(vector<int>& nums, int left, int right) { if (left >= right) { return; } int mid = left + (right - left) / 2; mergeSort(nums, left, mid); // 对左子数组进行排序 mergeSort(nums, mid + 1, right); // 对右子数组进行排序 merge(nums, left, mid, right); // 合并两个有序子数组 } // 调用归并排序函数 vector<int> nums = {5, 2, 9, 1, 7}; mergeSort(nums, 0, nums.size() - 1); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值