GO算法-贪心算法&&动态规划

贪心算法:当用于求解最优结果是可以考虑。比如股票题,怎么买卖收益最高,这样存在最优最合适等字眼都可以进行考虑,不过前提一定是可以通过局部最优推导出全局最优。

一:买股票的最佳时机I

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

一和2的区别就是1只能交易一次

func maxProfit(prices []int) int {
    /**
    思路分析:
    只能在不同天交易一次获取最大利润。所以其实就是最大值减去最小值,按顺序。那么用动态规划来思考。今天的最大最小,都是取决于昨天的最大最小,所以代码如下
    **/

    //2:定义dp记录
    n := len(prices)
    //第一天是不能卖出的,所以第一天最大是0,最小是price[0],
    maxPrice , minPrice := 0, prices[0]

    //1:动态规划我们先把循环的核心写出来
    for i:=1; i < n; i++ {
        //今天卖出,是要和之前的最小比,所以如果今天卖出收益比昨天大,那就卖出
        if prices[i] - minPrice > maxPrice {
            maxPrice = prices[i] - minPrice
        }

        //更新下最小的价格
        if prices[i] < minPrice {
            minPrice = prices[i]
        }
    }

    //最后肯定要把股票卖掉,所以返回dp[i][0],或者最后一天没有股票就,之前卖了,那也是dp[i][0]就是之前的最大值了,上面已经算过了,所以直接返回
    return maxPrice

}

func max(a, b int) int {
    if a > b {
        return a
    }

    return b
}

二:买股票的最佳时机II

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:

输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:

输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路分析:

    分析题可知:第i天股票只有两个状态:有股票或者无股票。使用dp(i,0)表示无股票,dp(i,1)表示有股票。分情况讨论。
    当无股票时:今天无,那昨天有两个状态,无或者有,昨天无就是 dp[i-1][0],昨天有,那必然是今天卖了,今天才是无,所以卖了就有今天的收益,所以昨天是:dp[i-1][1],今天把股票卖了,手里就有了prices[i]的钱,所以方程如下:dp[i][0] = max(dp[i-1][0, dp[i-1][1] + prices[i])
    当有股票时:今天有,那昨天有两个状态,有或者无。昨天无今天有,所以今天必然要买入,买入就要扣除手里prices[i]的钱,所以就是dp[i-1][0] - prices[i],昨天有,那今天有价格不变,就是dp[i-1][1],所以最终方程就是:dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
    所以第i天,会产生两个价格,取最大的一个,就是第i天的最大值,当i=最大,就是最终最大的收益。

这里一维的空间压缩比较简单,就不单独写了,下买呢看下贪心算法

func maxProfit(prices []int) int {
    /**
    思考分析:
    这个题相比2,增加了只能购买两次的限制
    先思考下今天的状态:
    1:已经交易了2次 
    2:已经交易了1次,手里有股票
    3:已经交易了1次,手里没有股票
    4:还没有交易,但是手里有股票
    5:还没有交易,但是手里有股票 一直是0 不用记录

    这里是一维的就直接用5个变量滚动记录了
    针对1:可能是前面就完成两次,或者前面1次这次要卖出:allSell = max(allSell,oneSellHave + price[i])
    针对2:可能是之前手里就有股票,或者之前手里没有:oneSellHave = max(oneSellHave, onSellHave - price[i])
    针对3:可能是之前没交易,这次卖了一次,或者之前卖了一次:oneSeleNo = max(oneSeleNo, noSellHave + price[i])
    针对4:可能是上一次买的股票,或者这一次买的过:noSellHave = max(noSellHave, noSellNo + price[i])
    针对5:就是noSellNo = 0
    **/

    //思考完开始写代码
    //1:先把基准的值定义出来,i=0时
    allSell, oneSellNo := 0, 0
    noSellNo := 0
    //这种存在第一天买卖再买的情况所以不是0是 -prices[0]
    oneSellHave := -prices[0]
    noSellHave := -prices[0]


    n := len(prices)

    //1:先写滚动部分的逻辑
    for i:= 1; i<n; i++ {
        allSell     = max(allSell,oneSellHave + prices[i])
        oneSellHave = max(oneSellHave, oneSellNo - prices[i])
        oneSellNo   = max(oneSellNo, noSellHave + prices[i])
        noSellHave  = max(noSellHave, noSellNo - prices[i])
    }

    return allSell
}

func max(a, b int) int {
    if a > b {
        return a
    }

    return b
}

贪心算法思考的比较简单:就是局部最优到全局最优。假定当前每一次交易只要有利润就卖出,最终利润就是最大的,可以这样理解:1,2,3,4 (2-1 + 3 -2 + 4 -3) = 4-1 = 3,所以题解如下

func maxProfit(prices []int) int {
    /**
    思路分析:
    贪心算法思考的比较简单:就是局部最优到全局最优。假定当前每一次交易只要有利润就卖出,最终利润就是最大的,可以这样理解:1,2,3,4 (2-1 + 3 -2 + 4 -3) = 4-1 = 3,所以题解如下
    **/

    //2:定义dp记录
    n := len(prices)
    //记录昨天的
    max := 0

    //1:动态规划我们先把循环的核心写出来
    for i:=1; i < n; i++ {
        if prices[i] - prices[i-1] > 0 {
            max += prices[i] - prices[i-1]
        }
    }

    return max

}

三:买卖股票的最佳时机III

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1] 
输出:0 
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

