跟随carl代码随想录刷题
语言:python
134. 加油站
题目:在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
👉示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
👉示例 2:
输入: gas = [2,3,4], cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
题目分析
for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while!
- 如果
gas总量
小于消耗总量
,那么一定不可能行驶一周。return -1
- 如果
gas总量
大于等于消耗总量
,那么一定可以行驶一周。- 那具体应该以哪个地方作为起点呢?
- i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,起始位置从i+1算起,再从0计算curSum。
- 那具体应该以哪个地方作为起点呢?
完整代码如下
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
start = 0
curSum = 0
totalSum = 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
if totalSum < 0: return -1
return start
738. 中等
单调递增的数字
题目:当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称
这个整数是单调递增的。
给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。
👉示例 1:
输入: n = 10
输出: 9
👉示例 2:
输入: n = 1234
输出: 1234
👉示例 3:
输入: n = 332
输出: 299
题目分析
先判断题目给定的数字本身是否是单调递增的。
- 遍历顺序:从后往前遍历:
- 如果后一位
小于
前一位,那么前一位减1
,后一位 :之后的位
都变成9
- 如果后一位
- 把
整数n
变成数组的形式:list(str(n))
- 计算时转化为数字:
int()
- 返回时转化成数字:
int(''.join(num))
,注意,num是字符串的形式。
完整代码如下
class Solution:
def monotoneIncreasingDigits(self, n: int) -> int:
num = list(str(n))
for i in range(len(num)-1, 0, -1): # 遍历范围为[len(num)-1, 1],因为range的右边界是开的,所以右边界应该写成`0`
if int(num[i]) < int(num[i-1]):
num[i-1] = str(int(num[i-1]) - 1)
num[i:] = '9' * (len(num) - i)
return int(''.join(num))
968. 困难
监控二叉树
题目:给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
题目分析
难点:
- 二叉树的遍历
- 如何隔两个节点放一个摄像头
遍历顺序:自底向上,从叶子节点的父节点处开始安装摄像头
- 因此,选择后序遍历
- 采用递归方式
def traversal(root):
if not root:
return
root.left = traversal(root.left)
# 中root.right = traversal(root.right)
# 右- 逻辑处理 # 中
return
如何隔两个节点放一个摄像头
- 状态转移
完整代码如下
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def minCameraCover(self, root: Optional[TreeNode]) -> int:
# Greedy Algo:
# 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
# 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
# 0: 该节点未覆盖
# 1: 该节点有摄像头
# 2: 该节点有覆盖
result = 0
# 从下往上遍历:后序(左右中)
def traversal(curr: TreeNode) -> int:
nonlocal result
if not curr: return 2
left = traversal(curr.left)
right = traversal(curr.right)
# Case 1:
# 左右节点都有覆盖
if left == 2 and right == 2:
return 0
# Case 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 += 1
return 1
# Case 3:
# left == 1 && right == 2 左节点有摄像头,右节点有覆盖
# left == 2 && right == 1 左节点有覆盖,右节点有摄像头
# left == 1 && right == 1 左右节点都有摄像头
elif left == 1 or right == 1:
return 2
# 其他情况前段代码均已覆盖
if traversal(root) == 0:
result += 1
return result