代码随想录算法训练营第十七天 | 贪心系列全部

本文介绍了作者在学习贪心算法过程中,通过解决一系列题目,如分发饼干、摆动序列、最大子数组和、跳跃游戏等,逐步理解和掌握贪心算法的应用。文章列举了多个代码示例,展示了如何运用贪心策略解决实际问题,并强调了在面对不同问题时,如何分析和构建贪心逻辑的重要性。
摘要由CSDN通过智能技术生成

贪心系列全部

贪心理论基础

直接上文章链接。
贪心理论基础

455 分发饼干

未看解答自己编写的青春版

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        g.sort()
        s.sort()
        gmin = g[0]
        n = len(s)
        m = len(g)
        i = 0
        while i < n :
            if s[i]>=gmin :
                break
            i += 1
        s = s[i:]   
        n = len(s)
        count = 0
        i = j = 0
        while i < n and j < m :
            if s[i]>=g[j]:
                i += 1
                j += 1
                count += 1
            else :
                i += 1

        return count

上述代码存在冗余,不需要修改s数组

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        g.sort()
        s.sort()
        gmin = g[0]
        n = len(s)
        m = len(g)
        count = 0
        i = j = 0
        while i < n and j < m :
            if s[i]>=g[j]:
                i += 1
                j += 1
                count += 1
            else :
                i += 1

        return count

重点

基本和我也差不多一个思路。

代码随想录的代码

贪心 大饼干优先

class Solution:
    def findContentChildren(self, g, s):
        g.sort()  # 将孩子的贪心因子排序
        s.sort()  # 将饼干的尺寸排序
        index = len(s) - 1  # 饼干数组的下标,从最后一个饼干开始
        result = 0  # 满足孩子的数量
        for i in range(len(g)-1, -1, -1):  # 遍历胃口,从最后一个孩子开始
            if index >= 0 and s[index] >= g[i]:  # 遍历饼干
                result += 1
                index -= 1
        return result

贪心 小饼干优先

class Solution:
    def findContentChildren(self, g, s):
        g.sort()  # 将孩子的贪心因子排序
        s.sort()  # 将饼干的尺寸排序
        index = 0
        for i in range(len(s)):  # 遍历饼干
            if index < len(g) and g[index] <= s[i]:  # 如果当前孩子的贪心因子小于等于当前饼干尺寸
                index += 1  # 满足一个孩子,指向下一个孩子
        return index  # 返回满足的孩子数目

我的代码(当天晚上理解后自己编写)

感觉还是小饼干优先,更适合我的逻辑。

class Solution:
    def findContentChildren(self, g, s):
        g.sort()  # 将孩子的贪心因子排序
        s.sort()  # 将饼干的尺寸排序
        index = 0
        for i in range(len(s)):  # 遍历饼干
            if index < len(g) and g[index] <= s[i]:  # 如果当前孩子的贪心因子小于等于当前饼干尺寸
                index += 1  # 满足一个孩子,指向下一个孩子
        return index  # 返回满足的孩子数目

376 摆动序列

不会做,想不到怎么使用贪心算法,能想到使用动态规划能做,但是目前按照代码随想录的进度,还没学到动态规划,也不会写。

未看解答自己编写的青春版

重点

删除单调坡度上的元素,保留所有峰值。

没有必要真的去做删除元素的操作,因为删除操作本身是O(n),而且题目要求,本身也只是返回一个长度值。

定义两个变量,一个prediff,一个currentdiff,这样判断条件就清晰了!

此题细节很多,一遍都注意到很不容易,可以多去看解析。
摆动序列

动态规划,目前还没学习,代码也没太看懂。

代码随想录的代码

贪心(版本一)

class Solution:
    def wiggleMaxLength(self, nums):
        if len(nums) <= 1:
            return len(nums)  # 如果数组长度为0或1,则返回数组长度
        curDiff = 0  # 当前一对元素的差值
        preDiff = 0  # 前一对元素的差值
        result = 1  # 记录峰值的个数,初始为1(默认最右边的元素被视为峰值)
        for i in range(len(nums) - 1):
            curDiff = nums[i + 1] - nums[i]  # 计算下一个元素与当前元素的差值
            # 如果遇到一个峰值
            if (preDiff <= 0 and curDiff > 0) or (preDiff >= 0 and curDiff < 0):
                result += 1  # 峰值个数加1
                preDiff = curDiff  # 注意这里,只在摆动变化的时候更新preDiff
        return result  # 返回最长摆动子序列的长度

贪心(版本二)

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)  # 如果数组长度为0或1,则返回数组长度
        preDiff,curDiff ,result  = 0,0,1  #题目里nums长度大于等于1,当长度为1时,其实到不了for循环里去,所以不用考虑nums长度
        for i in range(len(nums) - 1):
            curDiff = nums[i + 1] - nums[i]
            if curDiff * preDiff <= 0 and curDiff !=0:  #差值为0时,不算摆动
                result += 1
                preDiff = curDiff  #如果当前差值和上一个差值为一正一负时,才需要用当前差值替代上一个差值
        return result

动态规划(版本一)

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        # 0 i 作为波峰的最大长度
        # 1 i 作为波谷的最大长度
        # dp是一个列表,列表中每个元素是长度为 2 的列表
        dp = []
        for i in range(len(nums)):
            # 初始为[1, 1]
            dp.append([1, 1])
            for j in range(i):
                # nums[i] 为波谷
                if nums[j] > nums[i]:
                    dp[i][1] = max(dp[i][1], dp[j][0] + 1)
                # nums[i] 为波峰
                if nums[j] < nums[i]:
                    dp[i][0] = max(dp[i][0], dp[j][1] + 1)
        return max(dp[-1][0], dp[-1][1])

