分治法与递归编程步骤

分治法是一种很强大的算法设计方法。基本思想是:将原问题分解为几个规模小但类似于原问题的子问题,递归的求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

在分治策略中,递归地求解一个问题,在每层递归中应用如下三个步骤:

(1)分解(Divide):将原问题分解为一些子问题,子问题的形式与原问题一样,只是规模更小。

(2)解决(Conquer):递归地解出子问题。如果子问题规模足够小,则停止递归,直接求解。

(3)合并(Combine):将子问题的解组合成原问题的解。

分治思想体现在编码上,往往就是递归的形式。实际在编码的时候,可以遵循如下步骤:

(1)精心设计函数原型,包括入参、出参和返回值等。

(2)调用(1)中的函数处理各个子问题,并假设子问题已经解决了。

(3)处理子问题合并的具体细节。

(4)处理基本情况。

(5)将(4)中的代码移到函数体的前面,整理代码结构。

例子1:递归版插入排序

为了排序A[1..n],我们递归地排序A[1..n-1],然后把A[n]插入已排序的数组A[1..n-1]。

第一步:设计函数原型

void my_insertion_sort(int a[], int left, int right) //将数组a[left...right]之间的元素排序,left和right都是下标,从0开始取值.
{
}

第二步:调用函数解决子问题

void my_insertion_sort(int a[], int left, int right) //将数组a[left, ...right]之间的元素排序,left, 和right都是下标,从0开始取值。
{
    my_insertion_sort(a, left, right - 1); //调用函数给数组a[left .. right-1]之间的元素排序

    //数组a[left .. right-1]已经排好序了,接下来就是将a[right]插入到a[left .. right-1]中的适当位置了。
}

第三步:合并子问题

接下来要编写的代码就是将a[right]插入到a[left .. right-1]中的适当位置,如下:

void my_insertion_sort(int a[], int left, int right) //将数组a[left, ...right]之间的元素排序,left, 和right都是下标,从0开始取值。
{
    my_insertion_sort(a, left, right - 1); //调用函数给数组a[left .. right-1]之间的元素排序

    //将a[right]插入到a[left .. right-1]中
    int j = right - 1;
    int temp = a[right];
    while (j >= left && temp < a[j])
    {
        a[j + 1] = a[j];
        --j;
    }
    a[j + 1] = temp;
    //至此,a[right]已经插入到a[left .. right-1]中的适当位置了
}

第四步:处理基本情况

查看函数原型void my_insertion_sort(int a[],int left,in right); 发现当left等于right的时候,待排序区间就一个元素,直接返回。

void my_insertion_sort(int a[], int left, int right) //将数组a[left, ...right]之间的元素排序,left, 和right都是下标,从0开始取值。
{
    my_insertion_sort(a, left, right - 1); //调用函数给数组a[left .. right-1]之间的元素排序//将a[right]插入到a[left .. right-1]中
    int j = right - 1;
    int temp = a[right];
    while (j >= left && temp < a[j])
    {
        a[j + 1] = a[j];
        --j;
    }
    a[j + 1] = temp;
    //至此,a[right]已经插入到a[left .. right-1]中的适当位置了

    //当left == right的时候就是基本情况,此时就直接返回了。
    if(left == right)
       return;
}

第五步:优化代码结构,将第四步的代码移到前面。

void my_insertion_sort(int a[], int left, int right) //将数组a[left, ...right]之间的元素排序,left, 和right都是下标,从0开始取值。
{
    //当left == right的时候就是基本情况,此时就直接返回了。
    if(left == right)
        return;

    my_insertion_sort(a, left, right - 1); //调用函数给数组a[left .. right-1]之间的元素排序//将a[right]插入到a[left .. right-1]中
    int j = right - 1;
    int temp = a[right];
    while (j >= left && temp < a[j])
    {
        a[j + 1] = a[j];
        --j;
    }
    a[j + 1] = temp;
    //至此,a[right]已经插入到a[left .. right-1]中的适当位置了
}

或者最好是

void my_insertion_sort(int a[], int left, int right) //将数组a[left, ...right]之间的元素排序,left, 和right都是下标,从0开始取值。
{
    if(left < right)    //处理边界条件
    {
        my_insertion_sort(a, left, right - 1); //调用函数给数组a[left .. right-1]之间的元素排序//将a[right]插入到a[left .. right-1]中
        int j = right - 1;
        int temp = a[right];
        while (j >= left && temp < a[j])
        {
            a[j + 1] = a[j];
            --j;
        }
        a[j + 1] = temp;
        //至此,a[right]已经插入到a[left .. right-1]中的适当位置了
    }

}

