leetcode思路简述(171-200)
171. Excel表列序号
每次乘26再加上当前字符序号。
for c in s:
num = num * 26
num += ord(c) - ord('A') + 1
172. 阶乘后的零
对于10,只有2*5可以构成。把所有乘数能分解为2和5的都分解,显然2的数量远多于5,所以只需要乘数分解后统计5的数量。每隔5个数就会出现一次5的倍数。而每隔25个数,乘数中就出现两个5。
最终5的个数就是 n//5 + n//25 + n//125 ...
初始化 five = 1。
while n >= five:
five = five * 5
count += n // five
173. 二叉搜索树迭代器
由于空间限制,不能把所有结点放在列表中,所以每次放一条路径的。定义 leftmost(root) 函数:
while root:
self.stack.append(root)
root = root.left
每次 next 就 stack.pop(),如果 pop 的结点有右子树,leftmost(node.right)。
也就是二叉树中序遍历迭代形式的分解。二叉树中序遍历迭代:
stack = []while(root or stack):
while(p):
stack.append(root)
root = root.left
root = stack.pop()
访问 root
root = root.right
174. 地下城游戏
找一条路径,路径中健康值大于0的情况下,所需健康值最少。
如果自顶向下,要考虑的因素有当前健康值和历史最低健康值,似乎找不到合适的状态转移方程,无法保证走的路径是否正确。
从右下到左上动态规划,考虑的因素只有当前到终点所需健康值(此时路径中的最低不重要,只要求得左上角最小值,路径中健康值大于0肯定是满足的)。
也可以这样想,起点往终点时的初始健康值不知道,但是终点往起点走的初始健康值为1。
令 dp[i][j] 表示从 (i, j) 格子走到右下角所需要最小健康值。可以合成一维 n 位的 dp 数组,方便理解这里写二维。
(1) 初始化右下角
dp[m-1][n-1] = max(1, 1 - dungeon[m-1][n-1])
(2) 初始化最右一列和最下一行
for i in range(m-2, -1, -1):
dp[i][n-1] = max(1, dp[i+1][n-1] - dungeon[i][n-1])
for i in range(n-2, -1, -1):
dp[m-1][i] = max(1, dp[m-1][i+1] - dungeon[m-1][i])
(3) 计算其他格
for i in range(m-2, -1, -1):
for j in range(n-2, -1, -1):
dp[i][j] = max(1, min(dp[i+1][j],dp[i][j+1]) - dungeon[i][j])
(4) 最后 return dp[0][0]
179. 最大数
令两个字符串连接符号为加号,如果 a + b > b + a 且 b + c > c + b,则 a + c > c + a,这个大小是传递的。
所以按这个定义字符串间大小规则,进行排序即可。
187. 重复的DNA序列
令结果集合 ans 和 哈希表 seen 都为 set,窗口大小为10向后移动,如果窗口字符串在 seen 中则保存到 ans,否则放到 seen 里。
188. 买卖股票的最佳时机 IV
动态规划。初始化三维数组dp[len(prices)+1][k+1][2],dp[i][j][0] 维度表示第 i 天 第 k 次交易,最后一维表示是否持股,dp 值表示当前 profit。
初始化 dp[0][j][0] 和 dp[0][j][1] = 0(第0天利润为 0);dp[i][0][0] = 0(没交易过利润为0)和 dp[i][0][1] = float("-inf")(没交易不可能持股)。
则状态转移方程为:dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) 即前一天没持股与前一天持股今天卖掉中的最大值;p[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) 即前一天持股和前一天没持股今天买了的最大值。
然后循环对每个 i = [1,len(prices)],j = 1 和 2,是否持股等于 0 和 1,更新 dp。最后返回 dp[len(prices)][2][0]。
k 可能设置的很大, 如果 k > day // 2,相当于第 122 题,一次遍历就可以了。
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
day = len(prices)
# 可交易次数大于天数
if 2 * k > day:
profit = 0
for i in range(len(prices)-1):
if prices[i] < prices[i+1]:
profit += prices[i+1] - prices[i]
return profit
# 初始化
dp = [[[0, 0] for _ in range(k+1)] for _ in range(day+1)]
for j in range(k+1):
dp[0][j][0] = 0
dp[0][j][1] = float("-inf")
# 计算
for i in range(1,day+1):
dp[i][0][0] = 0
dp[i][0][1] = float("-inf")
for j in range(1, k+1):
dp[i][j][0] = max(dp[i-1][j][1]+prices[i-1],dp[i-1][j][0])
dp[i][j][1] = max(dp[i-1][j-1][0]-prices[i-1],dp[i-1][j][1])
return dp[day][k][0]
在存储上可以优化成二维数组,因为算到第 i 天时,第 i-1 天的 dp 就可以覆盖掉了。
189. 旋转数组
① 环形移动。
首先 k = k % n。i 遍历数组,设置当前这轮循环起点 start = i,然后内层循环。把当前位置 cur 的数移动到应在的位置 next = (cur + k) % n,再将 next 位置的数作为 cur,移动到它应在的位置,如此循环。如果 cur == i 则停止这轮。
加个计数器,把一共移动了多少数记录下来,如果 count == n 说明所有数移动过了,可以返回了。
② 反转。
好像链表也有这种题,就是用的反转。
将所有元素反转。然后反转前 k 个元素。再反转后面 n-k 个元素。
190. 颠倒二进制位
① 逐位移动
取最右一位可以 n%2 或 n &1,取最右一位并移到某一位置 (n&1)<< power。遍历可以用 n = n >> 1。
class Solution:
def reverseBits(self, n):
ans, power = 0, 31
while n:
ans += (n&1) << power
n = n >> 1
power -= 1
return ans
② 分治
将 32 位分为 2 个 16 位的块,反转两块位置。再分和反转直到块大小为 1。将每一次划分的中间结果合并为一个整数。
n = (n >> 16) | (n << 16) n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8) n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4) n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2) n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1)
191. 位1的个数
n > 0 时进行循环:取最右一位判断是否为1 if n%2 == 1: ans++,然后 n = n >> 1。
198. 打家劫舍
动态规划。dp数组保存从左往右到第 i 家时能得到的最多的数目。
对于每个房子 i 有两种可能:1. 偷 i,得到的钱是这家的 nums[i] + 第 i-2 家时一共的数目 dp[i-2];2. 不偷 i,那么数目为到前一家的总数 dp[i-1]。
初始化 dp[0] = nums[1],dp[1] = max(nums[0], nums[1])。i 从 2 开始遍历,dp[i] = max(dp[i-2]+nums[i], dp[i-1])。最后返回 max(dp[-1], dp[-2])。
199. 二叉树的右视图
广度优先。每层最后一个结点放入结果列表中。下面代码的队列使用列表实现,也可用 queue 模块。
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if not root:
return []
queue = [root]
ans = []
while queue:
count = 0
num = len(queue)
while count < num:
count += 1
node = queue.pop(0)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
ans.append(node.val)
return ans
200. 岛屿数量
遍历 grid,如果为 ‘1’ ,把它标记为 ‘0’。深度优先搜索,island(i, j) 检查周边四个方向的点,遇到为 '1' 的标记为 '0',并对它使用 island 函数。最终岛屿数量就是主函数循环中遇到 '1' 的次数。