动态规划(版本二)

class Solution:
    def wiggleMaxLength(self, nums):
        dp = [[0, 0] for _ in range(len(nums))]  # 创建二维dp数组,用于记录摆动序列的最大长度
        dp[0][0] = dp[0][1] = 1  # 初始条件,序列中的第一个元素默认为峰值,最小长度为1
        for i in range(1, len(nums)):
            dp[i][0] = dp[i][1] = 1  # 初始化当前位置的dp值为1
            for j in range(i):
                if nums[j] > nums[i]:
                    dp[i][1] = max(dp[i][1], dp[j][0] + 1)  # 如果前一个数比当前数大,可以形成一个上升峰值,更新dp[i][1]
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i][0] = max(dp[i][0], dp[j][1] + 1)  # 如果前一个数比当前数小,可以形成一个下降峰值,更新dp[i][0]
        return max(dp[-1][0], dp[-1][1])  # 返回最大的摆动序列长度

动态规划(版本三)优化

class Solution:
    def wiggleMaxLength(self, nums):
        if len(nums) <= 1:
            return len(nums)  # 如果数组长度为0或1,则返回数组长度
        
        up = down = 1  # 记录上升和下降摆动序列的最大长度
        for i in range(1, len(nums)):
            if nums[i] > nums[i-1]:
                up = down + 1  # 如果当前数比前一个数大,则可以形成一个上升峰值
            elif nums[i] < nums[i-1]:
                down = up + 1  # 如果当前数比前一个数小,则可以形成一个下降峰值
        
        return max(up, down)  # 返回上升和下降摆动序列的最大长度

我的代码(当天晚上理解后自己编写)

挺难的一道题,要好好理解。

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        if len(nums)<=1:
            return len(nums)

        curDiff = 0
        preDiff = 0
        result = 1
        for i in range(len(nums)-1):
            curDiff = nums[i+1]-nums[i]
            if (preDiff<=0 and curDiff>0) or (preDiff>=0 and curDiff<0) :
                result += 1
                preDiff = curDiff
        return result

53 最大子数组和

未看解答自己编写的青春版

最朴素,逻辑细致但重复版

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        result = nums[0]
        maxsum = nums[0]
        res = nums[0]
        n = len(nums)

        for i in range(1,n):
            
            if nums[i] > 0 :
                if maxsum <= 0 :
                    result = nums[i]
                    maxsum = nums[i]
                else :
                    maxsum += nums[i]
                    result = maxsum
            else :
                if maxsum <= 0:
                    result = nums[i]
                    maxsum = nums[i]

                else :
                    maxsum += nums[i]
                    result = maxsum
            res = max(res,result)

        return res

看了重复逻辑后改进版

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        result = nums[0]
        maxsum = nums[0]
        n = len(nums)
        for i in range(1,n):
            if maxsum <= 0 :    
                maxsum = nums[i]
            else :
                maxsum += nums[i]         
            result = max(maxsum,result)
        return result

重点

思路和我精简后的代码差不多,同样先不看动态规划的代码。

代码随想录的代码

暴力法

class Solution:
    def maxSubArray(self, nums):
        result = float('-inf')  # 初始化结果为负无穷大
        count = 0
        for i in range(len(nums)):  # 设置起始位置
            count = 0
            for j in range(i, len(nums)):  # 从起始位置i开始遍历寻找最大值
                count += nums[j]
                result = max(count, result)  # 更新最大值
        return result

贪心法
好好理解一下,为什么count<=0时,令count=0,因为当目前count<=0时,选择下一个元素作为我们的起点。

这个想法和我自己的想法,差一个身位。但在循环中,是等价的。

class Solution:
    def maxSubArray(self, nums):
        result = float('-inf')  # 初始化结果为负无穷大
        count = 0
        for i in range(len(nums)):
            count += nums[i]
            if count > result:  # 取区间累计的最大值(相当于不断确定最大子序终止位置)
                result = count
            if count <= 0:  # 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
                count = 0
        return result

动态规划C++

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if (nums.size() == 0) return 0;
        vector<int> dp(nums.size(), 0); // dp[i]表示包括i之前的最大连续子序列和
        dp[0] = nums[0];
        int result = dp[0];
        for (int i = 1; i < nums.size(); i++) {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 状态转移公式
            if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值
        }
        return result;
    }
};

我的代码(当天晚上理解后自己编写)

感觉还是自己的贪心逻辑好理解一点。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        result = nums[0]
        maxsum = nums[0]
        n = len(nums)
        for i in range(1,n):
            if maxsum <= 0 :    
                maxsum = nums[i]
            else :
                maxsum += nums[i]         
            result = max(maxsum,result)
        return result

贪心系列1总结

直接上链接。
贪心系列1总结

122 买卖股票的最佳时机 II

未看解答自己编写的青春版

小贪一手,贪心算法纯纯直觉,觉得作差相加就可以,稍微想了一下也没有反例。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        res = 0
        diff = 0
        for i in range(1,len(prices)):
            diff = prices[i]-prices[i-1]
            if diff > 0 :
                res += diff
        return res

重点

只有一只股票。

想明白利润是可以分解的!不愧是代码随想录!
在这里插入图片描述
从图中可以发现,其实我们需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间。那么只收集正利润就是贪心所贪的地方!局部最优:收集每天的正利润,全局最优:求得最大利润。

同样有两种方法,动态规划方法还没看。

代码随想录的代码

贪心

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        result = 0
        for i in range(1, len(prices)):
            result += max(prices[i] - prices[i - 1], 0)
        return result

