LeetCode-算法:101-150(Python)


101. 对称二叉树

在这里插入图片描述
思路
递归与迭代的区别:递归是自己延伸出去,而迭代是将得到的结果替代自己
递归(函数不断引用自身,知道引用的对象已知):

  1. 根结点拆分为两个,即将原二叉树拆分为左子树和右子树
  2. 镜像比较即比较左节点和右节点结构与值是否相等
  3. 递归左子树和右子树
class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        def helper(p, q):
            if not p and not q:
                return True
            if not p or not q:
                return False
            if p.val != q.val:
                return False
            return helper(p.left, q.right) and helper(p.right, q.left)
        return helper(root, root)

思路
迭代(每一次迭代得到的结果作为下一次迭代的初始值):

  1. 插入两次根结点至队列中,及拆分为左右子树
  2. 取出队列的前两个结点进行比较
  3. 插入左边的左、右结点和右边的右、左结点至队列中,如果镜像相等则队列的前两个结点相等
class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        p, q = root, root
        queue = list()
        queue.append(p)
        queue.append(q)
        while queue:
            p = queue.pop(0)
            q = queue.pop(0)
            if not p and not q:    # 左右结点为空但未遍历结束
                continue
            if not p or not q:    # 左右结点一个有值一个为空
                return False
            if p.val != q.val:    # 左右结点值不相等
                return False
            queue.append(p.left)    
            queue.append(q.right)
            queue.append(p.right)
            queue.append(q.left)
        return True



102. 二叉树的层序遍历

在这里插入图片描述
思路
BFS(宽度优先搜索算法)的基础上修改为层次遍历

  1. 将根结点root保存在队列queue中
  2. 当队列不为空时,计算队列的长度(len(queue)),从队头中取出len(queue)个结点,即一层的结点数,并插入其子节点至队末
  3. 将一层的结点的值保存在临时数组tmp中
  4. 将tmp添加至返回数组ans中
class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        queue = list()
        ans = list()
        queue.append(root)
        if not root:    # 列表为空,返回空列表
            return ans
        while queue:
            tmp = list()   # 保存每层的结点值
            for i in range(len(queue)):    # 从队列中取出上一层的结点,并插入子节点至队末
                node = queue.pop(0)
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            ans.append(tmp)
        return ans



103. 二叉树的锯齿形层次遍历

在这里插入图片描述
思路
上一题102. 二叉树的层序遍历 的思路
偶数行时,将数组reverse

class Solution(object):
    def zigzagLevelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        queue = list()
        ans = list()
        queue.append(root)
        direction = False
        if not root:    # 列表为空,返回空列表
            return ans
        while queue:
            tmp = list()
            for i in range(len(queue)):
                node = queue.pop(0)
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            if direction:
                tmp.reverse()
                ans.append(tmp)
            else:
                ans.append(tmp)
            direction = not direction
        return ans



104. 二叉树的最大深度

在这里插入图片描述
思路
递归

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        leftDepth = self.maxDepth(root.left)
        rightDepth = self.maxDepth(root.right)
        return max(leftDepth, rightDepth) + 1



105. 从前序与中序遍历序列构造二叉树

在这里插入图片描述
思路
迭代:
preorder:前序遍历,根左右
inorder: 中序遍历,左根右

  1. 根root为preorder第0位,即preorder[0],并把根结点root压进堆stack中
  2. inorderIndex指向中序遍历第0位
  3. 遍历preorder剩下的数,查看stack[-1]栈顶的数node(3, 已经构建为数的结点)是否等于inorder[0](9, 最左边的结点):
    • 如果不等于,即node不是最左边的结点(node.val:3 != 9),则node的左节点为preorder1,并将其压进堆stack中。
    • 如果等于,且栈不为空(栈中保存了已构造的结点),栈顶的值等于inorder的值时,将其从栈中pop出,inorderIndex往右移动一位直到inorderIndex指向的数与栈顶的数不相等时,即preorder[2]为node的右节点,并将其压进堆stack中
class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if not preorder:
            return None

        root = TreeNode(preorder[0])
        stack = [root]
        inorderIndex = 0
        for i in range(1, len(preorder)):
            node = stack[-1]
            if node.val != inorder[inorderIndex]:
                node.left = TreeNode(preorder[i])
                stack.append(node.left)
            else:
                while stack and stack[-1].val == inorder[inorderIndex]:
                    node = stack.pop()
                    inorderIndex += 1
                node.right = TreeNode(preorder[i])
                stack.append(node.right)
        return root



106. 从中序与后序遍历序列构造二叉树

在这里插入图片描述
思路
迭代:
inorder: 中序遍历,左根右
postorder:后序遍历,左右根
参考上一题105. 从前序与中序遍历序列构造二叉树