例子2:归并排序

归并排序就是分治思想的典型例子。

第一步:设计函数原型:

比如要将数组a[left...right]之间的元素排序,可以设计如下原型:

void my_merge_sort(int a[],int left,in right); // left和right分别是待排序区间的左右下标,取值从0开始。
{

}

第二步:调用函数解决子问题:

我们将待排序区间分成两部分,并在这两部分上调用我们的函数解决它。

void my_merge_sort(int a[],int left,in right); // left和right分别是待排序区间的左右下标,取值从0开始。
{
    int mid = ( left + right ) / 2;    //将待排序数组均分为两部分,递归解决
    my_merge_sort(a,left,mid);        //给数组的前半部分排序
    my_merge_sort(a,mid+1,right);    //给数组的后半部分排序

    //数组的前、后半部分都已经排好序了,接下来就是合并了。
}

第三步:合并子问题

设计一个函数merge(int a[],int l,int m,int r)来处理两个已排序数组的合并问题,这里不给出实现。

void my_merge_sort(int a[],int left,in right); // left和right分别是待排序区间的左右下标,取值从0开始。
{
    int mid = ( left + right ) / 2;    //将待排序数组均分为两部分,递归解决
    my_merge_sort(a,left,mid);      //给数组的前半部分排序
    my_merge_sort(a,mid+1,right); //给数组的后半部分排序
    merge(a,left,mid,right); //调用写好的函数来合并数组的前后两部分
}

第四步:处理基本情况

对于之前设计的排序函数的原型:void my_merge_sort(int a[],int left,in right); 当left等于right的时候,待排序区间就一个元素,直接返回。

void my_merge_sort(int a[],int left,in right); // left和right分别是待排序区间的左右下标,取值从0开始。
{
     int mid = ( left + right ) / 2;    //将待排序数组均分为两部分,递归解决
    my_merge_sort(a,left,mid);      //给数组的前半部分排序
    my_merge_sort(a,mid+1,right); //给数组的后半部分排序

    merge(a,left,mid,right); //调用写好的函数来合并数组的前后两部分

    //当left == right的时候就是基本情况,此时就直接返回了。
    if(left == right)
        return;
}

第五步:优化代码结构,将第四步的代码移到前面。此时的代码结构如下:

void my_merge_sort(int a[],int left,in right); // left和right分别是待排序区间的左右下标,取值从0开始。
{
    //当left == right的时候就是基本情况,此时就直接返回了。
    if(left == right)
        return;

    int mid = ( left + right ) / 2;    //将待排序数组均分为两部分,递归解决
    my_merge_sort(a,left,mid);      //给数组的前半部分排序
    my_merge_sort(a,mid+1,right); //给数组的后半部分排序

    merge(a,left,mid,right); //调用写好的函数来合并数组的前后两部分

}

或者最好是

void my_merge_sort(int a[],int left,in right); // left和right分别是待排序区间的左右下标,取值从0开始。
{
    if(left < right)
    {
        int mid = ( left + right ) / 2;    //将待排序数组均分为两部分,递归解决
        my_merge_sort(a,left,mid);      //给数组的前半部分排序
        my_merge_sort(a,mid+1,right); //给数组的后半部分排序

        merge(a,left,mid,right); //调用写好的函数来合并数组的前后两部分
    }
}
时间: 2024-12-28 00:53:04

分治法与递归编程步骤的相关文章

分治法的理解

什么是分治法? 分治法的基本思想是将一个难以直接解决的大问题,分解成一些规模较小的相同问题,以便各个击破,分而治之. 何时能,何时用分治法来解决这些问题比较好呢? 这些问题应当具备这几个特征: (1)问题的规模缩小到一定程度就可以容易的解决了. (2)问题可以分解为若干个规模较小的相同子问题. (3)问题所分解成各个子问题是相互独立的,即子问题之间不包含公共的子问题. (4)问题分解出的子问题的解可以合并为原问题的解. 上述的第一条特征是绝大多数问题可以满足的,因为问题的计算复杂性一般是随着问题

