1.二叉树理论
1.1二叉树的种类
- 满二叉树:所有节点的数目等于2**k-1
- 完全二叉树:除了底层之外其他层都是满的,最底层的节点是从左到右连续的。
- 二叉搜索树:左边子树的所有节点都小于中间节点,右边子树的所有节点都大于中间节点。并且左子树也是满足这个规律的。搜索数的时间复杂度为logN。
- 平衡二叉搜索树:左子树和右子树的深度差不能超过1。且左子树也符合这个规则。
1.2存储方式
- 链式存储:每个节点包括val和左右指针指向左右节点。
- 顺序存储:用一个数组来存储,数组中的值是二叉树的节点值,索引是从上到下,从左到右的顺序。比如:给你一个索引i,下标为i的节点的左子节点下标是2*i+1,右子节点下标是2*i+2。
1.3遍历方式
- 深度优先搜索:深度优先搜索(Depth-First Search,DFS)是一种用于遍历或搜索树和图数据结构的算法。它从起始节点开始,沿着一条路径尽可能深入地探索,直到无法继续为止,然后回溯并尝试其他路径。DFS通常使用递归或栈数据结构来实现。前中后序遍历都是深度优先搜索。一般是递归法和迭代法。
- 广度优先搜索:广度优先搜索(Breadth-First Search,BFS)是一种用于遍历或搜索树和图数据结构的算法。与深度优先搜索不同,BFS从起始节点开始,首先探索其所有相邻节点,然后再逐层向外扩展。这意味着它会先访问离起始节点最近的节点,然后依次访问距离更远的节点。层序遍历是广度优先搜索。一般是迭代法。用队列(先进先出)来进行。
前序遍历的顺序是:中左右。
中序遍历是:左中右。
后序遍历是:左右中。
其实表示的就是中在其中所处的位置。
2.算法题
2.1第94题-二叉树的中序遍历
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
# 中序遍历是左中右
# 这是深度优先搜索dfs
# 一般是递归法和迭代法
def dfs(cur, value):
if cur == None:
return
dfs(cur.left, value)
value.append(cur.val)
dfs(cur.right, value)
value = []
dfs(root, value)
return value
递归比较简单,主要还是要掌握迭代的做法。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
# 中序遍历是左中右
# 这是深度优先搜索dfs
# 一般是递归法和迭代法
# 用迭代法
# 栈,用来储存节点地址
stack = []
# 用来储存结果
res = []
while root or stack:
# 先把左节点一直走到底
if root:
stack.append(root)
root = root.left
# 如果左节点都放入栈中了
else:
# 弹出栈中最上面的节点
temp = stack.pop()
# 储存该节点的值
res.append(temp.val)
# 将该节点的右子节点作为根节点遍历,如果没有右节点这个就是左,再次弹出最上面的节点,储存值,这个值是中,再以该节点的右子节点作为根节点,这个值是右。
root = temp.right
return res
2.2第104题-二叉树的最大深度
心得:不知道为什么使max_deepth为数值的时候,输出的总是初始值,明明在函数里变化了的。最后只好改成了数组,输出数组最后的值。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
# 遍历二叉树,用深度优先搜索的方式
# 就用前序遍历吧,顺序是中左右
# 先用递归法,后用迭代法
def dfs(root, deepth, max_deepth):
if not root:
return
deepth += 1
if max_deepth[-1] < deepth:
max_deepth.append(deepth)
dfs(root.left, deepth, max_deepth)
dfs(root.right, deepth, max_deepth)
deepth -= 1
deepth = 0
max_deepth = [0]
dfs(root, deepth, max_deepth)
return max_deepth[-1]
心得:二叉树是一个天然的递归,算最大深度只需要算每个小树的深度,也就是左树深度+右树深度+1,不断递归得到。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
# 遍历二叉树,用深度优先搜索的方式
# 就用前序遍历吧,顺序是中左右
# 先用递归法,后用迭代法
# 用栈
if root == None:
return 0
left = self.maxDepth(root.left)
right = self.maxDepth(root.right)
deepth = max(left,right)
return deepth+1
2.3第226题-翻转二叉树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def invertTree(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
steam = root
def invert(root):
if root == None:
return
# 翻转大树,小树也要翻转,一眼递归
temp = root.left
root.left = root.right
root.right = temp
invert(root.left)
invert(root.right)
invert(root)
return steam
2.4第101题-对称二叉树
给你一个二叉树的根节点 root
, 检查它是否轴对称。
示例 1:
心得:自己能想到怎么做,但是代码不知道怎么写,解析是定义了一个新函数来判断。应该思考到如果轴对称需要满足几个条件,两个指针分别指向左右子树,两个指针的val值相等,并且左指针的左子树等于右指针的右子树。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# 递归,左右同时开始,结构一样就继续往下,不一样就返回false
# 层遍历
# 或者分成左右两个树,每一步都同时往下深度优先搜索
return self.check(root, root)
def check(self, p, q):
if p==None and q==None:
return True
if p==None or q==None:
return False
return p.val==q.val and self.check(p.left, q.right) and self.check(p.right, q.left)
下面的方法更容易理解
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# 递归,左右同时开始,结构一样就继续往下,不一样就返回false
# 层遍历
# 或者分成左右两个树,每一步都同时往下深度优先搜索
if not root:
return True
def dfs(left,right):
# 递归的终止条件是两个节点都为空
if not (left or right):
return True
# 或者两个节点中有一个为空
if not (left and right):
return False
# 或者两个节点的值不相等
if left.val!=right.val:
return False
return dfs(left.left,right.right) and dfs(left.right,right.left)
# 用递归函数,比较左节点,右节点
return dfs(root.left,root.right)
用队列做
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# 队列实现
queue = [root.left, root.right]
while queue:
# pop(0)是从头弹出,pop()是从尾弹出
left = queue.pop(0)
right = queue.pop(0)
# 如果都是None,继续循环
if not (left or right):
continue
# 如果其中一个是None
if not (left and right):
return False
if left.val != right.val:
return False
queue.append(left.left)
queue.append(right.right)
queue.append(left.right)
queue.append(right.left)
return True
2.5第543题-二叉树的直径
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root
。
两节点之间路径的 长度 由它们之间边数表示。
示例 1:
输入:root = [1,2,3,4,5] 输出:3 解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。
心得:想到了怎么来求,但就是不会写。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def diameterOfBinaryTree(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if root.left==None and root.right==None:
return 0
# 左右两边可以分开算
# 直径等于左子树直径加右子树直径
# 选出最大的
# 父节点的直径等于左右子树中最大直径的边相加
self.max_depth = 1
def depth(node):
if node == None:
return 0
L = depth(node.left)
R = depth(node.right)
deep = L + R
self.max_depth = max(deep, self.max_depth)
return max(L, R) + 1
depth(root)
return self.max_depth
2.6第102题- 二叉树的层序遍历
心得:二叉树必须要熟练掌握BFS和DFS,几乎所有题都会用到,DFS主要就是递归,BFS主要就是队列维护节点。不断弹出和入队。
第一个有问题的地方就是数组需要==[],而不是None。
第二个地方是store数组需要及时清零。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
if root == None:
return []
# 层序遍历用迭代,迭代用栈或队列
# 广度优先搜索, BFS加一个层序
result = []
queue = []
queue.append(root)
store = []
temp2 = []
while queue != []:
temp = queue.pop(0)
store.append(temp.val)
if temp.left != None:
temp2.append(temp.left)
if temp.right != None:
temp2.append(temp.right)
if queue == []:
result.append(store)
store = []
queue = temp2
temp2 = []
return result
2.7第108题-将有序数组转化为二叉搜索树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def sortedArrayToBST(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
# 递归
length = len(nums)
if length == 1:
return TreeNode(nums[0])
self.steam = None
def dfs(nums):
length = len(nums)
if length == 1:
return TreeNode(nums[0])
mid = nums[length/2]
left = nums[:length/2]
L = dfs(left)
R = None
if length/2+1 < length:
right = nums[length/2+1:]
R = dfs(right)
self.steam = TreeNode(mid, L, R)
return self.steam
dfs(nums)
return self.steam
2.8第98题-验证二叉搜索树
心得:方法还是很容易想到的,至于遍历方法有递归、迭代、队列,需要自己想一下哪种合适,然后写出每种方法对应的基础框架,在上面进行修改,带入到最左下角的节点,主要考虑终止条件、需要左右子树返回的值,result的填入顺序。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# 遍历二叉树,从最左下的点开始遍历,最后数组是绝对升序的
# 用dfs试试
result = []
def dfs(root):
if root == None:
return
dfs(root.left)
result.append(root.val)
dfs(root.right)
return
dfs(root)
for i in range(1,len(result)):
if result[i-1] < result[i]:
continue
else:
return False
return True
心得:看了解析,直接递归,每个子树判断一下是否大于下限小于上限即可,并且上下限是实时更新的。这种返回True和False的题最好把递归放到return里面,这样一旦有一个false就能马上弹出递归,很方便。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# 遍历二叉树,从最左下的点开始遍历,最后数组是绝对升序的
# 用dfs试试
flag = True
def helper(root, upper = float('inf'), lower = float('-inf')):
if root == None:
return True
if not (root.val < upper and root.val > lower):
return False
return helper(root.left, upper=root.val, lower=lower) and helper(root.right, upper=upper, lower=root.val)
flag = helper(root)
return flag
2.9第230题-二叉搜索树中第k小元素
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def kthSmallest(self, root, k):
"""
:type root: TreeNode
:type k: int
:rtype: int
"""
# 首先想到的肯定是遍历打印,然后选出第k个,但是那样效率太低
# 先找到最小的值,然后慢慢返回查找第k个,中序遍历
num = 0
self.result = 0
def dfs(root, num):
if root == None:
return num
if num == k:
self.result = root.val
return num + 1
temp = dfs(root.left, num)
if temp == k:
return k
num = temp + 1
if num == k:
self.result = root.val
return num + 1
temp = dfs(root.right, num)
if temp == k:
return k
return temp
dfs(root, num)
return self.result
用迭代法节省时间,遇到了等于k的就可以结束程序。要熟练掌握迭代法前中后序遍历。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def kthSmallest(self, root, k):
"""
:type root: TreeNode
:type k: int
:rtype: int
"""
# 首先想到的肯定是遍历打印,然后选出第k个,但是那样效率太低
# 先找到最小的值,然后慢慢返回查找第k个,中序遍历
# 用迭代的方法可以不用遍历整个二叉树
stack = []
target = 0
result = 0
while root or stack:
if root:
stack.append(root)
root = root.left
else:
temp = stack.pop()
target += 1
if target == k:
result = temp.val
break
root = temp.right
return result
2.10第199题-二叉树的右视图
可以用BFS,层序遍历,从左到右遍历存储每层最后一个节点,也可以从右到左,存储每层第一个节点。
可以用DFS,从右开始向下遍历,记录深度,从左子树的时候超过深度就添加到结果中。
两种方法都想到了,但是DFS没写出来,BFS相对简单,写出来了。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def rightSideView(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
# 相当于先从右走,如果右不存在再从左走
# 如果右子树走到头了,左子树比右子树深
# 则再从左往下走,所以两颗子树应该同时走
# 层遍历,只保存最右边的
if root == None:
return None
result = []
result.append(root.val)
queue = []
queue.append(root)
temp2 = []
store = []
while queue != []:
temp = queue.pop(0)
if temp.right:
temp2.append(temp.right)
if temp.left:
temp2.append(temp.left)
if queue == []:
if temp2 != []:
result.append(temp2[0].val)
queue = temp2
temp2 = []
return result
2.11第114题-二叉树展开为链表
给你二叉树的根结点 root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:
输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6]
心得:太蠢了,应该想到的,一直想不到怎么使5接到4后面,只需要用前序遍历,维护好子树返回上来的地址就行了。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def flatten(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
# 前序遍历
# DFS,递归或者迭代
def dfs(root, result):
if not root:
return None
result.append(root)
dfs(root.left, result)
dfs(root.right, result)
result = []
dfs(root, result)
for i in range(len(result)-1):
result[i].right = result[i+1]
result[i].left = None
return root
2.12从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
心得:递归!!
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
# 前序遍历中,第一个数一定是根节点
# 中序遍历中,根节点对应val左边的数就是左子树,右边的是右子树
# 递归
def dfs(preorder, inorder):
if len(preorder) == 0:
return None
num = preorder.pop(0)
root = TreeNode(num)
ind = inorder.index(num)
left = inorder[:ind]
right = inorder[ind+1:]
root.left = dfs(preorder[:len(left)], left)
root.right = dfs(preorder[len(left):], right)
return root
return dfs(preorder, inorder)
2.13第437题-路径总和|||
给定一个二叉树的根节点 root
,和一个整数 targetSum
,求该二叉树里节点值之和等于 targetSum
的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例 1:
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 输出:3 解释:和等于 8 的路径有 3 条,如图所示。
心得:想用一个数组把每个往下的路径的点存起来,遍历得出有多少条,试一下,果然超时。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def pathSum(self, root, targetSum):
"""
:type root: TreeNode
:type targetSum: int
:rtype: int
"""
# dfs前序遍历
self.count = 0
self.depth = 0
def dfs(root, store):
if not root:
return None
self.depth += 1
deep = self.depth
store.append(root.val)
num = 0
for i in range(len(store)):
for j in range(i,len(store)):
num += store[j]
if num == targetSum:
self.count += 1
num = 0
temp = dfs(root.left, store)
if temp:
store = store[:deep]
dfs(root.right, store)
self.depth = deep-1
return root
store = []
dfs(root, store)
return self.count
2.14第236题-二叉树的最近公共祖先
暴力存储
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
# DFS, 用栈的方式迭代
stack = []
stack.append(root)
store = []
store2 = []
store3 = []
depth = 0
while stack or root:
if store == [] or store2 == []:
if root:
stack.append(root)
stack.append(root)
if root == p:
store = copy.deepcopy(stack)
if root == q:
store2 = copy.deepcopy(stack)
root = root.left
else:
temp = stack.pop()
root = temp.right
else:
break
a = len(store)
b = len(store2)
for i in reversed(range(a)):
for j in reversed(range(b)):
if store[i].val == store2[j].val:
return store[i]