目录
参考了很多大佬的题解,仅作为自己学习笔记用。
07. 给前中序重建二叉树
题意:
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
题解:
要先将中序放入哈希表。部分递归示意图:
class Solution {
int[] pre;
HashMap<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] pre, int[] in) {
this.pre = pre;
for(int i = 0; i < in.length; i++) // 将中序放入map集合
map.put(in[i], i);
return recur(0, 0, in.length - 1);
}
// 三个形参的含义:根在前序中的索引,子树在中序的左边界,子树在中序的右边界
public TreeNode recur(int root, int left, int right) {
if(left > right)
return null; // 递归终止
TreeNode node = new TreeNode(pre[root]); // 建立根节点
int i = map.get(pre[root]); // 拿出根节点在中序map的索引,划分根节点、左子树、右子树
node.left = recur(root + 1, left, i - 1); // 开启左子树递归
node.right = recur(i - left + root + 1, i + 1, right); // 开启右
return node; // 回溯返回根节点
}
}
08. 二叉树的下一个节点
题意:(leetcode剑指专题没有,做的牛客)
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
题解:
一共有三种情况:
1、给的节点有右子树,那么下一个节点就是右子树的最左节点。
2、给的节点无右子树,并且自身是右节点,那么就一直往上找,找到一个节点是左节点,那么这个左节点的父亲就是下一节点。
3、给的节点无右子树,并且自身是左节点,那么下一节点就是他的父节点。(中序遍历:左根右)
public TreeLinkNode GetNext(TreeLinkNode pNode){
if(pNode.right != null){ // 情况1:有右子树,找右子树的最左边节点
TreeLinkNode node = pNode.right;
while(node.left != null)
node = node.left;
return node;
}else{ // 情况2&3:没有右子树
while(pNode.next != null){
TreeLinkNode parent = pNode.next;
if(parent.left == pNode) // 情况3:自身是左节点
return parent;
pNode = parent; // 情况2:自身是右节点
}
}
return null;
}
26. 树的子结构
题意:
输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(约定空树不是任意一个树的子结构)
B 是 A 的子结构, 即 A 中有出现和 B 相同的结构和节点值。
题解:
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null) return false;
//先从根节点判断B是不是A的子结构,如果不是在分别从左右两个子树判断,
return recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
public boolean recur(TreeNode A, TreeNode B){
if(B == null) return true; //这里如果B为空,说明B已经访问完了,确定是A的子结构
//如果B不为空A为空,或者这两个节点值不同,说明B树不是A的子结构
if(A == null || A.val != B.val)
return false;
//说明当前节点B是A的子,当前节点比较完之后还要继续判断左右子节点
return recur(A.left, B.left) && recur(A.right, B.right);
}
27. 二叉树的镜像
题意:
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
题解:
交换每个节点的左右节点。
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
TreeNode left = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(left);
return root;
}
28. 对称的二叉树
题意:
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
题解:
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return recur(root.left, root.right);
}
public boolean recur(TreeNode l, TreeNode r){
if(l == null && r == null) // 同时越过叶子节点,true
return true;
if(l == null || r == null) // 只有其中一个越过叶子节点,false
return false;
if(l.val != r.val) // 值不相等,false
return false;
return recur(l.left, r.right) && recur(l.right, r.left);
}
32-1. 从上到下打印二叉树(一行)
题意:
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
题解:
层序遍历,使用队列。先将根节点加入队列,然后访问,再加入左右子节点。
public int[] levelOrder(TreeNode root) {
if(root == null) return new int[0];
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root); // 父节点先加入队列
ArrayList<Integer> list = new ArrayList<>();
while(!queue.isEmpty()){ // 队列不空时
TreeNode node = queue.poll(); // 弹出队首
list.add(node.val); // 加入list
if(node.left != null) // 加入刚刚弹出的左右节点
queue.add(node.left);
if(node.right != null)
queue.add(node.right);
}
int[] res = new int[list.size()];
for(int i=0; i<list.size(); i++)
res[i] = list.get(i);
return res;
}
32-2. 从上到下打印二叉树(多行)
题目和上一题一样,只不过这一次打印的一层就是一行,需要打印多行。
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null)
queue.add(root);
while(!queue.isEmpty()) {
List<Integer> list = new ArrayList<>();
int size = queue.size(); // 统计队列数量,为 0 的时候就需要换行,加入list
while(size-- > 0){
TreeNode node = queue.poll();
list.add(node.val);
if(node.left != null)
queue.add(node.left);
if(node.right != null)
queue.add(node.right);
}
res.add(list);
}
return res;
}
32-3. 从上到下打印二叉树(之字型)
题目和上一题一样,只不过这一次打印的一层就是一行,需要打印多行,同时蛇形走位。
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null)
queue.add(root);
while(!queue.isEmpty()) {
List<Integer> list = new ArrayList<>();
int size = queue.size();
while(size-- > 0){
TreeNode node = queue.poll();
list.add(node.val);
if(node.left != null)
queue.add(node.left);
if(node.right != null)
queue.add(node.right);
}
if(res.size() % 2 == 1){ // 如果是奇数行,需要反转链表,第一行是0,第二行是1
Collections.reverse(list);
}
res.add(list);
}
return res;
}
33. 二叉搜索树的后序遍历序列
题意:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
题解:
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length - 1);
}
boolean recur(int[] postorder, int left, int right) {
//如果left==right,就一个节点不需要判断了,如果left>right说明没有节点,
//也不用再看了,否则就要继续往下判断
if (left >= right)
return true;
//因为数组中最后一个值postorder[right]是根节点,这里从左往右找出第一个比
//根节点大的值,他后面的都是根节点的右子节点(包含当前值,不包含最后一个值,
//最后一个是根节点),他前面的都是根节点的左子节点
int mid = left;
int root = postorder[right];
while (postorder[mid] < root)
mid++;
int temp = mid;
//因为postorder[mid]前面的值都是比根节点root小的,
//我们还需要确定postorder[mid]后面的值都要比根节点root大,
//如果后面有比根节点小的直接返回false
while (temp < right) {
if (postorder[temp++] < root)
return false;
}
//然后对左右子节点进行递归调用
return recur(postorder, left, mid - 1) && recur(postorder, mid, right - 1);
}
}
34. 二叉树中和位某一值的路径
题意:
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
题解:
class Solution {
LinkedList<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
recur(root, sum);
return res;
}
public void recur(TreeNode node, int tar) {
if(node == null)
return;
path.add(node.val);
tar -= node.val;
if(tar == 0 && node.left == null && node.right == null)
res.add(new LinkedList(path));
recur(node.left, tar);
recur(node.right, tar);
path.removeLast();
}
}
36. 二叉搜索树和双向链表
题意:
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
题解:
class Solution {
Node pre, head;
public Node treeToDoublyList(Node root) {
if(root == null) return null;
recur(root);
head.left = pre; // 首尾相连,因为是循环双向链表,不写这个就是纯双向链表
pre.right = head;
return head;
}
public void recur(Node cur) {
if(cur == null) return; // 递归终止条件
recur(cur.left); // 左节点
if(pre != null) // 根节点,如果pre 遍历到末端
pre.right = cur;
else
head = cur;
cur.left = pre;
pre = cur;
recur(cur.right); // 右节点
}
}
37. 序列化二叉树
题意:
请实现两个函数,分别用来序列化和反序列化二叉树。
题解:
public class Codec {
public String serialize(TreeNode root) {
if(root == null) return "[]";
StringBuilder res = new StringBuilder("[");
Queue<TreeNode> queue = new LinkedList<>() {{ add(root); }};
while(!queue.isEmpty()) {
TreeNode node = queue.poll();
if(node != null) {
res.append(node.val + ",");
queue.add(node.left);
queue.add(node.right);
}
else
res.append("null,");
}
res.deleteCharAt(res.length() - 1);
res.append("]");
return res.toString();
}
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null;
String[] vals = data.substring(1, data.length() - 1).split(",");
TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
Queue<TreeNode> queue = new LinkedList<>() {{ add(root); }};
int i = 1;
while(!queue.isEmpty()) {
TreeNode node = queue.poll();
if(!vals[i].equals("null")) {
node.left = new TreeNode(Integer.parseInt(vals[i]));
queue.add(node.left);
}
i++;
if(!vals[i].equals("null")) {
node.right = new TreeNode(Integer.parseInt(vals[i]));
queue.add(node.right);
}
i++;
}
return root;
}
}
54. 二叉搜索树的第 k 大节点
题意:
给定一棵二叉搜索树,请找出其中第k大的节点。
题解:
class Solution {
private int count = 0;
private int res;
public int kthLargest(TreeNode root, int k) {
if(root == null || k == 0) return 0;
inorder(root, k);
return res;
}
public void inorder(TreeNode root, int k){
if(root == null || count >= k) return;
inorder(root.right, k);
count++;
if(count == k){
res = root.val;
return;
}
inorder(root.left, k);
}
}
55-1. 二叉树的深度
题意:
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
在这里插入代码片
题解:
public int maxDepth(TreeNode root) {
return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
55-2. 判断平衡二叉树
题意:
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
题解:
class Solution {
private boolean isBalanced = true;
public boolean isBalanced(TreeNode root) {
height(root);
return isBalanced;
}
public int height(TreeNode root){
if(root == null || !isBalanced)
return 0;
int left = height(root.left);
int right = height(root.right);
if(Math.abs(left - right) > 1)
isBalanced = false;
return 1 + Math.max(left, right);
}
}
68-1. 二叉搜索树的最近祖先
题意:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
题解1:
迭代法,经过改进的。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(p.val > q.val) { // 改进一下,保证 p.val < q.val
TreeNode tmp = p;
p = q;
q = tmp;
}
while(root != null) {
if(root.val < p.val) // p,q 都在 root 的右子树中
root = root.right; // 遍历至右子节点
else if(root.val > q.val) // p,q 都在 root 的左子树中
root = root.left; // 遍历至左子节点
else break;
}
return root;
}
题解2:
递归。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val < p.val && root.val < q.val)
return lowestCommonAncestor(root.right, p, q);
if(root.val > p.val && root.val > q.val)
return lowestCommonAncestor(root.left, p, q);
return root;
}
68-2. 二叉树的最近祖先
和上一题一样,只不过二叉搜索树变成了二叉树。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null; // 如果树为空,直接返回null
// 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
if(root == p || root == q) return root;
// 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
TreeNode left = lowestCommonAncestor(root.left, p, q);
// 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
if(left == null)
return right;
else if(right == null)
return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
else
return root; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
}