• 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' 的次数。

  • 相关阅读:
    常用PHP array数组函数
    每天学习30分钟新知识之html教程1
    laravel学习之路2: jwt集成
    JWT简介json web token bear token
    MDwiki 调研
    laravel学习之路1:认证相关
    OAuth 2.0介绍
    第一行代码 6.4 数据存储全方案-详解持久化数据- 数据库
    github(1)安装及使用图文详解
    Android集成讯飞语音、百度语音、阿里语音识别
  • 原文地址:https://www.cnblogs.com/sumuyi/p/12842545.html
Copyright © 2020-2023  润新知