动态规划

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        length = len(prices)
        dp = [[0] * 2 for _ in range(length)]
        dp[0][0] = -prices[0]
        dp[0][1] = 0
        for i in range(1, length):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) #注意这里是和121. 买卖股票的最佳时机唯一不同的地方
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
        return dp[-1][1]

我的代码(当天晚上理解后自己编写)

过,本题的要点在于想到,利润可以拆分为每天。

55 跳跃游戏

未看解答自己编写的青春版

逻辑上想错方向了,没写出来,这里记录一下自己的垃圾错误代码吧。

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        n = len(nums)
        count = 0
        dis = 1
        for i in range(n-2,-1,-1):
            count += nums[i]
            if count < dis :
                return False
            dis += 1
            

        return True

重点

不要纠结跳几步,从最大覆盖范围上考虑。
在这里插入图片描述

理清逻辑后自己编写

本题重点在于理清逻辑,在想到从覆盖范围考虑后,怎样编写代码逻辑也是要小心的。

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        index = 0
        for i in range(len(nums)):
            if index >= i :
                index = max(index,i+nums[i])
            else :
                return False

            if index >= len(nums)-1 :
                return True

代码随想录的代码

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        cover = 0
        if len(nums) == 1: return True
        i = 0
        # python不支持动态修改for循环中变量,使用while循环代替
        while i <= cover:
            cover = max(i + nums[i], cover)
            if cover >= len(nums) - 1: return True
            i += 1
        return False
## for循环
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        cover = 0
        if len(nums) == 1: return True
        for i in range(len(nums)):
            if i <= cover:
                cover = max(i + nums[i], cover)
                if cover >= len(nums) - 1: return True
        return False

我的代码(当天晚上理解后自己编写)

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        cover = 0
        if len(nums)==1:
           return True

        i = 0
        while i <= cover :
            cover = max(cover,i+nums[i])
            if cover>=len(nums)-1:
                return True
            i += 1
        return False

45 跳跃游戏 II

未看解答自己编写的青春版

没思路。

重点

用最小的步数,去尽可能地增加我们的覆盖范围。

了解逻辑后,自己去编写了一下代码,没写对。

此题的代码逻辑也不好写。

代码随想录的代码

贪心(版本一)

class Solution:
    def jump(self, nums):
        if len(nums) == 1:
            return 0
        
        cur_distance = 0  # 当前覆盖最远距离下标
        ans = 0  # 记录走的最大步数
        next_distance = 0  # 下一步覆盖最远距离下标
        
        for i in range(len(nums)):
            next_distance = max(nums[i] + i, next_distance)  # 更新下一步覆盖最远距离下标
            if i == cur_distance:  # 遇到当前覆盖最远距离下标
                ans += 1  # 需要走下一步
                cur_distance = next_distance  # 更新当前覆盖最远距离下标(相当于加油了)
                if next_distance >= len(nums) - 1:  # 当前覆盖最远距离达到数组末尾,不用再做ans++操作,直接结束
                    break
        
        return ans

贪心(版本二)

class Solution:
    def jump(self, nums):
        cur_distance = 0  # 当前覆盖的最远距离下标
        ans = 0  # 记录走的最大步数
        next_distance = 0  # 下一步覆盖的最远距离下标
        
        for i in range(len(nums) - 1):  # 注意这里是小于len(nums) - 1,这是关键所在
            next_distance = max(nums[i] + i, next_distance)  # 更新下一步覆盖的最远距离下标
            if i == cur_distance:  # 遇到当前覆盖的最远距离下标
                cur_distance = next_distance  # 更新当前覆盖的最远距离下标
                ans += 1
        
        return ans

贪心(版本三) 类似‘55-跳跃游戏’写法

class Solution:
    def jump(self, nums) -> int:
        if len(nums)==1:  # 如果数组只有一个元素,不需要跳跃,步数为0
            return 0
        
        i = 0  # 当前位置
        count = 0  # 步数计数器
        cover = 0  # 当前能够覆盖的最远距离
        
        while i <= cover:  # 当前位置小于等于当前能够覆盖的最远距离时循环
            for i in range(i, cover+1):  # 遍历从当前位置到当前能够覆盖的最远距离之间的所有位置
                cover = max(nums[i]+i, cover)  # 更新当前能够覆盖的最远距离
                if cover >= len(nums)-1:  # 如果当前能够覆盖的最远距离达到或超过数组的最后一个位置,直接返回步数+1
                    return count+1
            count += 1  # 每一轮遍历结束后,步数+1

动态规划

class Solution:
    def jump(self, nums: List[int]) -> int:
        result = [10**4+1] * len(nums)  # 初始化结果数组,初始值为一个较大的数
        result[0] = 0  # 起始位置的步数为0

        for i in range(len(nums)):  # 遍历数组
            for j in range(nums[i] + 1):  # 在当前位置能够跳跃的范围内遍历
                if i + j < len(nums):  # 确保下一跳的位置不超过数组范围
                    result[i + j] = min(result[i + j], result[i] + 1)  # 更新到达下一跳位置的最小步数

        return result[-1]  # 返回到达最后一个位置的最小步数

我的代码(当天晚上理解后自己编写)

注意注意,此题的编写细节与上一题存在不同之处,是有一个,走到当前的最大长度后,再更新下一index。

本题的代码,要学习!

class Solution:
    def jump(self, nums: List[int]) -> int:
        if len(nums)==1 :
            return 0
        cur_distance = 0
        ans = 0
        next_distance = 0
        for i in range(len(nums)):
            next_distance = max(nums[i]+i,next_distance)
            if i == cur_distance :
                ans += 1
                cur_distance = next_distance
                if next_distance >= len(nums)-1 :
                    break
        return ans
     

