LeetCode Top100之560, 394,309题(关于股票有关的所有题目)

560. 和为K的子数组
① 题目描述

中文描述:https://leetcode-cn.com/problems/subarray-sum-equals-k/

② 暴力法
  • 从前往后遍历,求解子数组的和sum,如果sum等于target,则计数变量加1。
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( 1 ) O(1) O(1)
  • 代码如下,运行时间122ms
public int subarraySum(int[] nums, int k) {
    int count = 0;
    for (int i = 0; i < nums.length; i++) {
        int sum = 0;
        for (int j = i; j < nums.length; j++) {
            sum = sum + nums[j];
            if (sum == k) {
                count++;
            }
        }
    }
    return count;
}
③ 哈希表
  • 使用hash表存储从下标0开始到当前位置的累积和,并记录累计和的个数。
  • 假设在j位置时,累积和为sum[j];在i位置时,累积和为sum[i]。若有sum[j]+k=sum[i]则表示从j+1位置i位置的累积和为k。这时,查找hash表中sum[j]的个数,也就是j+1位置i位置的连续子数组的累积和个数。
  • 特殊的: 当从初始位置到某一位置的累积和为k时,j的值应该为-1,本身数组中是不存在该下标的。需要我们在hash表中手动添加map.put(0,1)
  • 代码如下,运行时间13ms
public int subarraySum(int[] nums, int k) {
    HashMap<Integer, Integer> map = new HashMap<>();
    int s = 0;
    int count = 0;// 应该提前加入子数组和0,因为从起始位置到某一位置的子数组和可能为k
    map.put(0,1);
    for (int i = 0; i < nums.length; i++) {
        s += nums[i];
        // 如果存在sum[j]+k=sum[i],说明从当前位置i开始往前走,有子数组和为k
        if (map.containsKey(s - k)) {
            count += map.get(s - k);
        }
        // 将当前位置的累积和存入hash表中
        map.put(s, map.getOrDefault(s, 0) + 1);
    }
    return count;
}
394. 字符串解码
① 题目描述

中文题目:https://leetcode-cn.com/problems/decode-string/

② 两个栈
  • 一个栈用于存放重复次数,一个栈用于存放待重复的字符。
  • 计算重复次数时,因为是数字字符串,所以要进行十进制累加。
  • 遇到左括号[就将重复次数入栈,将当前字符串入栈,这时当前字符串是不用进行重复的,比如a3[cb]中的第一个a
  • 遇到右括号]就将重复次数出栈,并对当前字符串进行重复,然后与栈顶元素构成新的当前字符串。
  • 代码如下,运行时间1ms
public String decodeString(String s) {
    Stack<Integer> nStack = new Stack<>();
    Stack<String> sStack = new Stack<>();
    String cur = "";// 保存当前字符串
    int num = 0;// 保存重复数字
    for (int i = 0; i < s.length(); i++) {
        char ch = s.charAt(i);
        if (Character.isDigit(ch)) {
            num = num * 10 + ch - '0';// 计算重复次数
        } else if (ch == '[') {// 将重复次数和当前字符串压入栈并清零
            nStack.push(num);
            sStack.push(cur);
            num = 0;
            cur = "";
        } else if (Character.isLetter(ch)) {
            cur += ch;// 加入到当前字符串中
        } else if (ch == ']') {
            int times = nStack.pop();
            cur = helper(times, cur);// 构造重复字符串
            cur = sStack.pop() + cur;// 将栈顶元素出栈,构造新的待重复字符串
        }
    }
    return cur;
}
309. 最佳买卖股票时机含冷冻期
① 题目描述

中文题目:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