分治法——算法总结二

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同.求出子问题的解,就可得到原问题的解. 分治法解题的一般步骤: (1)分解,将要解决的问题划分成若干规模较小的同类问题: (2)求解,当子问题划分得足够小时,用较简单的方法解决: (3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解. 简而言之,分治法的设计思想就是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之. 问题分析:以归并排序为例子,将待排

【分治法】最接近点对问题(转)

转自:http://blog.csdn.net/liufeng_king/article/details/8484284 问题场景:在应用中,常用诸如点.圆等简单的几何对象代表现实世界中的实体.在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息.例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点.这类问题是计算几何学中研究的基本问题之一. 问题描述:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,

算法笔记_003:矩阵相乘问题【分治法】

目录 1 问题描述  1.1实验题目 1.2实验目的 1.3实验要求 2 解决方案 2.1 分治法原理简述 2.2 分治法求解矩阵相乘原理 2.3 具体实现源码 2.4 运算结果截图 1 问题描述 1.1实验题目 设M1和M2是两个n×n的矩阵,设计算法计算M1×M2 的乘积. 1.2实验目的 (1)提高应用蛮力法设计算法的技能: (2)深刻理解并掌握分治法的设计思想: (3)理解这样一个观点:用蛮力法设计的算法,一般来说,经过适度的努力后,都可以对其进行改进,以提高算法的效率. 1.3实验要求

动态规划和分治法,贪心算法以及递归的再一次深刻理解和体会

每次体会算法都有新的感觉,刷题越多,对算法的理解感觉也就越深刻. 下面我们来重新体会下分治法,动态规划,贪心法,递归的理解. 1.分治法: 将问题分成单独的阶段,每个阶段互相不干扰很独立,如10米长的木棍,切成10段,每段去解决每一段的问题.(阶段没有关系) 2.贪心法 站在全局的角度,也是将问题堪称分为多个阶段,只不过阶段和阶段之间有一定的递进关系,如从5毛,1元,2毛,1毛,2元中,去找最少的钱币构成10块钱.首先是站在全局的角度,先从中取其最大值,为第一阶段,然后在从剩余的当中在找最大值,

递归与分治法

分治法思想 把问题分解为k个规模较小的子问题,这些子问题(互相独立且)结构与原来问题的结构相同,再递归地求解这些子问题. 问题分解成子问题:(divide) 当达到某个阈值n0时,给出直接求解的方法:(conquer) 最后把各个子问题的解合并起来,得到原来问题的解:(merge) 算法设计伪代码 时间复杂性 分析分治法的运行时间,先列出递归方程,例如 典型例子 mergesort等 原文地址:https://www.cnblogs.com/eniac1946/p/8733531.html

二叉树 + 递归 + 分治法总结

二叉树递归相关题目的时间复杂度基本上都是O(n) = 一共有n个点 + 每个点的时间复杂度(1) 而二叉树分治法最坏的时间复杂度为O(n^2) 时间复杂度:T(n) = 2T(n/2) + O(1) = O(n) Merge Sort, Quick Sort: T(n) = 2T(n/2) + O(n) = O(nlogn) 前序遍历: 解法一:递归,通常是设置一个全局变量来保存结果. /** * Definition for a binary tree node. * struct TreeN

算法中的递归分析和分治法的原理

分析递归算法三种方法 替换法.迭代法.通用法(master method) 作用:分析递归算法的运行时间 分治算法 将一个问题分解为与原问题相似但规模更小的若干子问题,递归地解这些子问题,然后将这些子问题的解结合起来构成原问题的解.这种方法在每层递归上均包括三个步骤: divide(分解):将问题划分为若干个子问题 conquer(求解):递归地解这些子问题:若子问题Size足够小,则直接解决之 Combine(组合):将子问题的解组合成原问题的解 其中的第二步很关键:递归调用或直接求解  (递

专题:分治法

分治法(Divide and Conquer) 作为五大算法之一的分治法,可算是最早接触的一种算法.分治法,与其说是一种算法,不如将其称为策略来的更贴切一些.算法的思想就是将大问题分成小问题,并解决小问题之后合并起来生成大问题的解. 分治法的精髓: 分--将问题分解为规模更小的子问题: 治--将这些规模更小的子问题逐个击破: 合--将已解决的子问题合并,最终得出“母”问题的解: 分治法的作用,自然是让程序更加快速地处理问题.比如一个n的问题分解成两个n/2的问题,并由两个人来完成,效率就会快一些