1005 K 次取反后最大化的数组和

未看解答自己编写的青春版

需要注意的细节还是比较多的,Debug了几次才通过。

class Solution:
    def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
        nums.sort()
        n = len(nums)
        
        i = 0
        sumsum = sum(nums)
        if nums[0] == 0 :
            return sumsum
        if nums[0] > 0 :
            if k % 2 == 0:
                return sumsum
            else :
                return sumsum-2*nums[0]
        while i < n and nums[i] < 0 and k > 0 :
            nums[i] = -nums[i]
            i += 1
            k -= 1
        sumsum = sum(nums)
        if k == 0 :
            return sumsum
        else :
            if i < n :
                temp = min(nums[i],nums[i-1])
                if k % 2 == 0 :
                    return sumsum
                else :
                    return sumsum - 2*temp
            else :
                temp = nums[i-1]
                if k % 2 == 0 :
                    return sumsum
                else :
                    return sumsum - 2*temp

重点

一定要注意平时用贪心的题目去训练贪心的感觉,而不是靠感觉写出来就完事了。

代码随想录的代码

class Solution:
    def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
        A.sort(key=lambda x: abs(x), reverse=True)  # 第一步:按照绝对值降序排序数组A

        for i in range(len(A)):  # 第二步:执行K次取反操作
            if A[i] < 0 and K > 0:
                A[i] *= -1
                K -= 1

        if K % 2 == 1:  # 第三步:如果K还有剩余次数,将绝对值最小的元素取反
            A[-1] *= -1

        result = sum(A)  # 第四步:计算数组A的元素和
        return result

我的代码(当天晚上理解后自己编写)

class Solution:
    def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
        # 第一步,按照绝对值降序排列数组
        nums.sort(key=lambda x:abs(x),reverse=True)

        for i in range(len(nums)):
            if nums[i] < 0 and k > 0 :
                nums[i] = -nums[i]
                k-=1
        if k % 2 == 1:
            nums[-1] = -nums[i]
        
        result = sum(nums)
        return result

贪心系列2总结

直接上链接。本周最值得注意的就是跳跃游戏,用拘泥于每次究竟跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。
贪心系列2总结

134 加油站

未看解答自己编写的青春版

超出时间限制,错误输入是一个巨长的全零序列。学习一下代码随想录是怎么解这道题的。

没想到我写的这种写法是暴力法。

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        n = len(cost)
        if sum(gas) < sum(cost):
            return -1
        
        for i in range(n):
            if gas[i] < cost[i]:
                continue
            else :
                result = 0
                sumgas = 0
                sumcost = 0
                for j in range(i,n+i):
                    if j < n :
                        sumgas += gas[j]
                        sumcost += cost[j]
                    else :
                        sumgas += gas[j-n]
                        sumcost += cost[j-n]
                    if sumgas < sumcost :
                        result = -1
                        break
                if result == 0 :
                    return i
        return -1

重点

想不到怎么使用贪心。

这道题的贪心也太巧妙了,我想不到!而且感觉贪心的不是那么直观。

暂时不去理解,贪心的方法一,更不直观了,先学习方法二。

代码随想录的代码

暴力法,哈哈,卡哥的暴力法也超出时间限制

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        for i in range(len(cost)):
            rest = gas[i] - cost[i]  # 记录剩余油量
            index = (i + 1) % len(cost)  # 下一个加油站的索引

            while rest > 0 and index != i:  # 模拟以i为起点行驶一圈(如果有rest==0,那么答案就不唯一了)
                rest += gas[index] - cost[index]  # 更新剩余油量
                index = (index + 1) % len(cost)  # 更新下一个加油站的索引

            if rest >= 0 and index == i:  # 如果以i为起点跑一圈,剩余油量>=0,并且回到起始位置
                return i  # 返回起始位置i

        return -1  # 所有起始位置都无法环绕一圈,返回-1

贪心(版本二)

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        curSum = 0  # 当前累计的剩余油量
        totalSum = 0  # 总剩余油量
        start = 0  # 起始位置
        
        for i in range(len(gas)):
            curSum += gas[i] - cost[i]
            totalSum += gas[i] - cost[i]
            
            if curSum < 0:  # 当前累计剩余油量curSum小于0
                start = i + 1  # 起始位置更新为i+1
                curSum = 0  # curSum重新从0开始累计
        
        if totalSum < 0:
            return -1  # 总剩余油量totalSum小于0,说明无法环绕一圈
        return start

我的代码(当天晚上理解后自己编写)

学习这种写法

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        cursum = 0
        totalsum = 0
        index = 0
        for i in range(len(gas)):
            cursum += gas[i] - cost[i]
            totalsum += gas[i] - cost[i]

            if cursum < 0 :
                index = i+1
                cursum = 0
        if totalsum < 0 :
            return -1

        return index

135 分发糖果

未看解答自己编写的青春版

没有贪心的思路,也没有其他的思路。

重点

不能两边同时进行比较,会顾此失彼。

两边分别遍历,然后取最值

代码随想录的代码

class Solution:
    def candy(self, ratings: List[int]) -> int:
        candyVec = [1] * len(ratings)
        
        # 从前向后遍历,处理右侧比左侧评分高的情况
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i - 1]:
                candyVec[i] = candyVec[i - 1] + 1
        
        # 从后向前遍历,处理左侧比右侧评分高的情况
        for i in range(len(ratings) - 2, -1, -1):
            if ratings[i] > ratings[i + 1]:
                candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1)
        
        # 统计结果
        result = sum(candyVec)
        return result

我的代码(当天晚上理解后自己编写)

