!!!学习自labuladong
二叉树框架
void traverse(TreeNode root){
//前序遍历
traverse(root.left);
//中序遍历
traverse(root.right);
//后序遍历
}
二叉树总结
- 将题目细化为每个节点(root,left,right)需要做的事
- 前序遍历还是后序遍历
- 前序遍历:从上到下,以最下的分量思考
- 后序遍历:从下到上,以最上的分量思考
- 确定basecase
经典例题
226. 翻转二叉树
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
public TreeNode invertTree(TreeNode root) {
if(root==null) return null;
TreeNode left = root.left;
root.left=invertTree(root.right);
root.right=invertTree(left);
return root;
}
116. 填充每个节点的下一个右侧节点指针
给定一个完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
public Node connect(Node root) {
//base case
if(root==null) return null;
//连接两个相邻节点
connectTwoNode(root.left,root.right);
return root;
}
void connectTwoNode(Node node1,Node node2){
//base case
if(node1==null||node2==null){
return;
}
//连接两个节点
node1.next=node2;
//递归节点内和节点外的节点
connectTwoNode(node1.left,node1.right);
connectTwoNode(node2.left,node2.right);
connectTwoNode(node1.right,node2.left);
}
114. 二叉树展开为链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null
- 展开后的单链表应该与二叉树 先序遍历 顺序相同。
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
public void flatten(TreeNode root) {
//base case
if(root==null) return;
//从下往上用后序遍历
flatten(root.left);
flatten(root.right);
TreeNode left = root.left;
TreeNode right = root.right;
//按照规则重构
root.left = null;
root.right = left;
TreeNode p = root;
//找到右子树最下的顶点
while(p.right!=null){
p = p.right;
}
p.right = right;
}
654. 最大二叉树
给定一个不含重复元素的整数数组 nums 。一个以此数组直接递归构建的 最大二叉树 定义如下:
二叉树的根是数组 nums 中的最大元素。
左子树是通过数组中 最大值左边部分 递归构造出的最大二叉树。
右子树是通过数组中 最大值右边部分 递归构造出的最大二叉树。
返回有给定数组 nums 构建的 最大二叉树 。
public TreeNode constructMaximumBinaryTree(int[] nums) {
return build(nums,0,nums.length-1);
}
TreeNode build(int[]nums,int low,int high){
//base case
if(low>high) return null;
//遍历找到最大值
int max_value=nums[low],max_index=low;
for(int i=low+1;i<=high;i++){
if(nums[i]>max_value){
max_value = nums[i];
max_index = i;
}
}
TreeNode root = new TreeNode(max_value);
root.left = build(nums,low,max_index-1);
root.right = build(nums,max_index+1,high);
return root;
}
105. 从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
}
TreeNode build(int[]preorder,int prelow,int prehigh,int[]inorder,int inlow,int inhigh){
//base case
if(prelow>prehigh) return null;
//找到root在中序的坐标
int preroot = prelow,inroot=0;
int rootVal = preorder[preroot];
for(int i=inlow;i<=inhigh;i++){
if(inorder[i]==rootVal){
inroot = i;
break;
}
}
//建立根节点
TreeNode root = new TreeNode(rootVal);
//找到左子树大小以确定pre坐标
int leftsize = inroot-inlow;
root.left = build(preorder,prelow+1,prelow+leftsize,inorder,inlow,inroot-1);
root.right = build(preorder,prelow+leftsize+1,prehigh,inorder,inroot+1,inhigh);
return root;
}
652. 寻找重复的子树
给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
两棵树重复是指它们具有相同的结构以及相同的结点值。
示例 1:
1
/ \
2 3
/ / \
4 2 4
/
4
下面是两个重复的子树:
2
/
4
和
4
因此,你需要以列表的形式返回上述重复子树的根结点。
//记录结果
List<TreeNode> results = new LinkedList<>();
//记录子树结构出现次数
HashMap<String,Integer> record = new HashMap<>();
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
traverse(root);
return results;
}
String traverse(TreeNode root){
//base case
if(root==null) return "#";
String left = traverse(root.left);
String right = traverse(root.right);
//序列化表示树的结构
String subtree = left+","+right+","+root.val;
//判断是否出现过一次
int cnt = record.getOrDefault(subtree,0);
if(cnt==1){
results.add(root);
}
record.put(subtree,cnt+1);
return subtree;
}
124. 二叉树中的最大路径和
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
build(root);
return max;
}
int build(TreeNode root){
if(root==null) return 0;
int leftMax = Math.max(0,build(root.left));
int rightMax = Math.max(0,build(root.right));
max = Math.max(max,root.val+leftMax+rightMax);
return root.val+Math.max(leftMax,rightMax);
}
二叉搜索树
-
如果当前节点会对下面的子节点有整体影响,可以通过辅助函数增长参数列表,借助参数传递信息。
-
-特点:中序遍历是升序
//遍历框架
void BST(TreeNode root, int target) {
if (root.val == target)
// 找到目标,做点什么
if (root.val < target)
BST(root.right, target);
if (root.val > target)
BST(root.left, target);
}
经典例题
98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
//我的方法
boolean flag = true;
int pre =-1;
public boolean isValidBST(TreeNode root) {
if(root==null) return true;
boolean left = isValidBST(root.left);
if(flag){
pre=root.val;
flag = false;
}else {
if(root.val<=pre) {
return false;
}
}
pre = root.val;
boolean right = isValidBST(root.right);
return left&&right;
}
//另一种方法
public boolean isValidBST(TreeNode root) {
return find(root,null,null);
}
boolean find(TreeNode root,TreeNode min,TreeNode max){
if(root==null) return true;
if(min!=null&&root.val<=min.val) return false;
if(max!=null&&root.val>=max.val) return false;
return find(root.left,min,root)&&find(root.right,root,max);
}
701. 二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root==null) return new TreeNode(val);
if(root.val<val) root.right=insertIntoBST(root.right,val);
if(root.val>val) root.left=insertIntoBST(root.left,val);
return root;
}
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
if (root.val == key) {
// 这两个 if 把情况 1 和 2 都正确处理了
if (root.left == null) return root.right;
if (root.right == null) return root.left;
// 处理情况 3
TreeNode minNode = getMin(root.right);
root.val = minNode.val;
root.right = deleteNode(root.right, minNode.val);
} else if (root.val > key) {
root.left = deleteNode(root.left, key);
} else if (root.val < key) {
root.right = deleteNode(root.right, key);
}
return root;
}
TreeNode getMin(TreeNode node) {
// BST 最左边的就是最小的
while (node.left != null) node = node.left;
return node;
}