② 动态规划
  • 对于第i天,有两种情况:第i天持有股票,第i天不持股票。
  • 第i天不持股票,也有两种情况:
    ① 第i-1天不持股票
    ② 第i-1天持有股票,第i天卖出
  • 第i天持有股票,也有两种情况:
    ① 第i-1天持有股票
    ② 第i-1天不持股票,可能是第i-1天刚好卖出,但是第i天就成了冷冻期。为了保证第i天不是冷冻期,应该是第i-2天不持股票
  • 建立两个dp数组,一个用于记录第i不持有股票的最大利润,一个用于记录第i天持有股票的最大利润。
    d p 1 [ i ] = m a x ( d p 1 [ i − 1 ] , d p 2 [ i − 1 ] + p r i c e [ i ] ) dp_1[i]=max(dp1[i-1],dp2[i-1]+price[i]) dp1[i]=max(dp1[i1],dp2[i1]+price[i])
    d p 2 [ i ] = m a x ( d p 2 [ i − 1 ] , d p 1 [ i − 2 ] − p r i c e [ i ] ) dp_2[i]=max(dp2[i-1],dp1[i-2]-price[i]) dp2[i]=max(dp2[i1],dp1[i2]price[i])
  • 初始情况: d p 1 [ 0 ] = 0 dp1[0]=0 dp1[0]=0,第0天没有进行任何交易,利润为0; d p 2 [ 0 ] = − p r i c e [ 0 ] dp2[0]=-price[0] dp2[0]=price[0],第0天就买入股票,这时利润为-price[0]
  • 代码如下,运行时间1ms
public int maxProfit(int[] prices) {
    if (prices.length == 0) {
        return 0;
    }
    int[] dp1 = new int[prices.length];// 表示第i天不持有股票
    int[] dp2 = new int[prices.length];// 表示第天持有股票
    // dp1[0]=0,因为第0天没有股票,说明没有进行任何交易
    // dp2[0]=-prices[0],第一天就买入股票,利润做减法
    dp2[0] = -prices[0];
    for (int i = 1; i < prices.length; i++) {
        // 第i天不持股票,可能是第i-1天也不持股票,或者第i-1天持有股票,第i天卖出
        dp1[i] = Math.max(dp1[i - 1], dp2[i - 1] + prices[i]);
        // 第i天持有股票,可能是第i-1天也持有股票,或者第i天买入股票,这要求前一天为冷冻期或者不持有股票
        // 第i-1天不持有股票可能是刚卖出,为了保证第i天不是冷冻期,第i-2天必须不持股票
        dp2[i] = Math.max(dp2[i - 1], (i - 2 >= 0 ? dp1[i - 2] : 0) - prices[i]);
    }
    return Math.max(dp1[prices.length - 1], dp2[prices.length - 1]);
}
股票有关的所有题目
121. 买卖股票的最佳时机
  • 使用暴力法,求解当 j > i j>i j>i时, m a x ( p r i c e [ j ] − p r i c e [ i ] ) max(price[j]-price[i]) max(price[j]price[i])的值,就是最大利润。
  • 通过折线图发现,最低价格之后的最高价格(形成最低谷和最高峰):
public int maxProfit(int[] prices) {
    if (prices.length == 0) {
        return 0;
    }
    int minPrice = Integer.MAX_VALUE;
    int profit = 0;
    for (int i = 0; i < prices.length; i++) {
        if (prices[i] < minPrice) {// 寻找最低谷
            minPrice = prices[i];
        } else {
            if (prices[i] - minPrice > profit) {// 寻找最低谷后的最高价格,形成最高峰
                profit = prices[i] - minPrice;
            }
        }
    }
    return profit;
}
122. 买卖股票的最佳时机 II
  • 与309题很相似,只是没有了冷冻期,昨天卖出,今天就可以买入。
  • 也使用两个dp数组分别表示第i天不持股票的最大利润、第i天持有股票的最大利润。
  • 第i天不持股票,有两种情况:
    ① 第i-1天也不持股票
    ② 第i天卖出股票,这就要求第i-1天持有股票
  • 第i天持有股票,有两种情况:
    ① 第i-1天持有股票
    ② 第i天买入股票,这就要求第i-1天不持股票
  • 状态转移方程:
    d p 1 [ i ] = M a t h . m a x ( d p 1 [ i − 1 ] , d p 2 [ i − 1 ] + p r i c e s [ i ] ) dp1[i]=Math.max(dp1[i-1],dp2[i-1]+prices[i]) dp1[i]=Math.max(dp1[i1],dp2[i1]+prices[i])
    d p 2 [ i ] = M a t h . m a x ( d p 2 [ i − 1 ] , d p 1 [ i − 1 ] − p r i c e s [ i ] ) dp2[i]=Math.max(dp2[i-1],dp1[i-1]-prices[i]) dp2[i]=Math.max(dp2[i1],dp1[i1]prices[i])
  • 初始条件:
    d p 1 [ 0 ] = 0 dp1[0]=0 dp1[0]=0
    d p 2 [ 0 ] = − p r i c e s [ 0 ] dp2[0]=-prices[0] dp2[0]=prices[0]
  • 代码如下,运行时间2ms:
