• 分治法与递归编程步骤


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

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

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

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

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

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

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

    (2)考虑如何分解原问题。对于某些复杂的问题,会考虑设计一个函数来解决,此时也要设计好该函数的原型,入参、出参和返回值等。

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

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

    (5)处理基本情况。

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

    例子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); //调用写好的函数来合并数组的前后两部分
        }  
    }
  • 相关阅读:
    mysql原生语句基础知识
    利用layui前端框架实现对不同文件夹的多文件上传
    简述layui前端ui框架的使用
    利用bootstrap-select.min.js实现bootstrap下拉列表的单选和多选
    使用pycharm进行远程开发部署调试设置 与 远程部署调试是否必须使用远程主机的解释器?
    博客园积分规则
    mysql 数据库的备份与恢复
    flask 利用flask_wtf扩展 创建web表单
    jquery ajax几种书写方式的总结
    LightSpeed 的Left Join Bug解决方案
  • 原文地址:https://www.cnblogs.com/tsiangleo/p/5263676.html
Copyright © 2020-2023  润新知