字符串
基础知识
定义及相关概念
1.链表与图
单链表:一个数据域+一个指针域
树:一个数据域+多个指针域
图:顶点集+边
树:无环连通图,是特殊的一种图。
2.树的定义
树是N(N>=0)个节点的有限集合。N=0时为空树,N>0时应满足:
- 有且仅有一个特定的称为根的节点
- N>1时,其余节点可分为m(m>0)个互不相交的有限集合。其中每个有限集合自身又是一棵树
3.树的相关概念:
- 根节点:非空树处于最上层的唯一节点,其余节点都是它的子孙后代
- 节点的度:节点具有的孩子节点个数
- 叶子节点:度为0的节点
- 父子节点:直接相连的一对节点,处于上层的是父节点,处于下层的是子节点
- 兄弟节点:由同一个父节点生成的不同节点互称兄弟节点
- 节点层次:由根开始记为1层,其子节点为2层,孙子节点为3层
- 节点深度:节点所在的层次数
- 树的深度/高度:数的最大层次数
- 节点高度:以节点为根的子树的深度/高度
- 有序树:兄弟节点为根的子树交换位置得到的新树视作与原来的树不同的树
- 无序树:兄弟节点为根的子树交换位置得到的新树视作与原来的树相同的树
4.特殊的树
- 二叉树:二叉树是一种每个节点度不大于2的树,且二叉树是一个有序树
- 满二叉树:所有叶子节点都在最底层,且所有非叶子节点度为2,一定是完全二叉树
- 完全二叉树:最下层的叶子节点是从左至右紧密排列的
- 二叉搜索树:要么是空树,要么满足左子树所有节点关键字均小于根节点关键字,右子树所有节点关键字均大于根节点关键字,且左右子树均为二叉搜索树。
- 平衡二叉树(AVL):如果二叉树中每个节点的左右子树高度差不大于1,就是平衡二叉树,一般与二叉搜索树结合形成平衡二叉搜索树,用来查找数据。
树的基本操作
1.树的存储结构
- 顺序存储结构:图可以使用邻接表表示,树则使用带有表示父节点的邻接表,即父子节点表示法,还有父节点表示法、子节点表示法
- 链式存储结构:使用链表表示节点及它的孩子节点
2.树的增删改查
在树的增删改查中,查找/搜索/遍历是树的核心操作。
3.查询
遍历:按照某种规则“访问”树中的每个节点,保证每个节点都会被“访问”到且每个节点只被“访问”一次
“访问”:程序与节点产生交互或者在节点进行某些操作
“进入”:程序来到了某个节点,并未与该节点产生任何交互
不同规则下,对同一个节点的“进入”次数可能有一次或多次,但对同一节点的“访问”只会发生一次。
- 二叉树的深度优先搜索DFS
可以发现每个节点都“进入”了三次,根据节点“进入”的次序进行访问,即在第一次“进入”访问、第二次“进入”访问、第三次“访问”可以把树的遍历分为先根(序)遍历、中根(序)遍历和后根(序)遍历。
def dfs(TreeNode root):
if not root:
return
# print(root.val) # 先序遍历
dfs(root.left)
# print(root.val) # 中序遍历
dfs(root.right)
# print(root.val) # 后序遍历
- 广度优先搜索BFS:从根节点开始,按层次从上到下,同层次内从左到右“访问”每一个节点,也叫做层次遍历,每个节点都只会”进入“一次。
def bfs(self, root: Optional[TreeNode]):
q = []
q.append(root)
while len(q) and q[0]:
cur = q.pop()
print(cur.val)
if root.left:
q.append(root.left)
if root.right:
a.append(root.right)
return
def bfs(self, root: Optional[TreeNode]):
q = []
q.append(root)
while len(q) and q[0]:
for i in range(len(q)):
cur = q.pop()
print(cur.val)
if root.left:
q.append(root.left)
if root.right:
a.append(root.right)
题目解析
二叉树的最大深度
1.题目描述
2.解析思路及代码
- 递归:对于每一个节点,它的最大深度等于它的左子树深度和右子树深度的最大值加1
- 广度优先搜索:就是在广度优先搜索的模板加1即可
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
public int maxDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int ans = 0;
while (!queue.isEmpty()) {
int size = queue.size();
ans ++ ;
for (int i = 0; i < size; i ++ ) {
TreeNode node = queue.poll();
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
return ans;
}
# 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 maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
将有序数组转换为二叉搜索树
1.题目描述
2.解题思路及代码
思路:
递归构造,选择数组的中间值作为当前子树的根节点
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int l, int r) {
if (l > r) return null;
int mid = l + (r - l) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, l, mid - 1);
root.right = helper(nums, mid + 1, r);
return root;
}
}
# 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 sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def helper(l, r):
if l > r:
return None
mid = int(l + (r - l) / 2)
root = TreeNode(nums[mid])
root.left = helper(l, mid - 1)
root.right = helper(mid + 1, r)
return root
return helper(0, len(nums) - 1)
二叉树的最小深度
1.题目描述
2.解题思路及代码
递归求解,将大问题转化为子问题,如果当前节点为空,返回0,当前节点左节点和右节点为空,返回1,当前节点左节点不为空,计算左节点的最小深度,当前节点右节点不为空,计算右节点的最小深度,最后返回最小深度+1即可。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 1;
int min_depth = Integer.MAX_VALUE;
if (root.left != null) min_depth = Math.min(min_depth, minDepth(root.left));
if (root.right != null) min_depth = Math.min(min_depth, minDepth(root.right));
return min_depth + 1;
}
}
# 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 minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if not root.left and not root.right:
return 1
min_depth = 10**9
if root.left:
min_depth = min(self.minDepth(root.left), min_depth)
if root.right:
min_depth = min(self.minDepth(root.right), min_depth)
return min_depth + 1
路径总和
1.题目描述
2.解题思路及代码
同样使用递归求解,将大问题转为小问题,如果当前节点为空,返回false,当前节点左节点和右节点为空则返回当前和加上当前节点值是否与targetSum相等,否则判断左子树的和或右子树的和加上当前节点值是否等于targetSum。
优化:上述方法中,对每个节点需要保存三个信息:当前节点、当前和、目标和,可以进行优化,把求解树的路径总和转换为左子树或右子树的和是否等于targetSum - root.val的值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
return helper(root, 0, targetSum);
}
public boolean helper(TreeNode root, int sum, int targetSum) {
if (root == null) return false;
if (root.left == null && root.right == null) return sum + root.val == targetSum;
return helper(root.left, sum + root.val, targetSum) || helper(root.right, sum + root.val, targetSum);
}
}
# 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 hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
if not root.left and not root.right:
return targetSum == root.val
return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)
二叉搜索树迭代器
1.题目描述
2.解题思路及代码
- 提前将中序遍历的结果保存到数组中,然后对数组操作即可
- 使用栈维护好对二叉树的中序遍历,实时维护栈。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class BSTIterator {
private TreeNode cur;
private Deque<TreeNode> stack;
public BSTIterator(TreeNode root) {
cur = root;
stack = new LinkedList<>();
}
public int next() {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
int res = cur.val;
cur = cur.right;
return res;
}
public boolean hasNext() {
return cur != null || !stack.isEmpty();
}
}
/**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator obj = new BSTIterator(root);
* int param_1 = obj.next();
* boolean param_2 = obj.hasNext();
*/
# 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 BSTIterator:
def __init__(self, root: TreeNode):
def inorder(root, res):
if not root:
return
inorder(root.left, res)
res.append(root.val)
inorder(root.right, res)
self.arr = list()
inorder(root, self.arr)
self.idx = 0
def next(self) -> int:
res = int(self.arr[self.idx])
self.idx += 1
return res
def hasNext(self) -> bool:
return self.idx < len(self.arr)
# Your BSTIterator object will be instantiated and called as such:
# obj = BSTIterator(root)
# param_1 = obj.next()
# param_2 = obj.hasNext()