题目106题
从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/
9 20
/
15 7
思路
与105题基本一致
实现
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
def build(in_left, in_right, po_left, po_right):
if in_left > in_right:
return None
new_root = postorder[po_right]
root = TreeNode(new_root)
index = inorder.index(new_root)
right_len = in_right - index
root.left = build(in_left, index-1, po_left, po_right - right_len -1)
root.right = build(index+1, in_right, po_right - right_len,po_right-1)
return root
n = len(inorder) - 1
return build(0,n,0,n)
题目107题
二叉树的层次遍历 II
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
思路
与102题完全一致,只是将层数颠倒
实现
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
def helper(node: TreeNode, result, lev):
if node:
if len(result) <= lev:
result.insert(0, [])
size = len(result)
result[size - lev -1].append(node.val)
if node.left:
helper(node.left, result,lev+1)
if node.right:
helper(node.right, result,lev+1)
level = 0
result = []
helper(root, result, level)
return result
题目108题
将有序数组转换为二叉搜索树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/
-3 9
/ /
-10 5
思路
平衡二叉查找树:简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:
- 可以是空树。
- 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过 1。
由此想到采用中序遍历的方式,总是选择中间位置右边的数字作为根节点
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def sortedArrayToBST(self, nums: List[int]) -> TreeNode: def helper(left,right): if left > right: return None mid = (left + right + 1)//2 root = TreeNode(nums[mid]) root.left = helper(left, mid -1) root.right = helper(mid+1, right) return root return helper(0, len(nums)-1)
题目109
有序链表转换二叉搜索树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/
-3 9
/ /
-10 5
思路
此题思路与108题完全一致,但是由于这是单项链表,因此需要使用快慢指针法找到中间点
实现
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next # 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 sortedListToBST(self, head: ListNode) -> TreeNode: def midnode(left,right): fast = slow = left while fast != right and fast.next != right: fast = fast.next.next slow = slow.next return slow def helper(left,right): if left == right: return None mid = midnode(left, right) root = TreeNode(mid.val) root.left = helper(left, mid) root.right = helper(mid.next, right) return root return helper(head, None)
题目110
平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
思路
1.自顶向下:
计算节点高度的函数,即可判断二叉树是否平衡。计算当前节点高度是否满足平衡二叉树,若满足,则判定节点的子节点。
2.自底向上:
在方法一中,计算节点的高度是消耗时间资源的,因此可以采用自底向上的方法。
对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None 1. class Solution: def isBalanced(self, root: TreeNode) -> bool: def level(node: TreeNode): if not node: return 0 else: return max(level(node.left), level(node.right)) + 1 if not root: return True return abs(level(root.left) - level(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right) 2. class Solution: def isBalanced(self, root: TreeNode) -> bool: def height(root: TreeNode) -> int: if not root: return 0 leftHeight = height(root.left) rightHeight = height(root.right) if leftHeight == -1 or rightHeight == -1 or abs(leftHeight - rightHeight) > 1: return -1 else: return max(leftHeight, rightHeight) + 1 return height(root) >= 0
题目111
二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最小深度 2.
思路
当左孩子和右孩子都为空的情况,说明到达了叶子节点,直接返回1即可
当左孩子和由孩子其中一个为空,那么需要返回比较大的那个孩子的深度
当左右孩子都不为空,返回最小深度+1即可
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def minDepth(self, root: TreeNode) -> int: if not root: return 0 if not root.left and not root.right: return 1 l = self.minDepth(root.left) r = self.minDepth(root.right) if not root.left or not root.right: return l + r + 1 return min(l,r)+1
题目112
路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/
4 8
/ /
11 13 4
/
7 2 1
思路
递归:若当前节点就是叶子节点,那么我们直接判断 sum
是否等于 val,若不是叶子节点则继续
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def hasPathSum(self, root: TreeNode, sum: int) -> bool: if not root: return False if not root.left and not root.right: return sum == root.val sum -= root.val return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum)
题目113
路径总和II
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/
4 8
/ /
11 13 4
/
7 2 1
返回:
[ [5,4,11,2], [5,8,4,5] ]
思路
与上一题完全一致
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]: result = [] def hasPathSum(root: TreeNode, sum: int, res): if not root: return if not root.left and not root.right: if sum == root.val: result.append(res+[root.val]) sum -= root.val hasPathSum(root.left, sum, res+[root.val]) hasPathSum(root.right, sum, res+[root.val]) hasPathSum(root,sum,[]) return result
题目114
给定一个二叉树,原地将它展开为一个单链表。
例如,给定二叉树
1
/
2 5
/
3 4 6
将其展开为:
1
2
3
4
5
6
思路
1.前序遍历:前序遍历后放入数组中,再重新组合
2.寻找前驱节点:
实现
# 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 1. class Solution: def flatten(self, root: TreeNode) -> None: """ Do not return anything, modify root in-place instead. """ def preorder(node,lis): if not node: return lis.append(node) preorder(node.left,lis) preorder(node.right,lis) res = [] preorder(root, res) size = len(res) for i in range(1, size): prev, curr = res[i - 1], res[i] prev.left = None prev.right = curr
2.
题目116
填充每个节点的下一个右侧节点指针
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
思路
广度优先遍历:利用广度优先遍历,将每一层的节点都放入队列中,对同一层的元素来进行连接
实现
""" # Definition for a Node. class Node: def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): self.val = val self.left = left self.right = right self.next = next """ from collections import deque class Solution: def connect(self, root: 'Node') -> 'Node': if not root: return None queue = deque([root]) while queue: size = len(queue) for i in range(size): node = queue.popleft() if i < size -1: node.next = queue[0] if node.left: queue.append(node.left) if node.right: queue.append(node.right) return root
题目116
填充每个节点的下一个右侧节点指针II
给定一个二叉树。
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
思路
与上题思路完全一致
实现
""" # Definition for a Node. class Node: def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): self.val = val self.left = left self.right = right self.next = next """ from collections import deque class Solution: def connect(self, root: 'Node') -> 'Node': if not root: return None queue = deque([root]) while queue: size = len(queue) for i in range(size): node = queue.popleft() if i < size -1: node.next = queue[0] if node.left: queue.append(node.left) if node.right: queue.append(node.right) return root
题目118
杨辉三角
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
思路
递归法
实现
class Solution: def generate(self, numRows: int) -> List[List[int]]: res = [] def ge(numRows): if numRows == 1: res.append([1]) return [1] else: up = [0]+ ge(numRows-1) + [0] tmp = [] for i in range(len(up)-1): val = up[i] + up[i+1] tmp.append(val) res.append(tmp) return tmp if numRows == 0: return [] ge(numRows) return res
题目119
杨辉三角II
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
思路
递归法
实现
class Solution: def getRow(self, rowIndex: int) -> List[int]: def ge(numRows): if numRows == 0: return [1] else: up = [0]+ ge(numRows-1) + [0] tmp = [] for i in range(len(up)-1): val = up[i] + up[i+1] tmp.append(val) return tmp return ge(rowIndex)
题目120
三角形最小路径和
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
思路
1.递归:遍历所有点,但效率太低
2.动态规划
实现
class Solution: def minimumTotal(self, triangle: List[List[int]]) -> int: triangle_len = len(triangle) result = [0 for i in range(triangle_len)] for i in range(0, triangle_len): before = result[:] for j in range(1, i): temp = before[j-1:j+1] result[j] = min(temp) + triangle[i][j] if i != 0: result[i] = triangle[i][i] + before[i-1] result[0] += triangle[i][0] return min(result)
题目121
买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
思路
对于当前天数,最大利润为当天的价格减去之前的最低价格
实现
class Solution: def maxProfit(self, prices: List[int]) -> int: days = len(prices) minprice = int(1e9) max_profit = 0 for day in range(days): if prices[day] < minprice: minprice = prices[day] cur_profit = prices[day] - minprice if cur_profit > max_profit: max_profit = cur_profit return max_profit
题目122
买卖股票的最佳时机2
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
思路
遍历一次数组,当当前元素小于后一个元素时,则可以获得利润差
实现
class Solution: def maxProfit(self, prices: List[int]) -> int: days = len(prices) profit = 0 for day in range(days-1): if prices[day] < prices[day + 1]: profit += (prices[day + 1] - prices[day]) return profit
题目125
验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
思路
实现
class Solution: def isPalindrome(self, s: str) -> bool: strlen = len(s) s = s.upper() left = 0 right = strlen -1 while left < right: print(s[left],"+",s[right]) if not s[left].isalnum(): left += 1 continue elif not s[right].isalnum(): right -= 1 continue elif s[left] != s[right]: return False left += 1 right -= 1 return True
题目127
单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
思路
广度优先算法
实现
class Solution(object): def ladderLength(self, beginWord, endWord, wordList): """ :type beginWord: str :type endWord: str :type wordList: List[str] :rtype: int """ wordList=set(wordList) q=[(beginWord,1)] if endWord not in wordList: return 0 while q: node,level=q.pop(0) if node == endWord: return level for i in range(len(node)): for j in "abcdefghijklmnopqrstuvwxyz": new=node[:i]+j+node[i+1:] if new in wordList: q.append((new,level+1)) wordList.remove(new) return 0
题目129
求根到叶子节点数字之和
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
示例 1:
输入: [1,2,3]
1
/
2 3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.
思路
深度优先遍历
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def sumNumbers(self, root: TreeNode) -> int: def dfs(node,cur_sum): if not node: return 0 temp = cur_sum*10 + node.val if not node.left and not node.right: return temp else: return dfs(node.left, temp) + dfs(node.right,temp) return dfs(root,0)
题目130
被围绕的区域
给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。
找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
思路
首先对元素进行分类:1.X 2.被包围的O 3.没有被包围的O
此题可以发现在四周边上的O都是不被包围的,因此可以对这些边上的O做深度优先遍历获取所有与其连接的O
实现
class Solution: def solve(self, board: List[List[str]]) -> None: """ Do not return anything, modify board in-place instead. """ if not board: return row = len(board) col = len(board[0]) visited = [[False for _ in range(row)] for _ in range(col)] def dfs(i,j): if 0 <= i < row and 0 <= j < col: if board[i][j] != 'O': return else: board[i][j] = 'A' dfs(i+1,j) dfs(i-1,j) dfs(i,j+1) dfs(i,j-1) for i in range(row): dfs(i,0) dfs(i,col-1) for j in range(col): dfs(0,j) dfs(row-1, j) for i in range(row): for j in range(col): if board[i][j] == "A": board[i][j] = "O" elif board[i][j] == "O": board[i][j] = "X"