好题,要多练。注意点我已经写在了注释中。

注意遍历顺序与左右侧大小的关系,同时,第二个方向的取max操作也是精髓,必须要基于上一个candy的值进行判断。

class Solution:
    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        candy = [1] * n
        # 一定要注意遍历顺序与左右侧大小的关系

        # 从前向后,处理右侧评分比左边高的情况
        for i in range(1,len(ratings)):
            if ratings[i] > ratings[i-1]:
                candy[i] = candy[i-1]+1
        # 从后向前,处理左侧比右侧高的情况

        for i in range(len(ratings)-2,-1,-1):
            if ratings[i] > ratings[i+1]:
                # 这里的max也是精髓,必须要基于前一个candy的值,不能是一个全新的[1]数组
                candy[i] = max(candy[i+1]+1,candy[i])

        res = sum(candy)
        return res

860 柠檬水找零

首先能想到的是建立一个数组,因为只有三种面值,加减一就好了,一直遍历到数组结束,但是这种解法没有贪心的思想,顶多就是找零时,优先使用10元的。

本来以为有贪心,但是没想到就是我上面说的那样,而且贪心就体现在先用10元上。

未看解答自己编写的青春版

重点

代码随想录的代码

class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        five = 0
        ten = 0
        twenty = 0
        
        for bill in bills:
            # 情况一:收到5美元
            if bill == 5:
                five += 1
            
            # 情况二:收到10美元
            if bill == 10:
                if five <= 0:
                    return False
                ten += 1
                five -= 1
            
            # 情况三:收到20美元
            if bill == 20:
                # 先尝试使用10美元和5美元找零
                if five > 0 and ten > 0:
                    five -= 1
                    ten -= 1
                    #twenty += 1
                # 如果无法使用10美元找零,则尝试使用三张5美元找零
                elif five >= 3:
                    five -= 3
                    #twenty += 1
                else:
                    return False
        
        return True

我的代码(当天晚上理解后自己编写)

class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        five = 0
        ten = 0
        twenty = 0
        for bill in bills:
            if bill == 5 :
                five+=1
            if bill == 10 :
                if five <= 0:
                    return False
                ten += 1
                five-=1
            if bill == 20 :
                if ten > 0 and five > 0 :
                    ten -= 1
                    five -= 1
                    twenty += 1
                elif five >= 3:
                    five -= 3
                    twenty += 1
                else :
                    return False
        return True

406 根据身高重建队列

未看解答自己编写的青春版

思路上还是出现了偏差。

重点

首先要认识到,这也是一道双维度的题,类似于分发糖果。那么先处理哪一维度就很关键。

先处理K,按K排序,得到的既不满足K的约束,也不满足H。

所以应该先处理H,按H排序,然后处理K,对于同一K,先处理H大的。这点我倒是想到了。

按照K插入。K是多少就插入在哪个位置。

这题在代码逻辑上也不是那么好想的。

要点在于按身高H排序而不是K,因为排序后,身高一定是从小到大的,而对于K来说,也是一个和H相关的量,根据按顺序的H,K的插入位置可以随之确定。

代码随想录的代码

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
    	# 先按照h维度的身高顺序从高到低排序。确定第一个维度
        # lambda返回的是一个元组:当-x[0](维度h)相同时,再根据x[1](维度k)从小到大排序
        people.sort(key=lambda x: (-x[0], x[1]))
        que = []
	
	# 根据每个元素的第二个维度k,贪心算法,进行插入
        # people已经排序过了:同一高度时k值小的排前面。
        for p in people:
            que.insert(p[1], p)
        return que

我的代码(当天晚上理解后自己编写)

双维度,排序,插入!

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        # 先按照H维度,按身高从高到低排序,确定第一个维度
        # 当身高相同时,按照第二个维度K排序,K小的排在前面

        people.sort(key = lambda x : (-x[0],x[1]))

        res = []

        for p in people :
            res.insert(p[1],p)
        
        return res

贪心系列3总结

贪心系列3总结

根据身高重建队列(vector原理讲解)

可以看看,虽然Python不涉及这些东西,但毕竟都是底层实现。

452 用最少数量的箭引爆气球

未看解答自己编写的青春版

不会,也没思路,感觉又是两个维度?但是感觉从哪个维度排序都不对啊。

重点

靠,原本我想的极端示例的例子,竟然按照最朴素的思想,就可以包括了,重叠后更新右边界的操作,是比较好想的,但如果不进行实际模拟,很难想到这种一般性的解法,可以解决极端示例。

极端示例(已经按第一维排序):[2,4] [2,3] [2,3] [4,7] [5,6] [5,6] 。

等等!但是这种情况,如果我在排序时,规定,如果第一维相同,按第二维从大到小排序不就好了吗!

况且!就算只按第一维排序,第二维为乱序,按照代码随想录所讲解的,更新右边界的朴素思想,也是正确的!

逻辑真难想啊。

本题贪心的思路不复杂,是我第一遍看题的时候,想的太复杂了!

代码随想录的代码

class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        if len(points) == 0: return 0
        points.sort(key=lambda x: x[0])
        result = 1
        for i in range(1, len(points)):
            if points[i][0] > points[i - 1][1]: # 气球i和气球i-1不挨着,注意这里不是>=
                result += 1     
            else:
                points[i][1] = min(points[i - 1][1], points[i][1]) # 更新重叠气球最小右边界
        return result
class Solution: # 不改变原数组
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        points.sort(key = lambda x: x[0])
        sl,sr = points[0][0],points[0][1]
        count = 1
        for i in points:
            if i[0]>sr:
                count+=1
                sl,sr = i[0],i[1]
            else:
                sl = max(sl,i[0])
                sr = min(sr,i[1])
        return count

