2.3.1分治法(算法导论 学习)

递归 :为了解决一个给定的问题 算法一次或多次递归的调用其自身以解决紧密相关的若干子问题

好吧 还是没太理解清楚 是啥 百度百科给的解释

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

嗯 我觉得有道理的一句是 递归是一种程序调用自身的编程技巧 (一种技巧)在于用有限的语句来定义对象的无限集合。。。
呃 好玄 不过也是这样子 递归是一种while for 之类的循环 比较高级(抽象的)循环语句 可以说 while for 都可以用递归来同等的代替但是 有的递归不太好用while for 循环代替 尤其是 过程比较抽象的时候 比如汉诺塔问题。。。
好了 递归先扯到这 毕竟我也没比较认真的学到 来说今天看到的重点

分治法

将原问题分解为几个规模较小但类似于原问题的子问题 递归地求解这些子问题 然后合并这些子问题的解
三个步骤
分解:分解原问题为若干子问题
解决 这些自问题 复杂就递归 简单就去直接求解
合并 这些子问题

举个例子 归并排序

MergeSort(A,p,r) if p<r  q=(p+r)/2 MergeSortA,p,qMergeSortA,q,r) MergeA,p,q,r) Merge(A,p,q,r) n1=q-p+1 n2=r-q let L[1,n1+1] and R[1,n2+1] be new arrays for i=1 to n1 L[i]=A[p+i-1] for j=1 to n2 R[j]=A[q+j] L[n1+1]=无穷大 R[n2+1]=无穷大 i=1 j=1 for k=p to r if L[i]<R[j] A[k]=L[i] i++ else A[k]=R[j] j++ 

普及一下英语 merge 中文 融合 很好理解 merge的过程 初始化一下 把小的放入A中 (注意 L R 都已经按升序排好了 )ok
划重点 分治法的三步骤 划分 解决 合并 在本算法里

分解:分解待排序的n个元素的序列 成个具有n/2的两个子序列
解决:使用递归即调用自己来解决两个子序列
合并 :调用merge 算法来合并两个排好序的子序列
当待排序序列的长度为一时开始递归“开始回升”,因为 一个 即 排好序了

哇 好强的感觉 对了为了帮助自己理解递归的过程可以去画一个 二叉树 每一层为一种递归状态 即 参数的值是一样的环境下的 但递归一下时 就进入了下一层 即 往下面长出了一个树枝
上图

好的就是这样子啦
至于递归算法时间的分析 其实蛮简单的数学过程 不细讲了 后面有章 再说
看一下思考题 2.3-5
二分查找 算法
上一下 我自己写的 有点繁琐

int binary_search(int *A, int a, int begin, int end)
{
    int mid = (begin + end) / 2;
    if (begin < end)
    {
        if (a > A[(begin + end) / 2])
            binary_search(A, a, mid, end);
        else if (a < A[mid])
            binary_search(A, a, begin, end);
        else if (a == A[mid])
            return mid;
    }
    else
    {
        if (a == A[mid])
            return mid;
        else
            return -1;
    }
}

呃看看官方的答案 递归写的是这样子的

int RecursiveBinarySearch(int *A, int v, int low, int high)
{
    if (low > high)
        return -1;
    int mid = (low + high) / 2;
    if (v == A[mid])
        return mid;
    else if (v > A[mid])
        return RecursiveBinarySearch(A, v, mid + 1, high);
    else
        return RecursiveBinarySearch(A, v, low, mid - 1);
}

哇 简介美啊啊啊啊
学习一下了
第一部分
判断 递归终止 的条件
第二部分 解决问题
简单的情况 return mid
复杂的情况return 自身来解决
好了好了看另一种 用while 循环来解决的

两相对比一下 可以稍稍理解一下递归和while 循环的关系啦
为了 加深一下子理解
第一个例子 汉诺塔问题
关于这个例子有很多很多的人说了
懒得说了 不过是没有归并步骤的 分治法
第二个是书上的一个 题2-4(逆序对)

啊 第一二三问想一想就ok了 不是难题
问题是第四问啥子意思 想了好久 还是看了答案
感觉关键 在于修改归并 排序 由第三问的提示可以感觉到一些东西 就是 举个列子还是
有两堆牌 对于第二堆 牌来说 无论第一堆牌的顺序是如何的 第二堆相对第一堆的逆序对的个数是不会有变化的 (所以 将第一堆的牌安照 升序排列一下 不就 更加方便了)而且 假设在之前的递归途中 第一二堆的逆序对个数已经求出来了 那么 此时只有还有 将两堆合在一起的逆序对个数没有 计算 那么 算法就顺理成章的出来了 神奇
附图

