构造二叉树
思路:
- 根据中序遍历找到根节点,在前序遍历或者后序遍历中计算左右子树的范围;
- 之后递归实现。
例题:
- 105. 从前序与中序遍历序列构造二叉树
- 106. 从中序与后序遍历序列构造二叉树
- 889. 根据前序和后序遍历构造二叉树(根据前序遍历找到左子树的根节点,在后序遍历中找到对应下标并计算左子树的范围)
完全二叉树
958. 二叉树的完全性检验
两个条件:
- 左孩子为空,右孩子不为空不是完全二叉树;
- 遇到左孩子或者右孩子为空的节点,下一次遍历的时候以后的所有节点都应该没有左孩子和右孩子。
核心代码:
左孩子为空、右孩子不为空一定不是完全二叉树
if(node.left == null && node.right != null){
return false;
}
如果叶子节点左孩子、右孩子不为空一定不是完全二叉树
if(flag && (node.left != null || node.right != null)){
return false;
}
临界点、从该节点之后的所有节点都是叶子节点
if(node.left == null || node.right == null){
flag = true;
}
剑指 Offer II 043. 往完全二叉树添加节点 / 919. 完全二叉树插入器
分解为:
- 层序遍历二叉树,用list存储;
- 根据二叉树的性质寻找当前节点的父节点的下标(i - 1)/ 2。
根据二叉树的性质寻找父节点的下标(i - 1)/ 2
TreeNode parent = list.get((list.size() - 2) / 2);
if(parent.left == null){
parent.left = node;
}else if(parent.right == null){
parent.right = node;
}
满二叉树
分解为:
- 求二叉树的深度 h + 求节点的个数 n;
- 满足 n == 1 << h - 1;
二叉树的深度
public int height(TreeNode root){
if(root == null){
return 0;
}
int left = height(root.left);
int right = height(root.right);
return Math.max(left, right) + 1;
}
平衡二叉树
面试题 04.04. 检查平衡性 / 110. 平衡二叉树 / 剑指 Offer 55 - II. 平衡二叉树
分解为:
- 求二叉树的左右子树的深度;
- 递归到二叉树的左右子树。
求二叉树的深度同上;
二叉搜索树
701. 二叉搜索树中的插入操作
思路:根据二叉搜索树的性质找到对应的叶子节点,比较此叶子节点与val进行左、右插入
核心代码:
class Solution {
// 思路:根据二叉树的性质找到对应的叶子节点进行插入
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null){
return new TreeNode(val);
}
TreeNode cur = root, parent = null;
while(cur != null){
parent = cur;
if(cur.val < val){
cur = cur.right;
}else{
cur = cur.left;
}
}
TreeNode node = new TreeNode(val);
if(parent.val < val){
parent.right = node;
}else{
parent.left = node;
}
return root;
}
}
450. 删除二叉搜索树中的节点
思路:
- 如果当前节点有右子树,找到右子树的最小值赋值给根节点,之后删除右子树的最小值;
- 如果当前节点有左子树,找到左子树的最大值赋值给根节点,之后删除左子树的最大值。
核心代码:
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return root;
}else if(root.val < key){
root.right = deleteNode(root.right, key);
}else if(root.val > key){
root.left = deleteNode(root.left, key);
}else{
if(root.left == null && root.right == null){
return null;
}else if(root.left != null){
root.val = leftMax(root);
root.left = deleteNode(root.left, root.val);
}else{
root.val = rightMin(root);
root.right = deleteNode(root.right, root.val);
}
}
return root;
}
public int leftMax(TreeNode root){
root = root.left;
while(root.right != null){
root = root.right;
}
return root.val;
}
public int rightMin(TreeNode root){
root = root.right;
while(root.left != null){
root = root.left;
}
return root.val;
}
}
510. 二叉搜索树中的中序后继 II
思路:
- 如果当前节点有右子树,则寻找右子树中最左的节点;
- 如果没有,说明当前节点为最右侧节点,则一直寻找祖先节点。
class Solution {
public Node inorderSuccessor(Node node) {
// 如果有右子树,但是是最左边的节点
// 如果没有,需要一直向上找祖先节点
if(node.right != null){
node = node.right;
while(node.left != null){
node = node.left;
}
return node;
}else{
while(node.parent != null && node.parent.right == node){
node = node.parent;
}
return node.parent;
}
}
}
预存上一个节点与当前节点比较
分解为:
- 中序遍历;
- 预存储前一个节点与当前节点比较,比较之后节点再次更新为当前节点。
98. 验证二叉搜索树的核心代码:
if(pre != null && pre.val >= root.val){
flag = false;
}
pre = root;
99. 恢复二叉搜索树的核心代码:
inorder(root.left);
if(pre != null && pre.val > root.val){
// 定位两个错误的节点
if(a == null){
a = pre;
}
b = root;
}
pre = root;
inorder(root.right);
- 剑指 Offer II 053. 二叉搜索树中的中序后继
- 285. 二叉搜索树中的中序后继
- 面试题 04.06. 后继者
核心代码:
public void dfs(TreeNode root, TreeNode p) {
if(root == null){
return;
}
dfs(root.left, p);
if(pre != null && pre.val == p.val){
cur = root;
}
pre = root;
dfs(root.right, p);
}
剑指 Offer II 052. 展平二叉搜索树
思路:在中序遍历的时候构造一直向右的二叉树
核心代码:
public void dfs(TreeNode root){
if(root == null){
return;
}
dfs(root.left);
// 添加节点
node.right = new TreeNode(root.val);
// 继续向右节点添加
node = node.right;
dfs(root.right);
}
剑指 Offer II 055. 二叉搜索树迭代器 / 173. 二叉搜索树迭代器
中序遍历:
public void serialize(List<Integer> deque, TreeNode root){
if(root == null){
return;
}
serialize(deque, root.left);
deque.add(root.val);
serialize(deque, root.right);
}
剑指 Offer 36. 二叉搜索树与双向链表
中序遍历的时候构造前驱和后继
核心代码:if(pre == null){
当前cur没有左节点,也就是cur为头节点
cur = root;
}else{
正常情况
pre.right = root;
}
root.left = pre;
pre = root;
构造搜索二叉树
数组篇:
- 面试题 04.02. 最小高度树(转义为:构造二叉搜索树)
- 108. 将有序数组转换为二叉搜索树
-
1382. 将二叉搜索树变平衡(转义为:中序遍历 + 构造二叉搜索树)
核心代码:
private TreeNode buildTree(int[] nums, int left, int right) {
if (left <= right) {
int mid = left + (right - left) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = buildTree(nums, left, mid - 1);
root.right = buildTree(nums, mid + 1, right);
return root;
}
return null;
}
链表篇:
- 109. 有序链表转换二叉搜索树
核心代码:快慢指针找到中间位置,之后再进行构造二叉树
public TreeNode sortedListToBST(ListNode left, ListNode right) {
if(left == right){
return null;
}
// 快慢指针
ListNode fast = left, slow = left;
while(fast.next != right && fast.next.next != right){
fast = fast.next.next;
slow = slow.next;
}
//构造二叉树
TreeNode root = new TreeNode(slow.val);
root.left = sortedListToBST(left, slow);
root.right = sortedListToBST(slow.next, right);
return root;
}
二叉树的序列化与反序列化
分解为:二叉树的序列化 + 构造二叉树
题目:
(通解:前序遍历)
- 剑指 Offer 37. 序列化二叉树
- 剑指 Offer II 048. 序列化与反序列化二叉树
- 297. 二叉树的序列化与反序列化
- 449. 序列化和反序列化二叉搜索树
核心代码:
public void serialize(TreeNode root, StringBuffer buffer) {
if(root == null){
buffer.append("null").append(",");
}else{
buffer.append(root.val).append(",");
serialize(root.left, buffer);
serialize(root.right, buffer);
}
}
public TreeNode deserialize(Deque<String> deque) {
String temp = deque.poll();
if(temp.equals("null")){
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(temp));
root.left = deserialize(deque);
root.right = deserialize(deque);
return root;
}
(层序遍历)在for循环里面递归
- 428. 序列化和反序列化 N 叉树
核心代码:
public void serialize(StringBuffer buffer, Node root) {
if(root == null){
buffer.append("null").append(",");
}else{
// 层序遍历
buffer.append(root.val).append(":").append(root.children.size()).append(",");
for(Node node : root.children){
serialize(buffer, node);
}
}
}
public Node deserialize(Deque<String> deque) {
if(deque.peek().equals("null")){
return null;
}
String[] temp = deque.poll().split(":");
int val = Integer.valueOf(temp[0]);
int nums = Integer.valueOf(temp[1]);
Node root = new Node(val);
root.children = new ArrayList<>();
for(int i = 0; i < nums; i++){
root.children.add(deserialize(deque));
}
return root;
}
最近公共祖先节点
二叉搜索树的最近公共祖先节点
- 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
- 235. 二叉搜索树的最近公共祖先
核心代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || p == root || q == root){
return root;
}
if(p.val > root.val && q.val > root.val){
return lowestCommonAncestor(root.right, p, q);
}
if(p.val < root.val && q.val < root.val){
return lowestCommonAncestor(root.left, p, q);
}
return root;
}
}
二叉树的最近公共祖先节点
- 236. 二叉树的最近公共祖先
- 剑指 Offer 68 - II. 二叉树的最近公共祖先
核心代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || p == root || q == root){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left != null && right != null){
return root;
}else if(left != null){
return left;
}else if(right != null){
return right;
}
return null;
}
}
改进求最近公共祖先
- 1644. 二叉树的最近公共祖先 II
区别:增加判断节点是否在二叉树中
public boolean isExits(TreeNode root, TreeNode temp) {
if(root == null){
return false;
}
return root.val == temp.val || isExits(root.left, temp) || isExits(root.right, temp);
}
- 1650. 二叉树的最近公共祖先 III
class Solution {
public Node lowestCommonAncestor(Node p, Node q) {
// 有parent节点也就相当于链表
Node a = p, b = q;
while(a != b){
a = a.parent == null ? q : a.parent;
b = b.parent == null ? p : b.parent;
}
return a;
}
}
- 1676. 二叉树的最近公共祖先 IV
区别:多个节点的公共祖先,判断条件更改为是否包含
public TreeNode lowestCommonAncestor(TreeNode root, Set<Integer> set) {
if(root == null){
return root;
}
if(set.contains(root.val)){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, set);
TreeNode right = lowestCommonAncestor(root.right, set);
if(left != null && right != null){
return root;
}else if(left != null){
return left;
}else if(right != null){
return right;
}
return null;
}
根据深度进行判断返回节点,感觉有些类似于搜索二叉树寻找最近公共祖先了
- 865. 具有所有最深节点的最小子树
- 1123. 最深叶节点的最近公共祖先
核心代码:
class Solution {
public TreeNode subtreeWithAllDeepest(TreeNode root) {
if(root == null){
return root;
}
int left = deepth(root.left);
int right = deepth(root.right);
if(left == right){
return root;
}else if(left > right){
return subtreeWithAllDeepest(root.left);
}else{
return subtreeWithAllDeepest(root.right);
}
}
public int deepth(TreeNode root) {
if(root == null){
return 0;
}
int left = deepth(root.left);
int right = deepth(root.right);
return Math.max(left, right) + 1;
}
}
折纸问题
分解为:
- 中序遍历;
- 左孩子:down,右孩子:up。
核心代码:
public void dfs(List<String> list, int i, int n, boolean flag) {
// write code here
if(i > n){
return;
}
dfs(list, i + 1, n, true);
list.add(flag ? "down" : "up");
dfs(list, i + 1, n, false);
}
层序遍历(广度优先遍历)
- 515. 在每个树行中找最大值
- 429. N 叉树的层序遍历
- 637. 二叉树的层平均值
- 199. 二叉树的右视图
- 102. 二叉树的层序遍历
- 107. 二叉树的层序遍历 II
核心代码:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> list = new ArrayList<>();
if(root == null){
return list;
}
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> temp = new ArrayList<>();
for(int i = queue.size(); i > 0; i--){
TreeNode node = queue.poll();
temp.add(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
list.add(temp);
}
return list;
}
}
- 117. 填充每个节点的下一个右侧节点指针 II
- 116. 填充每个节点的下一个右侧节点指针
Node node = deque.poll();
if(i > 0){
node.next = deque.peek();
}
深度优先遍历
- 104. 二叉树的最大深度
- 111. 二叉树的最小深度(多加了左、右子树是否为空的判断条件)
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
if(root.left == null && root.right != null){
return minDepth(root.right) + 1;
}
if(root.left != null && root.right == null){
return minDepth(root.left) + 1;
}
int left = minDepth(root.left);
int right = minDepth(root.right);
return Math.min(left, right) + 1;
}

京公网安备 11010502036488号