我的代码(当天晚上理解后自己编写)

区间重叠的经典代码了。

class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        if len(points)==0 :
            return 0
        points.sort(key = lambda x :x[0])

        result = 1
        for i in range(1,len(points)):
            if points[i][0] > points[i-1][1] :
                result += 1
            else :
                points[i][1] = min(points[i][1],points[i-1][1])

        return result

435 无重叠区间

未看解答自己编写的青春版

因为上一题刚学完箭,这一题可以直接现学现卖,思路还是先算射箭的最小数目,当然区间判断条件要稍微改动,加一个等号,然后,要删除的区间数量,就等于总数量减去箭的数量,箭的数量,就是我们去掉所有重复集合后,剩余的集合数量。

当然,不得不承认的是,如果不是因为上一题是箭,这个方法根本想不到。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if len(intervals) == 0: return 0
        intervals.sort(key=lambda x: x[0])
        result = 1
        for i in range(1, len(intervals)):
            if intervals[i][0] >= intervals[i - 1][1]: 
                result += 1     
            else:
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1])
        return len(intervals)-result

然后就是这种直接计算重叠区间的方法,贪心思想很直观,但是是不是最优解,就不知道了,反正能通过。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if len(intervals) == 0: return 0
        intervals.sort(key=lambda x: x[0])
        result = 0
        for i in range(1, len(intervals)):
            if intervals[i][0] < intervals[i - 1][1]: 
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1])
                result += 1
        return result

重点

本题不管是,左边界排序,还是右边界排序,都是可以的。

按照代码随想录的说法,左边界排序,求的直接就是重叠区间个数;右边界排序,求的是非交叉区间的个数,但是我个人感觉,这里具体的意义,是和变量的定义相关的。

已经连续做了两道,和交叉区间,相关的题目,以后相关的题也是类似的解法。

这里放上代码随想录的解答链接,时时复习。
无重叠区间

代码随想录的代码

贪心 基于左边界

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        
        intervals.sort(key=lambda x: x[0])  # 按照左边界升序排序
        count = 0  # 记录重叠区间数量
        
        for i in range(1, len(intervals)):
            if intervals[i][0] < intervals[i - 1][1]:  # 存在重叠区间
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1])  # 更新重叠区间的右边界
                count += 1
        
        return count

贪心 基于左边界 把452.用最少数量的箭引爆气球代码稍做修改

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        
        intervals.sort(key=lambda x: x[0])  # 按照左边界升序排序
        
        result = 1  # 不重叠区间数量,初始化为1,因为至少有一个不重叠的区间
        
        for i in range(1, len(intervals)):
            if intervals[i][0] >= intervals[i - 1][1]:  # 没有重叠
                result += 1
            else:  # 重叠情况
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1])  # 更新重叠区间的右边界
        
        return len(intervals) - result

我的代码(当天晚上理解后自己编写)

关键在于理清逻辑,基于左边界,result从0开始计数,那么result代表的,就是重叠区间的个数,比如有3个重叠,result就是2,也就是要删除的区间的最小值。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if len(intervals) == 0: return 0
        intervals.sort(key=lambda x: x[0])
        result = 0
        for i in range(1, len(intervals)):
            if intervals[i][0] < intervals[i - 1][1]: 
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1])
                result += 1
        return result

763 划分字母区间

看了看解答,和我自己的思路基本一致,不可避免地要去统计每一个字符的最后出现位置,要用一个额外的循环。

未看解答自己编写的青春版

重点

先去计算一个哈希数组,来保存每一个字符的最后出现位置。同样,这里想到用哈希数组也是一个小技巧。

代码随想录的代码

贪心(版本一)
这个思路我已经想到了。

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        last_occurrence = {}  # 存储每个字符最后出现的位置
        for i, ch in enumerate(s):
            last_occurrence[ch] = i

        result = []
        start = 0
        end = 0
        for i, ch in enumerate(s):
            end = max(end, last_occurrence[ch])  # 找到当前字符出现的最远位置
            if i == end:  # 如果当前位置是最远位置,表示可以分割出一个区间
                result.append(end - start + 1)
                start = i + 1

        return result

贪心(版本二)与452.用最少数量的箭引爆气球 (opens new window)、435.无重叠区间 (opens new window)相同的思路。

这种思路在逻辑上不直观。

class Solution:
    def countLabels(self, s):
        # 初始化一个长度为26的区间列表,初始值为负无穷
        hash = [[float('-inf'), float('-inf')] for _ in range(26)]
        hash_filter = []
        for i in range(len(s)):
            if hash[ord(s[i]) - ord('a')][0] == float('-inf'):
                hash[ord(s[i]) - ord('a')][0] = i
            hash[ord(s[i]) - ord('a')][1] = i
        for i in range(len(hash)):
            if hash[i][0] != float('-inf'):
                hash_filter.append(hash[i])
        return hash_filter

    def partitionLabels(self, s):
        res = []
        hash = self.countLabels(s)
        hash.sort(key=lambda x: x[0])  # 按左边界从小到大排序
        rightBoard = hash[0][1]  # 记录最大右边界
        leftBoard = 0
        for i in range(1, len(hash)):
            if hash[i][0] > rightBoard:  # 出现分割点
                res.append(rightBoard - leftBoard + 1)
                leftBoard = hash[i][0]
            rightBoard = max(rightBoard, hash[i][1])
        res.append(rightBoard - leftBoard + 1)  # 最右端
        return res
            

我的代码(当天晚上理解后自己编写)