class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        if not postorder:
            return None
            
        root = TreeNode(postorder[-1])
        stack = [root]
        inorderInder = len(inorder) -1
        for i in range(len(postorder)-2, -1, -1):
            node = stack[-1]
            if node.val != inorder[inorderInder]:
                node.right = TreeNode(postorder[i])
                stack.append(node.right)
            else:
                while stack and stack[-1].val == inorder[inorderInder]:
                    node = stack.pop()
                    inorderInder -= 1
                node.left = TreeNode(postorder[i])
                stack.append(node.left)
        return root



107. 二叉树的层次遍历 II

在这里插入图片描述
思路
参考 102. 二叉树的层序遍历

class Solution(object):
    def levelOrderBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        queue = list()
        ans = list()
        queue.append(root)
        if not root:    # 列表为空,返回空列表
            return ans
        while queue:
            tmp = list()   # 保存每层的结点值
            for i in range(len(queue)):    # 从队列中取出上一层的结点,并插入子节点至队末
                node = queue.pop(0)
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            ans.insert(0, tmp)   # 将102题的append修改为insert
        return ans



108. 将有序数组转换为二叉搜索树

在这里插入图片描述
思路
递归:
中间结点作为跟结点,左边数组递归构造左子树,右边数组递归构造右子树

class Solution(object):
    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        if not nums:
            return None
        mid = len(nums)//2
        root = TreeNode(nums[mid])
        root.left = self.sortedArrayToBST(nums[:mid])
        root.right = self.sortedArrayToBST(nums[mid+1:])
        return root



109. 有序链表转换二叉搜索树

在这里插入图片描述
思路
递归:

  1. 当链表head为空时返回空;当链表只有一个结点时直接返回
  2. two走两步,one走一步,当two走到链表尾时,one在链表中间,得到根结点
  3. 递归左子树,递归右子树
class Solution(object):
    def sortedListToBST(self, head):
        """
        :type head: ListNode
        :rtype: TreeNode
        """
        if not head:
            return None
        if head.next == None:
            return TreeNode(head.val)
        one, two = head.next, head.next.next
        pre = head
        while two and two.next:
            pre = one
            one = one.next
            two = two.next.next
        pre.next = None
        root = TreeNode(one.val)
        root.left = self.sortedListToBST(head)
        root.right = self.sortedListToBST(one.next)
        return root



110. 平衡二叉树

在这里插入图片描述
思路
递归:

  1. 使用104. 二叉树的最大深度 辅助求左右子树的高度
  2. 左右子树高度相差小于2时,递归左子树,递归右子树
class Solution(object):
    def maxDepth(self, root):
        if not root:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        return abs(self.maxDepth(root.left) - self.maxDepth(root.right)) < 2 and self.isBalanced(root.left) and self.isBalanced(root.right)



111. 二叉树的最小深度

在这里插入图片描述
思路
递归:

  1. 二叉树为空,返回0
  2. 二叉树只有根结点,返回1
  3. 设置最小高度为无限大,如果有左子树,计算左子树的最小高度;如果有右子树,计算右子树的最小高度
class Solution(object):
    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        if not root.right and not root.left:
            return 1
        minD = float("inf")
        if root.left:
            minD = min(minD, self.minDepth(root.left))
        if root.right:
            minD = min(minD, self.minDepth(root.right))
        return minD + 1



112. 路径总和

在这里插入图片描述
思路
深度优先搜索DFS:
sum减去当前结点的值,当遍历到叶子结点时(root.left和root.right为None),判断sum是否为0,是返回True,否返回False

class Solution(object):
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        if not root:
            return False
        sum -= root.val
        if not root.left and not root.right:
            return sum == 0
        return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum)



113. 路径总和 II

在这里插入图片描述
思路
深度优先搜索DFS:
参考 112. 路径总和 当没有子节点时,tmp保存结点的值,当总和等于sum时添加至tmp至ans中。tmp.pop()的作用是回溯

class Solution(object):        
    def pathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: List[List[int]]
        """   
        def helper(root, sum):
            if not root:
                return
            tmp.append(root.val)
            if not root.left and not root.right and root.val == sum:
                t = copy.copy(tmp)    # 数组是引用类型,不copy的话,tmp变化后,ans里的tmp也会随之变化
                ans.append(t)
            helper(root.left, sum-root.val)
            helper(root.right, sum-root.val)
            tmp.pop()
        tmp=list()
        ans = list()
        helper(root, sum)
        return ans



114. 二叉树展开为链表

在这里插入图片描述
思路

  1. 先序遍历把结点保存在列表中
  2. 链接列表中的结点到原二叉树中
class Solution(object):
    def preorder(self, root):
        return [] if root == None else [root] + self.preorder(root.left) + self.preorder(root.right)
    def flatten(self, root):
        """
        :type root: TreeNode
        :rtype: None Do not return anything, modify root in-place instead.
        """
        pre = self.preorder(root)
        tmp = root
        for i in range(1, len(pre)):
            tmp.right = pre[i]
            tmp.left = None
            tmp = tmp.right



115. 不同的子序列

在这里插入图片描述
在这里插入图片描述
思路
动态规划:
栗子

dpS“”rabbbit
T“”11111111
-r01111111
-a00111111
-b00012333
-b00001333
-i00000033
-t00000003
  1. 创建dp数组,长为S字符串的长度n加1,高为T字符串的长度m加1
  2. 初始化第一行和第一列:
    • 第一行dp[0][i]:当T为空"“时,S只有一个子序列”"等于T
    • 第一列dp[i][0]:当S为空""时,T不为空时,S的子序列中不包含T,均为0
  3. 递推公式:
    • 当t[i-1]==s[j-1]时(i的区间是[1:m], j的区间是[1:n]):dp[i][j]=dp[i-1][j-1]+dp[i][j-1](dp[i-1][j-1]意味着s[:j-1]子序列中出现t[:i-1]的次数, dp[i][j-1]意味着s[:j-1]中出现t[:i]的次数)
    • 当t[i-1]!=s[j-1]时:dp[i][j]=dp[i][j-1]
class Solution(object):
    def numDistinct(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: int
        """
        m, n = len(t), len(s)
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(n+1):
            dp[0][i] = 1
        for i in range(1, m+1):
            for j in range(1, n+1):
                if t[i-1] == s[j-1]:
                    dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
                else:
                    dp[i][j] = dp[i][j-1]
        return dp[-1][-1]