示例 4:

输入:prices = [1]
输出:0
func maxProfit(prices []int) int {
    /**
    思考分析:
    这个题相比2,增加了只能购买两次的限制
    先思考下今天的状态:
    1:已经交易了2次 
    2:已经交易了1次,手里有股票
    3:已经交易了1次,手里没有股票
    4:还没有交易,但是手里有股票
    5:还没有交易,但是手里有股票 一直是0 不用记录

    这里是一维的就直接用5个变量滚动记录了
    针对1:可能是前面就完成两次,或者前面1次这次要卖出:allSell = max(allSell,oneSellHave + price[i])
    针对2:可能是之前手里就有股票,或者之前手里没有:oneSellHave = max(oneSellHave, onSellHave - price[i])
    针对3:可能是之前没交易,这次卖了一次,或者之前卖了一次:oneSeleNo = max(oneSeleNo, noSellHave + price[i])
    针对4:可能是上一次买的股票,或者这一次买的过:noSellHave = max(noSellHave, noSellNo + price[i])
    针对5:就是noSellNo = 0
    **/

    //思考完开始写代码
    //1:先把基准的值定义出来,i=0时
    allSell, oneSellHave, oneSellNo := 0, 0, 0
    noSellNo := 0
    noSellHave := -prices[0]


    n := len(prices)

    //1:先写滚动部分的逻辑
    for i:= 1; i<n; i++ {
        allSell     = max(allSell,oneSellHave + prices[i])
        oneSellHave = max(oneSellHave, oneSellNo - prices[i])
        oneSellNo   = max(oneSellNo, noSellHave + prices[i])
        noSellHave  = max(noSellHave, noSellNo - prices[i])
    }

    return allSell
}

func max(a, b int) int {
    if a > b {
        return a
    }

    return b
}

四:跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

func canJump(nums []int) bool {
    /**
    思考分析:
    如果i,能跳的最远距离能到达,那肯定是true。这样我们每走一步判断下当前能走得最远的步数和上一个能走得最远的那个大,我就把最大的更新过来,知道最大的大于数组的长度
    **/

    //开始
    //记录长度
    n := len(nums)
    //记录当前最大能走距离
    maxPre := nums[0]

    //1:先写核心逻辑
    for i:=0; i < n; i++ {
        //先取最大的步数
        maxPre = max(nums[i], maxPre)

        //当前不能走了,并且离终点还有距离
        if maxPre == 0 && n - i - 1 > 0 {
            return false
        }
        
        //能走的距离大于到终点的距离
        if maxPre >= n - i - 1 {
            return true
        }

        maxPre -= 1
    }

    return false
}

func max(a, b int) int {
    if a > b {
        return a
    }

    return b
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值