当前位置:   article > 正文

(四)分而治之策略——算法设计与分析

分而治之策略


一、分而治之

1.1 思想

我们在求解一些复杂问题的时候,因为问题规模较大,难以理清思路。自然便会想到,把一个复杂、大规模的问题拆解成若干个简单、小规模的问题

拆解得到的小问题与大问题的本质相同,但是对于小问题,我们更容易看清它的本质,更容易求解。这便是分而治之策略的思想。


1.2 框架

基于分而治之思想设计的算法一般有以下3个步骤组成:

分而治之一般步骤:

  1. 分解原问题:将原问题分解为若干个不相交的子问题
  2. 解决子问题:递归地求解各个子问题
  3. 合并问题解:将子问题的解合并为原问题的解

因此,其时间复杂度分为3个部分:划分时间;求解时间;合并时间。

1.3 适用情况

在我们自己设计算法的时候,可以根据问题的特征来设计分治算法

分治算法能解决的问题的特征:

  1. 问题规模缩小到一定程度可以很容易解决
  2. 该问题具有最优子结构性质(即问题可以分解为若干个规模较小的相同问题)
  3. 子问题之间不交叉(即分解出的子问题之间是相互独立的)
  4. 分解出的子问题的解可以合并为该问题的解

二、应用

分而治之的精髓在于,针对不同的问题,巧妙地设计分与合的策略,可以取得令人满意的效果。

其中很典型的两个例子便是归并排序与快速排序,一个侧重分,一个侧重合。通过这两种算法的学习,可以更深刻地体会到分而治之思想的妙处。

推荐大家观看北航童咏昕教授的课程,简洁明了,清晰易懂。

2.1 归并排序

视频链接说明
归并排序分而治之经典算法,侧重于
最大子数组问题基于归并排序的应用
逆序对计数问题基于归并排序的应用

归并排序,归指递归,并指合并。

基于分而治之思想设计的算法都要使用到递归,因此必须掌握对递归式的时间复杂度分析,可以参考我之前的一篇博客,介绍了4种分析递归式时间复杂度的方法,简洁易懂易用。

依据童教授的讲解,这里给出我自己实现的归并排序代码(C语言),水平有限,仅供参考学习。

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>

void MergeSort(int *arr, int low, int high);
void Merge(int *arr, int low, int mid, int high);

int main()
{
    int i;
    int arr[] = {70, 25, 23, 55, 31, 20, 22, 13, 62, 81, 50, 74, 65, 84, 80, 66, 64, 42, 12, 40};
    MergeSort(arr, 0, 19);
    for (i = 0; i < 20; i++)
        printf("%d ", arr[i]);
    system("pause");
    return 0;
}

void MergeSort(int *arr, int low, int high)
{
    if (low >= high)
        return;
    int mid = (low + high) / 2;
    MergeSort(arr, low, mid);
    MergeSort(arr, mid + 1, high);
    Merge(arr, low, mid, high);
}

void Merge(int *arr, int low, int mid, int high)
{
    int i, j, k;
    int *assist = (int *)malloc(sizeof(int) * (high + 1)); // 辅助数组,将两部分copy过来
    for (k = low; k <= high; k++)
        assist[k] = arr[k];
    k = low;
    i = low;
    j = mid + 1;
    while (i <= mid && j <= high)
    {
        if (assist[i] <= assist[j])
            arr[k] = assist[i++];
        else
            arr[k] = assist[j++];
        k++;
    }
    while (i <= mid)
        arr[k++] = assist[i++];
    while (j <= high)
        arr[k++] = assist[j++];
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

2.2 快速排序

视频链接说明
快速排序分而治之经典算法,侧重于
次序选择问题基于快速排序的应用

快速排序有多种实现方式,但重要的是理解其中的思想,关于其具体实现方式,大家可以自行查阅别的资料。

依据童教授的讲解,这里给出我自己实现的快速排序代码(C语言),水平有限,仅供参考学习。

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

void QuickSort(int *arr, int low, int high);
int QuickSort_Partition(int *arr, int low, int high);
void QuickSort_RandomPivot(int *arr, int low, int high);

int main()
{
    int i;
    int arr[] = {70, 25, 23, 55, 31, 20, 22, 13, 62, 81, 50, 74, 65, 84, 80, 66, 64, 42, 12, 40};
    QuickSort(arr, 0, 19);
    for (i = 0; i < 20; i++)
        printf("%d ", arr[i]);
    system("pause");
    return 0;
}

void QuickSort(int *arr, int low, int high)
{
    if (low < high)
    {
        int pivot = QuickSort_Partition(arr, low, high);
        QuickSort(arr, low, pivot - 1);
        QuickSort(arr, pivot + 1, high);
    }
}

int QuickSort_Partition(int *arr, int low, int high)
{
    int i, j; // i作为分水岭,比主元小的在i左侧,比主元大的在右侧,i+1为第一个比主元大的
    int temp;
    QuickSort_RandomPivot(arr, low, high); // 随机选取主元
    int pivot = high;                      // 选取主元
    i = low - 1;                 // 初始化i为low-1,j为low
    for (j = low; j < high; j++) // 遍历所有元素
    {
        if (arr[j] <= arr[pivot]) // 当前元素比主元小,和i+1交换下
        {
            temp = arr[j];
            arr[j] = arr[i + 1];
            arr[i + 1] = temp;
            i++; // i右移
        }
    }
    temp = arr[pivot]; // 把主元放在中间做分界线
    arr[pivot] = arr[i + 1];
    arr[i + 1] = temp;
    return i + 1;
}

void QuickSort_RandomPivot(int *arr, int low, int high)
{
    srand((unsigned)time(NULL));
    int num = rand();
    int pivot = low + num % (high - low + 1);
    int temp;
    temp = arr[high];
    arr[high] = arr[pivot];
    arr[pivot] = temp;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

2.3 其他应用

基于分而治之的算法有很多,这里列举几个,感兴趣的读者可以自行查阅。

  • 大整数乘法
  • 矩阵乘法
  • 快速傅里叶变换
  • 线性时间选择算法
  • 最邻近点对
  • 凸包算法
  • 数据剪除方法
  • 排序与线性时间排序
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/592811
推荐阅读
相关标签
  

闽ICP备14008679号