116. 填充每个节点的下一个右侧节点指针

在这里插入图片描述
思路
参考 102. 二叉树的层序遍历
BFS(宽度优先搜索算法)的基础上修改为层次遍历

  1. 将根结点root保存在队列queue中
  2. 当队列不为空时,计算队列的长度(len(queue)),从队头中取出第一个结点pre, 再遍历该层后面的结点cur,将pre.next指向cur后,插入其子节点至队末,并更新pre为cur
  3. 返回root
class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root:
            return root
        queue = [root]
        while queue:
            len_queue = len(queue)
            pre = queue.pop(0)
            if pre.left:
                queue.append(pre.left)
            if pre.right:
                queue.append(pre.right)
            for i in range(1, len_queue):
                cur = queue.pop(0)
                pre.next = cur
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
                pre = cur
        return root



117. 填充每个节点的下一个右侧节点指针 II

在这里插入图片描述
思路
参考 116. 填充每个节点的下一个右侧节点指针

class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root:
            return root
        queue = [root]
        while queue:
            len_queue = len(queue)
            pre = queue.pop(0)
            if pre.left:
                queue.append(pre.left)
            if pre.right:
                queue.append(pre.right)
            for i in range(1, len_queue):
                cur = queue.pop(0)
                pre.next = cur
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
                pre = cur
        return root



118. 杨辉三角

在这里插入图片描述
思路
在杨辉三角中,每一行的第一位和最后一位是1,其他数是它左上方和右上方的数的和

class Solution(object):
    def generate(self, numRows):
        """
        :type numRows: int
        :rtype: List[List[int]]
        """
        ans = list()
        for i in range(numRows):
            tmp = [None]*(i+1)
            for j in range(i+1):
                tmp[j] = 1 if j == 0 or j == i else ans[i-1][j-1] + ans[i-1][j]
            ans.append(tmp)
        return ans



119. 杨辉三角 II

在这里插入图片描述
思路
参考 118. 杨辉三角

class Solution(object):
    def getRow(self, rowIndex):
        """
        :type rowIndex: int
        :rtype: List[int]
        """
        ans = [None] * (rowIndex+1)
        for i in range(rowIndex+1):
            tmp = copy.copy(ans)
            for j in range(i+1):
                ans[j] = 1 if j==0 or j==i else tmp[j-1] + tmp[j]
        return ans



120. 三角形最小路径和

在这里插入图片描述
思路
动态规划

  1. 创建dp数组,第1位即为第一层的最短路径triangle[0][0]
  2. 大于1行后,第一位dp[0]最小路径和为triangle当前值加上一层dp0的值;最后一位最小路径和为triangle当前值加上一层dp最后一个值;中间位置的最短路径为triangle当前值加上一层结点下标等于当前下标或上一层结点下标 -1中的较小值更新为当前位置的最小路径和
class Solution(object):
    def minimumTotal(self, triangle):
        """
        :type triangle: List[List[int]]
        :rtype: int
        """
        if not triangle:
            return 0
        n = len(triangle)
        dp = [None] * n
        dp[0] = triangle[0][0]
        for i in range(1, n):
            tmp = copy.copy(dp)      # tmp保存上一层的最小路径和
            for j in range(i+1):    # 更新dp为当前层的最小路径和
                if j == 0:
                    dp[j] = triangle[i][j]+tmp[j]     
                elif j==i:
                    dp[j] = triangle[i][j]+tmp[j-1]
                else:
                    dp[j] = triangle[i][j] + min(tmp[j], tmp[j-1])
        return min(dp)



121. 买卖股票的最佳时机

在这里插入图片描述
思路
第一天开始可以买股票,第二天开始可以卖股票。可以卖股票时,减去在前几天能买股票的最低价格

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        if not prices:
            return 0
        n = len(prices)
        mini, profit = prices[0], 0
        for i in range(1, n):
            profit = max(profit, prices[i]-mini)
            mini = min(mini, prices[i])
        return profit