public int maxProfit(int[] prices) {
   if (prices.length == 0) {
       return 0;
   }
   int[] dp1=new int[prices.length];// 第i天不持股票的最大利润
   int[] dp2=new int[prices.length];// 第i天持有股票的最大利润
   // 第0天不持股票,未进行任何交易,利润为0,dp1[0]=0;
   // 第0天持有股票,当天进行的买入,利润为-price[0]
   dp2[0]=-prices[0];
   for (int i=1;i<prices.length;i++){
       dp1[i] = Math.max(dp1[i - 1], dp2[i - 1] + prices[i]);
       dp2[i] = Math.max(dp2[i - 1], dp1[i - 1] - prices[i]);
   }
   return Math.max(dp1[prices.length-1],dp2[prices.length-1]);
}
123. 买卖股票的最佳时机 III
  • 暴力法: 从第i个位置,将整个数组分为左半部分和右半部分,分别查找left和right的一次售卖股票的最大利润,二者的和的最大值就是两次售卖股票的最大利润。
  • 运行时间很长586ms,因为需要分别求解left和right一次售卖股票的最大利润:
public int maxProfitOne(int[] prices) {
    if (prices.length == 0) {
        return 0;
    }
    int profit = 0;
    int minPrice = Integer.MAX_VALUE;
    for (int i = 0; i < prices.length; i++) {
        if (prices[i] < minPrice) {
            minPrice = prices[i];
        } else {
            if (prices[i] - minPrice > profit) {
                profit = prices[i] - minPrice;
            }
        }
    }
    return profit;
}

public int maxProfit(int[] prices) {
    int profit = 0;
    for (int i = 0; i < prices.length; i++) {
        int[] left = new int[i];
        int[] right = new int[prices.length - i];
        System.arraycopy(prices, 0, left, 0, left.length);
        System.arraycopy(prices, i, right, 0, right.length);
        profit = Math.max(profit, maxProfitOne(left) + maxProfitOne(right));
    }
    return profit;
}
188. 买卖股票的最佳时机 IV

不会!!!后面补

714. 买卖股票的最佳时机含手续费
  • 又是一个不限制交易次数的问题, 共有三种类型:
    ① 不限制交易次数
    ② 不限制交易次数,但是有一天的冷冻期
    ③ 不限制交易次数,但是交易完成要收手续费
  • 这个题的状态转移方程就变成了:
  • 状态转移方程:
    d p 1 [ i ] = M a t h . m a x ( d p 1 [ i − 1 ] , d p 2 [ i − 1 ] + p r i c e s [ i ] − 2 ) dp1[i]=Math.max(dp1[i-1],dp2[i-1]+prices[i]-2) dp1[i]=Math.max(dp1[i1],dp2[i1]+prices[i]2) 有手续费
    d p 2 [ i ] = M a t h . m a x ( d p 2 [ i − 1 ] , d p 1 [ i − 1 ] − p r i c e s [ i ] ) dp2[i]=Math.max(dp2[i-1],dp1[i-1]-prices[i]) dp2[i]=Math.max(dp2[i1],dp1[i1]prices[i])
  • 代码如下,运行时间6ms
public int maxProfit(int[] prices, int fee) {
    if (prices.length == 0) {
        return 0;
    }
    int[] dp1 = new int[prices.length];
    int[] dp2 = new int[prices.length];
    dp2[0] = -prices[0];
    for (int i = 1; i < prices.length; i++) {
        dp1[i] = Math.max(dp1[i - 1], dp2[i - 1] + prices[i] - fee);
        dp2[i] = Math.max(dp2[i - 1], dp1[i - 1] - prices[i]);
    }
    return Math.max(dp1[prices.length - 1], dp2[prices.length - 1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值