inversions 逆序对的意思 count 计算
递归第一段 循环判断 if 。。。
第二段就是拆分为两个部分 分别求逆序对的个数
重点在于mergeInversions

其他都好说 初始化呗
这里注意一下 counted 这个变量 英文意思为数过的(数的过去式)
看循环 是从 p到r
分情况 第一 没有数过 而且 左边的牌堆比右边的牌堆大 即满足逆序对的要求 注意 另一点 左右的牌堆已经排好序了。。所以 inversions是inversions (原有的)加上n1-i+1 即 因为是排序的那么如果L[i]大于R[j] 那么L[i+n]大于L[i]即大于R[j]即 对于R[j]来说 有这么多个逆序对 并且为啥要设置counted呢 先存一下疑问 一会上代码跑一下试试再说 然后R[j]就没有利用价值了 排序到A里 然后
进行下一个循环
总结一下 就是 比较 L 与 R 里较小的那个 然后如果L 小于 R 就把L写入A中 如果R 小于L即找到了一组 计算一下 然后再把R 写入到A中 ojbk了
写下代码试试

int mergeInversions(int *A, int p, int q, int r)
{
    int n1 = q - p+1;//q-p 为 q与p中间间隔几个 +1 是算上q 即求从p到q有几个
    int n2 = r - q;//之所以不用加一是因为 n1已经包含了q 求r q间隔几个就ok了
    int *L = new int[n1+1];
    int *R = new int[n2 + 1];
    for (int i = 0; i < n1; i++)
        L[i] = A[p + i];//因为i是从0 开始的所以不用减一
    for (int i = 0; i < n2; i++)
        R[i] = A[q + i+1];//还是因为i 是从0开始的 又因为q已经包含于L里了所以加一
    L[n1] = INT_MAX;
    R[n2] = INT_MAX;
    int i = 0;
    int j = 0;
    int inversions = 0;
    for(int k=p;k<=r;k++)
        if (R[j] < L[i])
        {
            inversions = inversions + n1 - i;
            A[k] = R[j];
            j++;
        }
        else
        {
            A[k] = L[i];
            i++;
        }
    return inversions;
}
int  countInversions(int *A, int p, int r)
{
    int inversions = 0;
    if (p < r)
    {
        int q = (p + r) / 2;
        inversions = inversions + countInversions(A, p, q);
        inversions = inversions + countInversions(A, q + 1, r);
        inversions = inversions + mergeInversions(A, p, q, r);
    }
    return inversions;
}

不用 counted 也行

全部评论

相关推荐

06-27 12:54
已编辑
门头沟学院 Java
累了,讲讲我的大学经历吧,目前在家待业。我是一个二本院校软件工程专业。最开始选专业是觉得计算机感兴趣,所以选择了他。本人学习计算机是从大二暑假结束开始的,也就是大三开始。当时每天学习,我个人认为Java以及是我生活的一部分了,就这样持续学习了一年半,来到了大四上学期末,大概是在12月中旬,我终于找的到了一家上海中厂的实习,但我发现实习生的工作很枯燥,公司分配的活也不多,大多时间也是自己在自学。就这样我秋招末才找到实习。时间来到了3月中旬,公司说我可以转正,但是转正工资只有7000,不过很稳定,不加班,双休,因为要回学校参加答辩了,同时当时也是心高气傲,认为可以找到更好的,所以放弃了转正机会,回学校准备论文。准备论文期间就也没有投递简历。然后时间来到了5月中旬,这时春招基本也结束了,然后我开始投递简历,期间只是约到了几家下场面试。工资也只有6-7k,到现在我不知道该怎么办了。已经没有当初学习的心劲了,好累呀,但是又不知道该干什么去。在家就是打游戏,boss简历投一投。每天日重一次。26秋招都说是针对26届的人,25怎么办。我好绝望。要不要参加考公、考研、央国企这些的。有没有大佬可以帮帮我。为什么感觉别人找工作都是顺其自然的事情,我感觉自己每一步都在艰难追赶。八股文背了又忘背了又忘,我每次都花很长时间去理解他,可是现在感觉八股、项目都忘完了。真的已经没有力气再去学习了。图片是我的简历,有没有大哥可以指正一下,或者说我应该走哪条路,有点不想在找工作了。
码客明:太累了就休息一下兄弟,人生不会完蛋的
如果实习可以转正,你会不...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务