122. 买卖股票的最佳时机 II

在这里插入图片描述
思路
当天价格比前一天的价格高时,卖出股票
栗子:prices = [7,1,5,6,3,6,4]
在这里插入图片描述

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        n = len(prices)
        cost, profit = prices[0], 0
        for i in range(1, n):
            if prices[i] > cost:    # 当天价格比前一天的价格高时,卖出股票
                profit += prices[i] - cost
            cost = prices[i]    # 更新成本位当天的价格
        return profit



123. 买卖股票的最佳时机 III

在这里插入图片描述
思路
见代码备注

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        if not prices:
            return 0
        buy1, sell1, buy2, sell2 = -prices[0], -float("inf"), -float("inf"), -float("inf")
        for i in range(1, len(prices)):
            buy1 = max(buy1, -prices[i])    # 第一次买股票,负数更大表示成本更低
            sell1 = max(sell1, prices[i]+buy1)    # 第一次卖股票, 当前价格高于buy1时卖出(buy1为负数),得到第一次卖股票的利润
            buy2 = max(buy2, sell1-prices[i])    # 第二次买股票后剩余的钱
            sell2 = max(sell2, prices[i]+buy2)    # 第二次卖股票,当前价格加上第二次买股票后剩余钱,得到第二次卖股票的利润
            
        return max(0, sell2)



124. 二叉树中的最大路径和

在这里插入图片描述
思路
回溯

  1. 递归计算左子树最大路径和
  2. 递归计算右子树最大路集和
  3. 更新结点的最大路径和,由左子树最大路径和+加当前值+右子树最大路集和。路径是左子树到结点到右子树
  4. 更新单边最大路径和,如果加上左右的路径和,路径会出现分岔

栗子:
在这里插入图片描述

