总结:动态规划部分

动态规划是面试中的热门话题,值得总结。

在LeetCode中比较典型的动态规划题目,及其java代码如下:

1.[LeetCode 53] Maximum Subarray 最大子数组

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.

这个题和《剑指offer》中42.连续子数组的最大和是同一个题目,因此相当经典。
代码如下:

public class Solution {
    public int maxSubArray(int[] nums) {
        int res = Integer.MIN_VALUE, curSum = 0;
        for (int num : nums) {
            curSum = Math.max(curSum + num, num);
            res = Math.max(res, curSum);
        }
        return res;
    }
}
2.[LeetCode 70] Climbing Stairs 爬楼梯问题

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.

Example 1:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.

  1. 1 step + 1 step
  2. 2 steps

Example 2:

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.

  1. 1 step + 1 step + 1 step
  2. 1 step + 2 steps
  3. 2 steps + 1 step

这道题也是《剑指offer》中出现的,10.斐波那契数列是非常经典的动态规划问题,其中变形包括“青蛙跳台阶”“小矩形覆盖大矩形”等问题。这类问题,第一步就是明白当出现一种情况后,后面的是不是同样的问题。比如“青蛙跳台阶”,我们把n级台阶时的跳法看成n的函数,记为f(n)。第一次跳一级,后面的问题变为f(n-1);第一次跳两级,后面的问题变为f(n-2),因此有f(n)=f(n-1)+f(n-2)。
代码如下:

    public static long fibonacci2(int n){//非递归
        if(n<=0)
            return 0;
        if(n==1)
            return 1;
        int a=0,b=1,f=0;
        for(int i=2;i<=n;i++){
            f=b+a;
            a=b;//先与下一步
            b=f;
        }
        return f;
    }
3.[LeetCode 121] Best Time to Buy and Sell Stock 买卖股票的最佳时间

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

这道题和《剑指offer》中63.股票的最大利润也是同一道题,只需要遍历一次数组,用一个变量记录遍历过数中的最小值,然后每次计算当前值和这个最小值之间的差值最为利润,然后每次选较大的利润来更新。当遍历完成后当前利润即为所求。

代码如下:

    public static int maxDiff(int[] prices){
        if(prices==null||prices.length<2)
            return 0;
		int buy=Integer.MAX_VALUE,res=Integer.MIN_VALUE;//将buy和res初始化为最大值和最小值,有益于后面的比较
		for(int price:prices) {
			res=Math.max(res,price-buy);
			buy=Math.min(buy, price);
			//res=Math.max(res,price-buy);//res和buy的顺序相当重要 
		}
		return res;
    }
4.[LeetCode] Best Time to Buy and Sell Stock II 买股票的最佳时间之二

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

这道题由于可以无限次买入和卖出。我们都知道炒股想挣钱当然是低价买入高价抛出,那么这里我们只需要从第二天开始,如果当前价格比之前价格高,则把差值加入利润中,因为我们可以昨天买入,今日卖出,若明日价更高的话,还可以今日买入,明日再抛出。以此类推,遍历完整个数组后即可求得最大利润。
代码如下:

public class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for (int i = 0; i < prices.length - 1; ++i) {
            if (prices[i] < prices[i + 1]) {
                res += prices[i + 1] - prices[i];
            }
        }
        return res;
    }
}
5.[LeetCode 198] House Robber 打家劫舍

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.

这道题的本质相当于在一列数组中取出一个或多个不相邻数,使其和最大。那么我们对于这类求极值的问题首先考虑动态规划Dynamic Programming来解,我们维护一个一位数组dp,其中dp[i]表示到i位置时不相邻数能形成的最大和,那么状态转移方程怎么写呢,我们先拿一个简单的例子来分析一下,比如说nums为{3, 2, 1, 5},那么我们来看我们的dp数组应该是什么样的,首先dp[0]=3没啥疑问,再看dp[1]是多少呢,由于3比2大,所以我们抢第一个房子的3,当前房子的2不抢,所以dp[1]=3,那么再来看dp[2],由于不能抢相邻的,所以我们可以用再前面的一个的dp值加上当前的房间值,和当前房间的前面一个dp值比较,取较大值当做当前dp值,所以我们可以得到状态转移方程dp[i] = max(num[i] + dp[i - 2], dp[i - 1]), 由此看出我们需要初始化dp[0]和dp[1],其中dp[0]即为num[0],dp[1]此时应该为max(num[0], num[1]),代码如下:

package chapter2;

public class P94_DynamicProgramming {
    //[LeetCode 198] House Robber 打家劫舍
    public static int rob(int[] data){
        if(data==null||data.length==0)
            return 0;
        if(data.length==1)
            return data[0];
        int[] dp=new int[data.length];
        dp[0]=data[0];
        dp[1]=Math.max(data[0],data[1]);
        for(int i=2;i<data.length;i++){
            dp[i]=Math.max(dp[i-1],dp[i-2]+data[i]);
        }
        return dp[data.length-1];
    }

    public static void main(String[] args) {
        System.out.println(rob(new int[]{1,2,3,1}));
        System.out.println(rob(new int[]{2,7,9,3,1}));
    }
}

还有一种解法,核心思想还是用DP,分别维护两个变量robEven和robOdd,顾名思义,robEven就是要抢偶数位置的房子,robOdd就是要抢奇数位置的房子。所以我们在遍历房子数组时,如果是偶数位置,那么robEven就要加上当前数字,然后和robOdd比较,取较大的来更新robEven。这里我们就看出来了,robEven组成的值并不是只由偶数位置的数字,只是当前要抢偶数位置而已。同理,当奇数位置时,robOdd加上当前数字和robEven比较,取较大值来更新robOdd,这种按奇偶分别来更新的方法,可以保证组成最大和的数字不相邻,最后别忘了在robEven和robOdd种取较大值返回,代码如下:

    public static int rob2(int[] data){
        if(data==null||data.length==0) return 0;
        if(data.length==1) return data[0];
        int odd=0,even=0;
        for(int i=0;i<data.length;i++){//从0开始
            if(i%2==0){
                even=Math.max(odd,even+data[i]);
            }else {
                odd=Math.max(even,odd+data[i]);
            }
        }
        return Math.max(even,odd);
    }

本文多数解法来自博客LeetCode全集,这位博主整理的非常完善。

参考:

http://www.cnblogs.com/grandyang/p/4606334.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值