学习这种思路,重点在于先求哈希表以及max更新的操作。

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        last_occurence = {}
        for i,ch in enumerate(s):
            last_occurence[ch]=i

        res = []
        start = 0
        end = 0
        for i,ch in enumerate(s):
            end = max(end,last_occurence[ch])
            if i==end :
                res.append(end-start+1)
                start = i+1

        return res
        

56 合并区间

未看解答自己编写的青春版

Debug了三次,通过。通过找Bug,注意点我已经写在注释了。

总结下来主要就是:不要忘记排序,注意append逻辑的位置,注意更改右区间。

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        # 细节一:记得先排序
        intervals.sort(key=lambda x: x[0])
        res = []
        for i in range(len(intervals)-1):
            if intervals[i][1] >= intervals[i+1][0] :
            # 细节2:不要把append放在这里,逻辑会乱掉,如果能合并,就在intervals里面修改
            # 修改区间去必须的,当不满足条件时,再append,自然就是合并后的区间了
               # res.append([intervals[i][0],intervals[i+1][1]])
                intervals[i+1][0] = intervals[i][0]
                # 可能存在上一个区间包含了下一个区间的情况,所以这里不仅要变化左区间,也要变化右区间
                # 变化右区间的思想,是在前面的题中已经涉及的,所以本题的新地方反倒是最容易想到的修改左值
                # 还是对右区间的改变掌握不到位
                intervals[i+1][1] = max(intervals[i+1][1],intervals[i][1])
            else :
                res.append(intervals[i])
        res.append(intervals[-1])
        return res

重点

但凡是做重叠区间的题,第一件事,一定是让重叠的区间尽可能挨在一起,所以要先排序!

代码随想录的代码

在区间处理上和我写的略有不同,代码随想录是先放入res数组,然后取区间和比较,均从res数组的最后一个元素取。理解下来,觉得这种方法可能更好一点。

class Solution:
    def merge(self, intervals):
        result = []
        if len(intervals) == 0:
            return result  # 区间集合为空直接返回

        intervals.sort(key=lambda x: x[0])  # 按照区间的左边界进行排序

        result.append(intervals[0])  # 第一个区间可以直接放入结果集中

        for i in range(1, len(intervals)):
            if result[-1][1] >= intervals[i][0]:  # 发现重叠区间
                # 合并区间,只需要更新结果集最后一个区间的右边界,因为根据排序,左边界已经是最小的
                result[-1][1] = max(result[-1][1], intervals[i][1])
            else:
                result.append(intervals[i])  # 区间不重叠

        return result

我的代码(当天晚上理解后自己编写)

我觉得自己写的这一版也挺好的。

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        # 细节一:记得先排序
        intervals.sort(key=lambda x: x[0])
        res = []
        for i in range(len(intervals)-1):
            if intervals[i][1] >= intervals[i+1][0] :
            # 细节2:不要把append放在这里,逻辑会乱掉,如果能合并,就在intervals里面修改
            # 修改区间去必须的,当不满足条件时,再append,自然就是合并后的区间了
               # res.append([intervals[i][0],intervals[i+1][1]])
                intervals[i+1][0] = intervals[i][0]
                # 可能存在上一个区间包含了下一个区间的情况,所以这里不仅要变化左区间,也要变化右区间
                # 变化右区间的思想,是在前面的题中已经涉及的,所以本题的新地方反倒是最容易想到的修改左值
                # 还是对右区间的改变掌握不到位
                intervals[i+1][1] = max(intervals[i+1][1],intervals[i][1])
            else :
                res.append(intervals[i])
        res.append(intervals[-1])
        return res

代码随想录的版本

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        result = []
        if len(intervals)==0 :
            return res
        intervals.sort(key=lambda x : x[0])
        result.append(intervals[0])

        for i in range(1,len(intervals)):
            if result[-1][1] >= intervals[i][0] :
                result[-1][1] = max(result[-1][1],intervals[i][1])

            else:
                result.append(intervals[i])

        return result

贪心系列总结3

本周的题,总的来说,都是求解重叠区间的类型,这类题我现在理解的有两个注意点。一是一定要先排序,并且可以统一就用左区间排序,不管什么问题,都用左区间排序。二是,一定要记得,在每次判断中,都要去修改右边界的值。放上代码随想录总结的链接如下。
贪心系列总结3

738 单调递增的数字

未看解答自己编写的青春版

没思路,总能想出一些特殊情形,处理不了。

重点

牛逼!这题的精妙点在于遍历顺序上。我一直先入为主,使用前序遍历,怪不得怎么想都不对!

要用后序遍历!做了这道题之后,也该有后序遍历的思维了!

还有一个很厉害的点,虽然本题的主要逻辑是,如果前一位大于后一位,比如32,就让前一位减一,后一位变为9,但是在处理中不能这样写,而是要用一个flag变量,来记录一个index,该index以后的位置,全部变成9。因为反例:1000,后面两个0是不比较的,因为比较逻辑是小于,不包括等于,不用flag,结果会是900,是错误的。

代码随想录的代码

暴力:会超时

class Solution:
    def checkNum(self, num):
        max_digit = 10
        while num:
            digit = num % 10
            if max_digit >= digit:
                max_digit = digit
            else:
                return False
            num //= 10
        return True

    def monotoneIncreasingDigits(self, N):
        for i in range(N, 0, -1):
            if self.checkNum(i):
                return i
        return 0

