分治法——合并排序MergeSort

合久必分,分久必合——合并排序

在数列排序中,如果只有一个属,本身就是有序的。如果只有两个数字,那么只需要一次就可以简单地排完序。也就是说,数字越少,排序越简单。那么如果有一个有大量数据组成的数列,我们很难完成排序,那该怎么办呢?我们可以考虑将其一直分解,分解直至只剩下一个数字,本身即有序,然后再将这些有序的数组合并在一起,执行一个与分解相反的过程,从而完成整个数列的排序 

合并排序就是采用分治的思想,将一个大的问题分解成很多个小问题,先解决小问题,再通过小问题解决大问题。 

算法设计 :

  1. 分解——将有序元素分解成大小大致相同的两个子序列
  2. 治理——对两个子序列再进行合并排序
  3. 合并——将拍好的有序子序列进行合并,得到最终的有序序列。

代码 :

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

void Merge(int A[],int low,int middle,int high)
{
    int *B=new int [high-low+1];//创建一个辅助数组,长度跟数组A一样长
    int i=low; //将i指针指向第一分段的最左边元素
    int j=middle+1; //将指针指向第二分段的最左边元素
    int k=0;//指向辅助函数第一元素的指针
    while(i<=middle && j<=high)
    {
        if(A[i]<=A[j])
        {
            B[k++]=A[i++];
        }
        else
        {
            B[k++]=A[j++];
        }
        //比较分段之后比较i和j指针指向的元素大小,并将较小的放入辅助数组B,然后指针后移
    }
    while(i<=middle)
    {
        B[k++]=A[i++];
    }
    while(j<=high)
    {
        B[k++]=A[j++];
    }
    //这两个while是处理A数组剩下的元素。因为分段之后数组大小不一定一样。并将其复制到B数组中去。
    for(i=low,k=0;i<=high;i++)
    {
        A[i]=B[k++];
    }
    //此时B数组中是排好序的数字,将其所有元素复制到A数组中去
    delete []B;//使用完B数组之后,将其删除掉。
}

void MergeSort(int A[],int low,int high)
{
    if(low<high)
    {
        int middle=(low+high)/2;
        MergeSort(A,low,middle); //将第一段进行合并排序(分治递归)
        MergeSort(A,middle+1,high); //将第二段进行合并排序
        Merge(A,low,middle,high);
        //最终合并完成后复制到A辅助数组。
    }
}

int main()
{
    int A[1111];
    int n;
    cout << "请输入元素的个数:"<< endl;
    cin >> n;
    cout << "请输入数组中的元素:" << endl;
    for (int i=0;i<n;i++)
    {
        cin>> A[i];
    }
    MergeSort(A,0,n-1);
    cout << "合并排序之后的结果:"<< endl;
    for (int i=0;i<n;i++)
    {
        cout << A[i] << " " ;
    }
    cout << endl;
    return 0;
}

分治法的核心思想是将一个问题分解为若干个小问题分别求解,最后再将小问题的结果合并得到最终结果。在排序算法中,“归并排序”就是基于分治法的经典实现之一。下面是用Java语言实现对无序数组 `[24,17,40,28,13,14,22,32,40,21,48,4,47,8,37,18]` 进行归并排序的具体代码及解析。 ### Java 实现代码 ```java public class MergeSort { public static void main(String[] args) { int[] array = {24, 17, 40, 28, 13, 14, 22, 32, 40, 21, 48, 4, 47, 8, 37, 18}; System.out.println("原始数组:"); printArray(array); mergeSort(array, 0, array.length - 1); System.out.println("排序后数组:"); printArray(array); } // 归并排序核心方法 private static void mergeSort(int[] array, int left, int right) { if (left < right) { int mid = (left + right) / 2; // 找到中间位置 // 对左半部分进行归并排序 mergeSort(array, left, mid); // 对右半部分进行归并排序 mergeSort(array, mid + 1, right); // 合并排序的两个子数组 merge(array, left, mid, right); } } // 合并两个有序数组的方法 private static void merge(int[] array, int left, int mid, int right) { int n1 = mid - left + 1; // 左边数组长度 int n2 = right - mid; // 右边数组长度 // 创建临时数组存放两边的数据 int[] L = new int[n1]; int[] R = new int[n2]; for (int i = 0; i < n1; ++i) L[i] = array[left + i]; // 填充左边数组 for (int j = 0; j < n2; ++j) R[j] = array[mid + 1 + j]; // 填充右边数组 // 开始比较左右两个数组的内容,按顺序放入原数组 int i = 0, j = 0, k = left; while (i < n1 && j < n2) { if (L[i] <= R[j]) { array[k++] = L[i++]; } else { array[k++] = R[j++]; } } // 如果左边有剩余,则直接加入原数组 while (i < n1) array[k++] = L[i++]; // 如果右边有剩余,则直接加入原数组 while (j < n2) array[k++] = R[j++]; } // 辅助方法:用于打印数组内容 private static void printArray(int[] array) { for (int num : array) { System.out.print(num + " "); } System.out.println(); } } ``` --- ### 输出结果 运行以上程序会得到以下输出: ``` 原始数组: 24 17 40 28 13 14 22 32 40 21 48 4 47 8 37 18 排序后数组: 4 8 13 14 17 18 21 22 24 28 32 37 40 40 47 48 ``` --- ### 分析与解释 #### 1. 分解(Divide) - 输入的数组首先被划分为尽可能小的单元——单个数字组成的数组。 - 初始时,整个数组被视为一个整体,在 `mergeSort()` 函数中通过递归来分割为更小的部分。 例如对于数组 `[24,17,...]` 的第一次分割将会变成 `[24,17,40,28,13,14,22,32]` 和 `[40,21,48,4,47,8,37,18]` ,之后继续细分下去直至每个子数组只有一个元素。 #### 2. 解决(Conquer) 当所有数组都被拆分成单元素状态的时候,它们自然是“已经排序”的状态。(因为单个元素无所谓先后顺序) #### 3. 合并(Combine) 这是最关键的一步。“合并”操作实际上是在不断对比相邻两个数组中的数值大小,并依次排列形成新的更大的有序数组,最终达到完全排好序的目的。 如上所示,我们通过多次循环完成了左右两块区域值之间的相互插入工作直到全部数据都按照从小到大进行了整理为止。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值