LeetCode《初级算法》动态规划之买卖股票的最佳时机 -- JavaScript

790 阅读2分钟

题目

题目链接:leetcode-cn.com/leetbook/re…

image.png

题解

1、动态规划的分析

价格数组为 prices

1、划分子问题,确定子问题的边界

  • 设天数为 i 的问题的最大利润为 maxProfit[i] , 则天数为 i+1 的问题的最大利润为 maxProfit[i+1] = Max( maxProfit[i] , prices[i+1]-minPrice )

2、定义优化函数,列出优化函数的递推方程,判断其是否满足优化原则

f(n)={0,n<=1Max(f(n1),prices[n]minPrices),n>1f(n) = \begin{cases} 0, & n <= 1 \\ Max( f(n-1) , prices[n] - minPrices ), & n > 1 \end{cases}

根据递推方程(状态转移方程),n 天的最大利润由 n-1 天的最大利润决定; 所以其满足优化原则,可以使用动态归划;

3、编程

具体程序实现如下;


2、动态归划(DP)


如上分析,可得到如下程序:
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {

    const len = prices.length;

    let minPrice = prices[0],
        f = [0];
        

    if(len < 2) {
        return 0;
    } 

    for(let i = 1;i < len;i++) {

        
        f[i] = Math.max(f[i-1],prices[i] - minPrice);
        
        if(minPrice > prices[i]) {
            minPrice = prices[i];
        }

    }

    
    return f[len - 1];
    
};

对程序进行时空优化;

2.1、优化时间

上面的循环中的循环体为:

方法一:

// 一个计算操作,一个比较操作,一个赋值操作
f[i] = Math.max(f[i-1],prices[i] - minPrice);
// 一个比较操作
if(minPrice > prices[i]) {
		// 一个赋值操作
		minPrice = prices[i];
}

方法二:

// 一个比较操作
if(minPrice > prices[i]) {
		// 两个赋值操作
		minPrice = prices[i];
		f[i] = f[i-1];
}else {
		// 一个计算操作,一个比较操作,一个赋值操作
		f[i] = Math.max(f[i-1],prices[i] - minPrice);
}

方法一最少4个操作,最多5个操作; 方法二最少3个操作(并且是更省时的赋值操作),最多4个操作;

理论上来讲,对于同规模的一个问题,方法一会比方法二的用时少;


2.2、优化空间

在上面的程序中,空间消耗主要花费在存储存储问题的的解的数组上,空间复杂度为 O(n),但是分析代码可知,计算 f(n) 时只需要知道 f(n-1) ,i < n-1 的 f(i) 并不需要知道; 于是可以只用一个变量存储子问题的最大利润;

算法如下:

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {

    const len = prices.length;

    let minPrice = prices[0],
        maxPro = 0;
        

    if(len < 2) {
        return 0;
    } 

    for(let i = 1;i < len;i++) {
        
        maxPro = Math.max(maxPro,prices[i] - minPrice);

        if(minPrice > prices[i]) {
            
            minPrice = prices[i];
        }

    }

    return maxPro;
    
};



3、暴力枚举法


时间消耗太大,程序可以运行,但是 leetcode 提交超出时间限制;

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {

    // 双重循环,外层遍历选定某个值,内层遍历找到选定值后的所有值中的最大值,使用变量记录利润
    let max = 0;
    let temp = 0;
    let tempMax = 0,
        tempj = 0;

    
    const len = prices.length;
    for(let i = 0;i < len;i++) {
        temp = prices[i];
        for(let j = i + 1;j < len;j++) {
            tempj = prices[j];
            if(temp < tempj) {

                temp = tempj;
            }
        }
        
        tempMax = temp - prices[i];
        if(max < tempMax) {

            max = tempMax;
        }
    }

    return max;
 
};


大家如果有更好的思路和解法,欢迎大家一起来讨论啊~

这是使用 JavaScript 对 LeetCode《初级算法》的每道题的总结和实现的其中一篇,汇总篇在这里:

juejin.cn/post/700669…