class Solution(object):
    def __init__(self):
        self.max_val = -float("inf")
    def maxPathSum(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        def maxSum(root):
            if not root:
                return 0
            left_val = max(0, maxSum(root.left))    # 左子树的最大路径和
            right_val = max(0, maxSum(root.right))    # 右子树的最大路径和
            self.max_val = max(self.max_val, root.val + left_val + right_val)    # 更新结点的最大路径和
            return root.val+max(left_val, right_val)     # 更新单边最大路径和

        maxSum(root)
        return self.max_val



125. 验证回文串

在这里插入图片描述
思路

  1. 过滤剩下数字和字母后全部变为小写
  2. s[::-1]可翻转字符串,与原字符串比较

注:列表翻转可用reversed(s)

class Solution(object):
    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        s = "".join(filter(str.isalnum, str(s))).lower()
        return s[::-1]==s



126. 单词接龙 II

在这里插入图片描述
思路

  1. 构建neighbor用于查找邻接词,单词长度通常较小,有一位不相等的即为邻居
  2. 构建preWords记录结点的前驱结点
  3. 构建queue用于广度优先搜索遍历
  4. 构建visited记录已访问的结点
  5. 搜索到endWord或当前层级大于已得到的路径长度时停止遍历
  6. 利用列表嵌套推导式得到答案

栗子
输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
广度优先搜索遍历得到:
preWords = defaultdict(<class ‘list’>, {‘hot’: [‘hit’], ‘dot’: [‘hot’], ‘lot’: [‘hot’], ‘dog’: [‘dot’], ‘log’: [‘lot’], ‘cog’: [‘dog’, ‘log’]})
visited = <class ‘dict’>: {‘hit’: 1, ‘hot’: 2, ‘dot’: 3, ‘lot’: 3, ‘dog’: 4, ‘log’: 4, ‘cog’: 5}

from collections import deque, defaultdict
class Solution(object):
    def findLadders(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: List[List[str]]
        """
        if endWord not in wordList:
            return []
        wordList.append(beginWord)
        wordList = list(set(wordList))    # 去重
        neighbor = defaultdict(list)    # 构建neighbor用于查找邻接词
        preWords = defaultdict(list)    
        queue = deque([(beginWord, 1)])    # 将单词和层级放到队列中
        visited = {beginWord:1}

        for word in wordList:
            for i in range(len(beginWord)):
                neighbor[word[0:i]+'*'+word[i+1:]].append(word)
                    
        while queue:
            curWord, level = queue.popleft()     # popleft()取左边的结点
            for i in range(len(beginWord)):
                w = curWord[0:i]+'*'+curWord[i+1:]
                for word in neighbor[w]:
                    if word not in visited:
                        visited[word] = level + 1
                        queue.append((word, level+1))
                    if visited[word] == level + 1:    # 深度为curWord加1时,讲curWord作为word的前驱词
                        preWords[word].append(curWord)
            if endWord in visited and level + 1 > visited[endWord]:    # 搜索到endWord或当前层级大于已得到的路径长度
                break

        if endWord in visited:
            ans = [[endWord]]
            while ans[0][0] != beginWord:
                ans = [[word] + pre for pre in ans for word in preWords[pre[0]]]
            return ans
        else:
            return []



127. 单词接龙

在这里插入图片描述
思路
参考 126. 单词接龙 II

class Solution(object):
    def ladderLength(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: int
        """
        if endWord not in wordList:
            return 0
        wordList.append(beginWord)
        wordList = list(set(wordList))    # 去重
        neighbor = defaultdict(list)    # 构建neighbor用于查找邻接词
        preWords = defaultdict(list)    
        queue = deque([(beginWord, 1)])    # 将单词和层级放到队列中
        visited = {beginWord:1}

        for word in wordList:
            for i in range(len(beginWord)):
                neighbor[word[0:i]+'*'+word[i+1:]].append(word)
                    
        while queue:
            curWord, level = queue.popleft()     # popleft()取左边的结点
            for i in range(len(beginWord)):
                w = curWord[0:i]+'*'+curWord[i+1:]
                for word in neighbor[w]:
                    if word not in visited:
                        visited[word] = level + 1
                        queue.append((word, level+1))
                    if visited[word] == level + 1:    # 深度为curWord加1时,讲curWord作为word的前驱词
                        preWords[word].append(curWord)
            if endWord in visited and level + 1 > visited[endWord]:    # 搜索到endWord或当前层级大于已得到的路径长度
                break
        
        return visited[endWord] if endWord in visited else 0



128. 最长连续序列

在这里插入图片描述
思路

  1. 当存在比自身更小的数时,什么都不做
  2. 当不存在比自身更小的数时,连续寻找比自己大1的数并记录长度
class Solution(object):
    def longestConsecutive(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums = list(set(nums))
        maxLen = 0
        for num in nums:
            if num-1 not in nums:
                curNum = num
                curlen = 1
                while curNum+1 in nums:
                    curNum += 1
                    curlen += 1
                maxLen = max(maxLen, curlen)
        return maxLen



129. 求根到叶子节点数字之和

在这里插入图片描述
思路

  1. 当前结点的值为父节点的值tmp乘以10加上当前结点的值
  2. 走到叶子结点时返回结果
  3. 遍历左子树和右子树并相加
class Solution(object):
    def sumNumbers(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        def helper(root, tmp=0):
            if not root:
                return 0
            tmp = tmp*10+root.val    # 当前结点的值为父节点的值tmp乘以10加上当前结点的值
            if not root.left and not root.right:    # 叶子结点
                return tmp
            return helper(root.left, tmp) + helper(root.right, tmp)
        return helper(root)



130. 被围绕的区域

在这里插入图片描述
思路

  1. 边界有“0”时,入队queue
  2. 当queue不为空时,遍历(x, y)相邻的点,当为“O”时,记录在connected中,即与边界“O”相连的点
  3. 遍历board中间的点,没有与边界“O”相连且等于“O”的点(不在connected中的点)修改为“X”
class Solution(object):
    def solve(self, board):
        """
        :type board: List[List[str]]
        :rtype: None Do not return anything, modify board in-place instead.
        """
        if not board:
            return
        m, n = len(board), len(board[0])    
        queue = list()
        for i in range(m):
            if board[i][0] == "O":
                queue.append((i,0))
            if board[i][n-1] == "O":
                queue.append((i, n-1))
        for i in range(n):
            if board[0][i] == "O":
                queue.append((0, i))
            if board[m-1][i] == "O":
                queue.append((m-1, i))
                
        connected = queue[:]
        while queue:
            x, y = queue.pop(0)
            for xx, yy in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]:    # 遍历(x, y)相邻的点
                if 0<xx<m and 0<yy<n:
                    if (xx, yy) not in connected and board[xx][yy]=="O":
                        connected.append((xx, yy))
                        queue.append((xx, yy))
        for i in range(1, m):
            for j in range(1, n):
                if (i, j) not in connected and board[i][j] == "O":
                    board[i][j]="X"



131. 分割回文串

在这里插入图片描述
思路
回溯

class Solution(object):
    def partition(self, s):
        """
        :type s: str
        :rtype: List[List[str]]
        """
        ans = list()
        def backtrack(s, start, length, tmp=list()):
            if start == length:
                ans.append(tmp[:])
            for i in range(start, length):    # 判断s[start:i+1]不是回文,跳过
                if s[start:i+1] == s[start:i+1][::-1]:
                    backtrack(s, i+1, length, tmp+[s[start:i+1]])    # 判断s[i+1,length]是否回文
        backtrack(s, 0, len(s))
        return ans



132. 分割回文串 II

在这里插入图片描述
思路
动态规划:

  1. 当s长度n为0或1时,无需分割,返回0
  2. 创建长度为n的数组dp,单个字符一定是回文,因此初始化dp数组为0到n。
  3. 遍历字符串s,如果长度为i的字符是回文,则dp[i]=0
  4. 长度为i的字符非回文时,需要分割。求状态转移方程:遍历i前面的字符,索引为j,如果长度s[j+1,i+1]是回文字符的话,就在dp[j]的基础上加1则可。因此,状态转移方程为:dp[i] = min(dp[j] + 1 for j in range(i) if s[j+1:i+1]==s[j+1:i+1][::-1])
class Solution(object):
    def minCut(self, s):
        """
        :type s: str
        :rtype: int
        """
        n = len(s)
        if n < 2:
            print(0)
        dp = [i for i in range(n)]
        for i in range(1, n):
            if s[0:i+1] == s[0:i+1][::-1]:
                dp[i] = 0
                continue
            dp[i] = min(dp[j] + 1 for j in range(i) if s[j+1:i+1]==s[j+1:i+1][::-1]) 
        return dp[-1]



133. 克隆图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路
广度优先遍历 BFS

class Solution(object):
    def cloneGraph(self, node):
        """
        :type node: Node
        :rtype: Node
        """
        if not node:
            return node
            
        visited = dict()
        visited[node] = Node(node.val)
        queue = [node]
        while queue:
            n = queue.pop()
            for neighbor in n.neighbors:
                if neighbor not in visited:
                    queue.append(neighbor)
                    visited[neighbor] = Node(neighbor.val)
                visited[n].neighbors.append(visited[neighbor])
        return visited[node]



134. 加油站

在这里插入图片描述
在这里插入图片描述
思路

  1. 当gas数组的总和大于等于cost数组的总和一定能绕环路行驶一周,res_gas为gas减cost(因为如果题目有解,该答案即为唯一答案)
    1. 出发点start,当前点cur,当前油量为cur_gas,当cur_gas小于0时,即start到cur不够油,则更新cur_gas为0,下一个点再出发start=cur+1
    2. 当出发点start到达起始点0点时,如res_gas大于等于0,即start为唯一答案,返回start
  2. 当gas数组的总和小于cost数组的总和则无法绕环路行驶一周(显而易见)。返回-1
class Solution(object):
    def canCompleteCircuit(self, gas, cost):
        """
        :type gas: List[int]
        :type cost: List[int]
        :rtype: int
        """
        start, cur_gas, res_gas = 0, 0, 0
        for cur in range(len(gas)):
            cur_gas = cur_gas + gas[cur]-cost[cur]
            res_gas = res_gas + gas[cur]-cost[cur]
            if cur_gas < 0:
                cur_gas = 0
                start = cur + 1
        return -1 if res_gas < 0 else start



135. 分发糖果

在这里插入图片描述
思路

  1. 创建candys数组记录给孩子的糖果数
  2. 往右遍历,右边孩子评分高于左边时,右边孩子的糖果数比左边多1个
  3. 往左遍历,左边孩子评分高于右边时,左边孩子的糖果数比右边多1个,比较步骤2的结果取较大值
  4. 返回candys数组的总和
class Solution(object):
    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        if not ratings:
            return 0
        n = len(ratings)
        candys = [1 for _ in range(n)]
        for i in range(1, n):
            if ratings[i] > ratings[i-1]:
                candys[i] = candys[i-1] + 1 
        for i in range(n-2, -1, -1):
            if ratings[i] > ratings[i+1]:
                candys[i] = max(candys[i], candys[i+1]+1)
        return sum(candys)



136. 只出现一次的数字

在这里插入图片描述
思路
reduce() 函数会对参数序列中元素进行累积。

函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果

nums里的数做异或操作:
0^num = num
num1 ^ num1 = 0

from functools import reduce
class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        return reduce(lambda x, y: x^y, nums)



137. 只出现一次的数字 II

在这里插入图片描述
思路
位运算

位运算符说明(python)
<<按位左移,左移n位相当于乘以2的n次方
>>按位右移 ,左移n位相当于除以2的n次方
&按位与,二进制位数同且为1结果位为1
l按位或 ,二进制位数或有1结果位为1
^按位异或 ,二进制位数不同结果位为1
~按位取反,二进制位0和1结果位互换
class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        once, twice = 0, 0
        for num in nums:
            once = ~twice&(once^num)
            twice = ~once&(twice^num)
        return once



138. 复制带随机指针的链表

在这里插入图片描述
在这里插入图片描述
思路

  1. 将克隆的结点clone链接在原结点后(node1–>clone1–>node2–>clone2–>…)
  2. 拷贝random:node1.next.random(clone1) = node1.random.next
  3. 连接克隆的结点:node1–>node2–>…, clone1–>clone2–>…
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:
            return head
        node = head
        while node:
            clone = Node(node.val, None, None)
            clone.next = node.next
            node.next = clone
            node = clone.next
        node = head
        while node:
            node.next.random = node.random.next if node.random else None
            node = node.next.next
        node = head
        clone = head.next
        ans = head.next
        while node:
            node.next = clone.next
            clone.next = node.next.next if clone.next else None
            node = node.next
            clone = clone.next
        return ans



139. 单词拆分

在这里插入图片描述
思路
动态规划

class Solution(object):
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: bool
        """
        n = len(s)
        dp = [False for _ in range(n+1)]
        dp[0] = True
        for i in range(n+1):
            for j in range(i):
                if dp[j] and (s[j:i] in wordDict):     # s前i个字符中,前j个字符存在wordDict中,后j到i个字符也存在wordDict中时,dp[i]为True
                    dp[i] = True
                    break
        return dp[-1]



140. 单词拆分 II

在这里插入图片描述
思路

  1. 回溯 参考131.分割回文串,提交会超时
  2. 优化:连接wordDict为一个字符串,遍历s字符串中的字符,有不存在wordDict的字符时返回空数组
class Solution(object):
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        def backtrack(s, wordDict, start, n, tmp=list()):
            if start == n:
                ans.append(" ".join(tmp))
            for i in range(start, n):
                if s[start:i+1] in wordDict:
                    backtrack(s, wordDict, i+1, n, tmp+[s[start:i+1]])
        ans = list()
        words = "".join(wordDict)
        for char in s:
            if char not in words:
                return ans
        backtrack(s, wordDict, 0, len(s), [])
        return ans



141. 环形链表

在这里插入图片描述
思路

  1. visited数组添加已访问的结点
  2. 链表中有环则会访问已访问的结点
class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        visited = list()
        while head:
            if head in visited:
                return True
            else:
                visited.append(head)
            head = head.next
        return False



142. 环形链表 II

在这里插入图片描述
思路
参考 141. 环形链表

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        visited = list()
        ans = head
        while ans:
            if ans in visited:
                return ans
            else:
                visited.append(ans)
            ans = ans.next
        return None



143. 重排链表

在这里插入图片描述
思路

  1. 用快慢指针找到中间结点first
  2. 将后半段结点倒转,如1–>2–3-->4–>5–>6变为1–>2–>3–>6–>5–>4
  3. 将后半段结点逐一插入前半段结点中。如1–>6–>2–>5–>3–>4
class Solution(object):
    def reorderList(self, head):
        """
        :type head: ListNode
        :rtype: None Do not return anything, modify head in-place instead.
        """
        if not head or not head.next or not head.next.next:
            return head
        first = head.next
        second = head.next.next
        while second.next:
            if second.next.next:
                second = second.next.next
                first = first.next
            else:
                second = second.next

        invert = first.next.next if first.next.next else None
        first.next.next = None
        while invert:
            cur = invert.next
            invert.next = first.next
            first.next = invert
            invert = cur

        front = head
        back = first.next
        while back:
            cur = back.next
            first.next = back.next
            back.next = front.next
            front.next = back
            front = back.next
            back = cur
        return head



144. 二叉树的前序遍历

在这里插入图片描述
思路
递归

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        return [] if not root else [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

思路
迭代

  1. 进栈:中,右左
  2. 出栈:中,左右(先进后出)
class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        queue, ans = [root], list()
        while queue:
            node = queue.pop()
            ans.append(node.val)
            if node.right:
                queue.append(node.right)
            if node.left:
                queue.append(node.left)
        return ans



145. 二叉树的后序遍历

在这里插入图片描述
思路
递归

class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        return [] if not root else self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

思路
迭代

  1. 进栈:中,左右
  2. 出栈:中,右左(先进后出)
  3. 将出栈的数组反转
class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        queue, ans = [root], list()
        while queue:
            node = queue.pop()
            ans.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return ans[::-1]



146. LRU缓存机制

在这里插入图片描述
思路

  1. 创建缓存哈希表cache(字典:键值),可快速查询,对应的value存在双链表中,在链表头部表示刚访问了,在链表尾部表示最久没访问。put数据时大于缓存capacity大小时,删除链表尾部结点
  2. get:查询key是否存在缓存cache中,是则将对应值的结点移动至链表头部,并返回对应的值。否则返回-1
  3. put:查询key是否存在缓存cache中,是则修改对应值的结点的值,并移动结点至双链表头部。否则判断链表长度是否大于capacity,如果大于,则先删除尾部结点,然后创建新的结点node = DListNode(key, value)并插入至链表头部
# Definition for double-linked list.
class DListNode(object):
    def __init__(self, key=0, value=0, prev=None, next=None):
        self.key = key
        self.value = value
        self.prev = prev
        self.next = next
        
class LRUCache(object):

    def __init__(self, capacity):
        """
        :type capacity: int
        """
        self.cache = dict()    # 缓存哈希表:cache的key存放关键词,value存放DListNode(key, value)
        self.head = DListNode()    # 双链表头部
        self.tail = DListNode()    # 双链表尾部
        self.head.next = self.tail    # 链接双链表
        self.tail.prev = self.head
        self.capacity = capacity   # 缓存容量
        self.size = 0    # 缓存大小
        

    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if key in self.cache:
            node = self.cache[key]    # cache的key存放关键词,value存放DListNode(key, value)
            self.moveToHead(node)
            return node.value
        return -1


    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: None
        """
        if key not in self.cache:
            if self.size >= self.capacity:
                removeNode = self.removeTail()
                self.cache.pop(removeNode.key)
                self.size -= 1
            node = DListNode(key, value)
            self.cache[key] = node
            self.addToHead(node)
            self.size += 1
        else:
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)
            
        
    def addToHead(self, node):    # 添加至双链表头部
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
        node.prev = self.head
    
    def removeNode(self, node):    # 删除链表结点
        node.prev.next = node.next
        node.next.prev = node.prev
        
    def moveToHead(self, node):    # 移动结点至链表头部
        self.removeNode(node)    # 先删除结点
        self.addToHead(node)    # 移动到链表头部
    
    def removeTail(self):
        node = self.tail.prev
        self.removeNode(node)
        return node    # 返回尾部要删除的结点,以便删除cache中对应的值

# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)



147. 对链表进行插入排序

在这里插入图片描述
思路

  1. 没有结点或只有一个结点时,直接返回head
  2. 创建dummy指向head的头结点,pre指向第一个结点head,node指向第二个结点,当node结点大于pre时,不操作指向后一个结点。当node大于pre(前一结点)时,temp指向dummy头结点,重头寻找大于node的结点(得到位置为temp.next),将node插入temp结点后
class Solution(object):
    def insertionSortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        dummy = ListNode(-1)
        dummy.next = head
        pre = head
        node = head.next
        while node:
            if node.val < pre.val:
                temp = dummy
                while temp.next.val < node.val:    # 查找插入的位置
                    temp = temp.next
                pre.next = node.next
                node.next = temp.next
                temp.next = node
                node = pre.next            
            else:
                pre = pre.next
                node = node.next
        return dummy.next



148. 排序链表

在这里插入图片描述
思路
归并排序

class Solution(object):
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        first, second = head, head.next
        while second and second.next:    # 奇偶数个结点时,second指向最后一个结点;数个结点时,second指向尾部None
            first, second = first.next, second.next.next
        mid, first.next = first.next, None
        left, right = self.sortList(head), self.sortList(mid)    # 分割为两个链表
        temp = dummy = ListNode(-1)    # 两个链表的头结点比较大小组成新的链表
        while left and right:
            if left.val < right.val:
                temp.next, left = left, left.next
            else:
                temp.next, right = right, right.next
            temp = temp.next
        temp.next = left if left else right
        return dummy.next



149. 直线上最多的点数

在这里插入图片描述
思路
直线两点式:
x − x 1 x 2 − x 1 = y − y 1 y 2 − y 1 = > ( x − x 1 ) ( y 2 − y 1 ) = ( x 2 − x 1 ) ( y − y 1 ) = > y − y 1 x − x 1 = y 2 − y 1 x 2 − x 1 = > d y d x \frac{x-x_1}{x_2-x_1} =\frac{y-y_1}{y_2-y_1}=>(x-x_1)(y_2-y_1)=(x_2-x_1)(y-y_1)=>\frac{y-y_1}{x-x_1} =\frac{y_2-y_1}{x_2-x_1}=>\frac{dy}{dx} x2x1xx1=y2y1yy1=>(xx1)(y2y1)=(x2x1)(yy1)=>xx1yy1=x2x1y2y1=>dxdy
直接相除可能产生多位的浮点小数,因此分子分母除最大公约数简化得到的斜率相等,即为一条直线。创建slope(defaultdict),斜率为键,值为对应的点个数
注: defaultdict(int):比如list对应[ ],str对应的是空字符串,set对应set( ),int对应0

from collections import Counter, defaultdict
class Solution(object):
    def gcd(self, x, y):
        while y != 0:
            temp = x%y
            x = y
            y = temp
        return x
    def maxPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        if len(points) <=2:
            return len(points)
        count_points = Counter(tuple(point) for point in points)    # 每个点对应的个数
        points = list(count_points)    # 去重
        n = len(points)
        if n == 1:
            return count_points[points[0]]
        ans = 0
        for i in range(n-1):
            x1, y1 = points[i]
            slope = defaultdict(int)    # 斜率, 默认值为0
            for j in range(i+1, n):
                x2, y2 = points[j]
                dy, dx = y2-y1, x2-x1
                g = self.gcd(dy, dx)
                if g != 0:     # g不为0,防止除0
                    dy, dx = dy//g, dx//g
                slope["{}/{}".format(dy, dx)] += count_points[points[j]]
                # print("({},{}), ({},{})   dy: {} dx: {}   {}".format(x1, y1, x2, y2, dy, dx, slope))
            ans = max(ans, max(slope.values())+count_points[points[i]])
        return ans



150. 逆波兰表达式求值

在这里插入图片描述
在这里插入图片描述
思路
后缀表达式:用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

class Solution:
    def evalRPN(self, tokens)
        stack = list()
        for token in tokens:
            if token not in "+-*/":
                stack.append(token)
            else:
                y = stack.pop()
                x = stack.pop()
                # print("{}{}{}".format(x, token, y))
                stack.append(str(int(eval(x+token+y))))
        return int(stack[-1])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值