采用动态规划的思想解决三种经典问题之Java实现

当遇到复杂问题时我们经常会通过递归的方式将大事化小,小事化了。但是有时候将复杂问题简单地分解成几个子问题,问题求解耗时会按问题规模呈幂级数增加。这时候为了节约重复求相同子问题的时间,引入一个数组(或临时变量),不管它们是否对最终解有用,把所有子问题的解存于该数组(或临时变量)中,这就是动态规划的基本思想。

1.Fibonacci数列

该数列的递归形式如下,即从每二项开始每一项等于前两项之和:

根据定义可以用递归方式写出如下代码:

public int fib(int n) { // 计算Fibonacci数列的第n项(二分递归版):O(2^n)
        return (2 > n) ? n // 若到达递归基,直接取值
                : fib(n - 1) + fib(n - 2); // 否则,递归计算前两项,其和即为正解
}

递规版本的时间复杂度将呈指数级别,明显不能很好的解决问题,现通过牺牲空间复杂度,利用动态规划的思想将时间复杂度控制降低到O(n).

public static int fib1(int n) { // 计算Fibonacci数列的第n项(动态规划版):O(n)
        if(n < 2) return n;
        int a = 0;//第一个值
        int b = 1;//第二个值
        int tmp = 0;//辅助变量
        for(int i = 2;i <= n;i++){
            tmp = a + b;
            a = b;
            b = tmp;
        }
        return tmp;     
    }

2.爬楼梯

问题描述:有一座高度为n级的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶,求出一共有多少种走法。

分析:因为每1步只能走1级或者2级,所以走到第n级的前一步有且只有两种情况,第一种情况是从第n - 1级走1级,第二种情况是从第n - 2级走2级。由此我们就可以得到此问题的递归公式:

F(1) = 1;
F(2) = 2;
F(n) = F(n - 1) + F(n - 2);

看到这里,相信聪明的你很快就反映过来了这不就是上面的Fibonacci数列问题嘛(注意:起始值不同),下面直接给出动态规划思想的代码实现:

public static int getNum(int n) { 
        if(n <= 2) return n;
        int a = 1;//只有1级台阶的情况
        int b = 2;//有2级台阶的情况
        int tmp = 0;//辅助变量
        for(int i = 3;i <= n;i++){
            tmp = a + b;
            a = b;
            b = tmp;
        }
        return tmp;     
    }

3.最长公共子序列

有了上面两个动态规划实现的热身,下面看一个复杂点的问题,首先看一下两个定义:
子序列(Sequence):由序列中若干字符,按原相对次序构成。

最长公共子序列(Longest Common Subsequence):两个序列公共子序列中的最长者,下面简称LCS,可能出现下面两种比较复杂的情况:
1).可能有多个

2).可能有歧义

现用递归的思想分析如下:
对于序列A[0,n]和B[0,m],LCS(A,B)无非三种情况:

  1. 若n = - 1或 m = - 1,则取作空序列("")
  2. 若A[n] = ‘X' = B[m],则取作LCS(A[0,n), B[0,m)) + 'X',示意图如下:
  1. 若A[n] != B[m],则在LCS(A[0,n], B[0,m))与LCS(A[0,n), B[0,m])中取更长者
    示意图如下:


如下图所示,展示了“EDUCATIONAL”和“ADVANTAGE”通过递归思想求最长公共子序列的过程。


代码实现如下:

    /**
     * 递归方式实现求两个字符串最长公共字序列的长度
     * @param str1 第一个字符串
     * @param m 第一个字符串需要比较的长度
     * @param str2 第二个字符串
     * @param n 第一个字符串需要比较的长度
     * @return
     */
    public static int lcs(String str1,int m,String str2,int n){
        if(m == 0 || n == 0) return 0;//如果其中有一个元素为空则返回0
        if(str1.charAt(m - 1) == str2.charAt(n - 1)) 
            return lcs(str1,m - 1,str2,n - 1) + 1;//如果需要比较的两个字符串最后一个字符相同则将问题缩小
        //剩下的情况则两个字符串的最后一个字符不相等,取两种情况中的最大值
        return getMax(lcs(str1,m - 1,str2,n),lcs(str1,m,str2,n - 1));
    }

可以看出如果最后一个字符不相等,反而会将问题的规模扩大,当m与n大小接近时,时间复杂度也呈指数级别。下面将考虑通过动态规划的思想来实现:
构造一个二维辅助数组,从上往下依次计算,如下图所示:

代码实现如下:

    /**
     * 非递归方式实现求两个字符串最长公共字序列的长度
     * @param str1 第一个字符串
     * @param m 第一个字符串需要比较的长度
     * @param str2 第二个字符串
     * @param n 第一个字符串需要比较的长度
     * @return
     */
    public static int lcs1(String str1,int m,String str2,int n){
        if(m == 0 || n == 0) return 0;
        //构建一个m + 1行 n + 1列的辅助二维数组,里面的元素初始值都为0
        int[][] arr = new int[m + 1][n + 1];
        //依次求二维数组中的值
        for(int i = 1;i <= m;i ++){
            for(int j = 1;j <= n; j ++){
                //当最后一个字符相等时等于左上角的元素加1
                if(str1.charAt(i - 1) == str2.charAt(j - 1)) arr[i][j] = arr[i - 1][j - 1] + 1;
                //不相等时取紧邻左边元素和上边元素者的大者
                else arr[i][j] = getMax(arr[i - 1][j],arr[i][j - 1]);
            }
        }
        return arr[m][n];//二维数组右下角的元素即我们需要的值
    }

总结:动态规划的思想通过牺牲空间复杂度的方式来大大减小时间复杂度,将自顶而下的计算方式改为自底而上,把已计算过的实例结果制表备查,从而取得非常显著的效果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容