贪心(版本一)

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        # 将整数转换为字符串
        strNum = str(N)
        # flag用来标记赋值9从哪里开始
        # 设置为字符串长度,为了防止第二个for循环在flag没有被赋值的情况下执行
        flag = len(strNum)
        
        # 从右往左遍历字符串
        for i in range(len(strNum) - 1, 0, -1):
            # 如果当前字符比前一个字符小,说明需要修改前一个字符
            if strNum[i - 1] > strNum[i]:
                flag = i  # 更新flag的值,记录需要修改的位置
                # 将前一个字符减1,以保证递增性质
                strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + strNum[i:]
        
        # 将flag位置及之后的字符都修改为9,以保证最大的递增数字
        for i in range(flag, len(strNum)):
            strNum = strNum[:i] + '9' + strNum[i + 1:]
        
        # 将最终的字符串转换回整数并返回
        return int(strNum)

代码随想录给出的其他几个版本的贪心算法,我看了看在基本思路上没有变化,改变的只有编写代码的技巧,但是我觉得不如最基础的这一版好理解,而且也并没有使代码简洁或高效,所以后面我觉得也不用关注这些版本了。

我的代码(当天晚上理解后自己编写)

这份代码也是要学习的。

class Solution:
    def monotoneIncreasingDigits(self, n: int) -> int:
        # 将整数转换为字符串
        strnum = str(n)
        # flag用于记录从哪里全赋值为9,同时应初始化为字符串长度
        flag = len(strnum)

        # 倒序,从右向左遍历,很重要
        # 注意循环的起始和结束
        for i in range(len(strnum)-1,0,-1):
            if strnum[i-1] > strnum[i] :
                flag = i # 记录9需要修改的位置
                # 这里只将前一个字符减1
                strnum = strnum[:i-1]+str(int(strnum[i-1])-1)+strnum[i:]

        for i in range(flag, len(strnum)):
            strnum = strnum[:i] + '9' + strnum[i + 1:]

        return int(strnum)

968 监控二叉树

未看解答自己编写的青春版

一点思路没有。

重点

首先确定二叉树的遍历顺序,一定是后序遍历,因为要先处理叶子节点,往叶子节点的父节点,优先放摄像头。每隔两个节点,放置一个摄像头。

理清每个节点的三种状态,无覆盖,有摄像头,有覆盖。

接着就要判断:空节点是上述三个状态的哪个状态?根据排除法,空节点只能是“有覆盖”的状态。

在递归逻辑中,主要有以下四种情况:
情况1:左右节点都有覆盖
情况2:左右节点至少有一个无覆盖的情况
情况3:左右节点至少有一个有摄像头
情况4:头结点没有覆盖

主要思想:根据左右孩子的状态,来决定其父节点的状态。

代码随想录的代码

贪心(版本一)

class Solution:
         # Greedy Algo:
        # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
        # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
        # 0: 该节点未覆盖
        # 1: 该节点有摄像头
        # 2: 该节点有覆盖
    def minCameraCover(self, root: TreeNode) -> int:
        # 定义递归函数
        result = [0]  # 用于记录摄像头的安装数量
        if self.traversal(root, result) == 0:
            result[0] += 1

        return result[0]

        
    def traversal(self, cur: TreeNode, result: List[int]) -> int:
        if not cur:
            return 2

        left = self.traversal(cur.left, result)
        right = self.traversal(cur.right, result)

        # 情况1: 左右节点都有覆盖
        if left == 2 and right == 2:
            return 0

        # 情况2:
        # left == 0 && right == 0 左右节点无覆盖
        # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
        # left == 0 && right == 1 左节点无覆盖,右节点有摄像头
        # left == 0 && right == 2 左节点无覆盖,右节点覆盖
        # left == 2 && right == 0 左节点覆盖,右节点无覆盖
        if left == 0 or right == 0:
            result[0] += 1
            return 1

        # 情况3:
        # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        # left == 1 && right == 1 左右节点都有摄像头
        if left == 1 or right == 1:
            return 2

贪心(版本二)利用elif精简代码
不推荐,条件判断不清晰。

class Solution:
         # Greedy Algo:
        # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
        # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
        # 0: 该节点未覆盖
        # 1: 该节点有摄像头
        # 2: 该节点有覆盖
    def minCameraCover(self, root: TreeNode) -> int:
        # 定义递归函数
        result = [0]  # 用于记录摄像头的安装数量
        if self.traversal(root, result) == 0:
            result[0] += 1

        return result[0]

        
    def traversal(self, cur: TreeNode, result: List[int]) -> int:
        if not cur:
            return 2

        left = self.traversal(cur.left, result)
        right = self.traversal(cur.right, result)

        # 情况1: 左右节点都有覆盖
        if left == 2 and right == 2:
            return 0

        # 情况2:
        # left == 0 && right == 0 左右节点无覆盖
        # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
        # left == 0 && right == 1 左节点无覆盖,右节点有摄像头
        # left == 0 && right == 2 左节点无覆盖,右节点覆盖
        # left == 2 && right == 0 左节点覆盖,右节点无覆盖
        elif left == 0 or right == 0:
            result[0] += 1
            return 1

        # 情况3:
        # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        # left == 1 && right == 1 左右节点都有摄像头
        else:
            return 2

我的代码(当天晚上理解后自己编写)

这道题真的很难。

class Solution:
    def minCameraCover(self, root: Optional[TreeNode]) -> int:
        result = [0]
        if self.traversal(root,result)==0:
            result[0]+=1
        
        return result[0]

    def traversal(self,cur,result):
        if not cur :
            return 2

        left = self.traversal(cur.left,result)
        right = self.traversal(cur.right,result)

        if left==2 and right==2 :
            return 0

        if left == 0 or right == 0:
            result[0]+=1
            return 1

        if left == 1 or right == 1:
            return 2

贪心系列大总结

好好好!复习贪心的大纲!贪心的中等题和难题一定要多练。
贪心系列大总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值