聊聊分治思想与归并排序

324 阅读4分钟

1、介绍:

分治是一种算法设计的思想,将大问题分解成多个小问题,例如归并排序将大问题:「排序整个数组」,分解为小问题:「排序左半和右半」;绝大部分情况下「分治算法」通过「递归」实现。即:子问题的求解通过递归方法实现。

算法和数据结构并不是凭空想象出来的,「递归」函数也不例外。「递归」函数基于 「自顶向下」拆分问题,再「自底向上」逐层解决问题的思想设计而成,这是所熟知的「分而治之」的算法思想。

2、分治与递归的解题思路:

分治算法一般通过递归实现

(1)分而治之(Divide-and-Conquer)的思想分为如下三步:

拆分:将原问题拆分成若干个子问题;

解决:解决这些子问题;

合并:合并子问题的解得到原问题的解。

(2)这样的三步恰好与递归的程序写法相吻合:

拆分:即对当前的大问题进行分析,写出相应代码,分解为子问题。

解决:即通过递归调用解决子问题;

合并:即在回溯的过程中,根据递归返回的结果,对子问题进行合并,得到大问题的解。

3、对分治思想的理解

更加形象的一种说法是:一开始我们只有一个问题,我们通过分治,将其分解成多个问题,发散开来,“自顶向下”走出去。在解决完子问题之后,在回溯的过程中合并子问题的解,将发散开来的问题合并成一个,“自底向上”走回来。

 典型的分治思想的应用是:归并排序、快速排序、绝大多数「树」中的问题(先把原问题拆分成子树的问题,当子树中的问题解决以后,结合子树求解的结果处理当前结点)(通过修改后的子树来对当前节点进行修改的过程)、链表中的问题。

4、分治与减治思想:

分治思想是将大问题转换为几个小问题,然后将他们的结果合并。而减治思想就是将一个大问题变成一个小问题,小问题的结果就是整个结果,不用合并。

「分治思想」的特例是「减治思想(Decrease-and-Conquer)」:每一步将问题转换成为规模更小的子问题。「减治思想」思想的典型应用是「二分查找」「选择排序」「插入排序」「快速排序」算法

「分治」与「减治思想」的区别如下:

分治思想:将一个问题拆分成若干个子问题,然后再逐个求解,根据各个子问题得到的结果得到原问题的结果;

减治思想:在拆分子问题的时候,只将原问题转化成 一个 规模更小的子问题,因此子问题的结果就是上一层原问题的结果,每一步只需要解决一个规模更小的子问题,相比较于「分治思想」而言,它没有「合并」的过程

5、归并排序:

思路:

先对将长序列拆分成短序列,对每个短序列内部排序以后,合并其他短序列,还原成排序好的长序列。

步骤:

1、把长度为n的输入序列分成两个长度为n/2的子序列;

2、对这两个子序列分别采用归并排序;

3、将两个排序好的子序列合并成一个最终的排序序列。

而将两个的有序数列合并成一个有序数列,我们称之为"归并",这就是归并排序名字的由来。

image.png

代码实现:

#include <iostream>   
using namespace std;

int nums[7] = { 7,3,5,2,9,8 };//待排序数组
int temp[7];//临时存储数组

void merge(int low,int mid,int high) {

    int left = low; //左边部分数组指针
    int right = mid + 1; //右边部分指针
    int k = low; //对temp数组进行操作的指针

    while (left < mid + 1 && right < high + 1) {
    
        if (nums[left] > nums[right]) {
            temp[k++] = nums[right++];
        }
        
        else {
            temp[k] = nums[left];
            k++;
            left++;
        }
    }

    //查看左边序列是否为空
    while (left < mid + 1) {
        temp[k++] = nums[left++];
    }

    //查看右边序列是否为空
    while (right < high + 1) {
    temp[k++] = nums[right++];
    }

    //移动回原数组
    for (int i = low; i <= high; i++) { //这里要i=low阿,不可以等于0
    nums[i] = temp[i];
    }
}

 

void mergeSort(int low,int high) {

    if(low >= high) {
        return;
    }
    
    //防止low和high太大导致越界,>>1和除以2一样,不过比/2效率快
    int mid = low + ((high - low) >> 1);

    //分
    mergeSort(low, mid);
    mergeSort(mid + 1, high);

    //治
    merge(low, mid, high);
}

 

int main() {

    mergeSort(0, 5);

    for (int i = 0; i <=5; i++) {
        cout << nums[i] << " ";
    }

    cout << endl;
    return 0;
    
}