文章目录
参考资料
二叉树基础
二叉树遍历
层次遍历
递归解法
BFS解法
例题
102.二叉树的层序遍历
107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的前序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度
116.填充每个节点的下一个右侧节点指针
116.填充每个节点的下一个右侧节点指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
思路一
层次遍历解法
public Node connect(Node root) {
if (root == null) {
return null;
}
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
Node pre = null;
for (int i = 0; i < size; i++) {
Node poll = queue.poll();
if (pre != null) {
pre.next = poll;
}
if (poll.left != null) {
queue.add(poll.left);
}
if (poll.right != null) {
queue.add(poll.right);
}
pre = poll;
}
}
return root;
}
思路二
不适用队列存储,进行层次遍历。
* 连接完全二叉树有两种不同的节点
* 1.直接连接root节点的两个左右子节点
* root.left.next = root.right
* 2.连接两个不同父节点的左右子节点,前提是第i层已经连接好了,即root.next!=null
* root.right.next = root.next.left
* 算法过程:从每一层的最左边的节点start节点开始遍历。start = start.left往前遍历
* 1,逐层遍历,对应root节点,首先连接root节点的左右子节点,然后连接第二种情况
* 2,将root节点往后移动,root=root.next
* 3,往下一层移动start节点,start=start.left,如果start.left==null,说明到了最后一层,停止
/**
* 连接完全二叉树有两种不同的节点
* 1.直接连接root节点的两个左右子节点
* root.left.next = root.right
* 2.连接两个不同父节点的左右子节点,前提是第i层已经连接好了,即root.next!=null
* root.right.next = root.next.left
* 算法过程:从每一层的最左边的节点start节点开始遍历。start = start.left往前遍历
* 1,逐层遍历,对应root节点,首先连接root节点的左右子节点,然后连接第二种情况
* 2,将root节点往后移动,root=root.next
* 3,往下一层移动start节点,start=start.left,如果start.left==null,说明到了最后一层,停止遍历
* @param root
* @return
*/
public Node connect2(Node root) {
if (root == null) {
return null;
}
Node start = root;
while (start.left != null) {
Node cur = start;
while (cur != null) {
cur.left.next = cur.right;// 完全二叉树,一定有left节点跟right节点
if (cur.next != null) {
cur.right.next = cur.next.left;
}
cur = cur.next;
}
start = start.left;
}
return root;
}
117.填充每个节点的下一个右侧节点指针II
思路一
层次遍历解法。
思路二
常数空间复杂度,不需要额外的空间进行层次遍历。
中心思想是:
每一层都可以看做是一条链表,在遍历上一层的过程中,对下一层的节点建立一条链表。
public Node connect(Node root) {
if (root == null)
return root;
//cur我们可以把它看做是每一层的链表
Node cur = root;
while (cur != null) {
//遍历当前层的时候,为了方便操作在下一
//层前面添加一个哑结点(注意这里是访问
//当前层的节点,然后把下一层的节点串起来,其实就是以下一层为基础建立一个链表
Node dummy = new Node(0);
//pre表示访下一层节点的前一个节点
Node pre = dummy;
//然后开始遍历当前层的链表
while (cur != null) {
if (cur.left != null) {
//如果当前节点的左子节点不为空,就让pre节点
//的next指向他,也就是把它串起来
pre.next = cur.left;
//然后再更新pre
pre = pre.next;
}
//同理参照左子树
if (cur.right != null) {
pre.next = cur.right;
pre = pre.next;
}
//继续访问这一层的下一个节点
cur = cur.next;
}
//把下一层串联成一个链表之后,让他赋值给cur,
//后续继续循环,直到cur为空为止,指向下一层的第一个节点
cur = dummy.next;
}
return root;
}
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
思路一
层次遍历
思路二
深度优先搜索。更高效
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
} else {
int leftHeight = maxDepth(root.left);
int rightHeight = maxDepth(root.right);
return Math.max(leftHeight, rightHeight) + 1;
}
}
111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
思路一
层次遍历。
public int minDepth2(TreeNode root) {
if (root == null) {
return 0;
}
int min =1;// 细节
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode poll = queue.poll();
if (poll.left == null && poll.right == null) { // 找到叶子节点
return min;
}
if (poll.left != null) {
queue.add(poll.left);
}
if (poll.right != null) {
queue.add(poll.right);
}
}
min++;
}
return min;
}
思路二
深度优先搜索
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
int min = Integer.MAX_VALUE;
if (root.left != null) { // 细节,不为null才去继续求,区别于最大深度
min = Math.min(min, minDepth(root.left));
}
if (root.right != null) {
min = Math.min(min, minDepth(root.right));
}
return min + 1;
}
二叉树的深度
平衡二叉树
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
int left = depth(root.left);
int right = depth(root.right);
if (Math.abs(left - right) > 1) {
return false;
}
return isBalanced(root.left) && isBalanced(root.right);
}
private int depth(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(depth(root.left), depth(root.right)) + 1;
}
}
最小深度
最大深度
完全二叉树的节点数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
思路
- 先计算出左右子树的层数left和right
- 如果left==right,利用完全二叉树的性质,left节点一定是满二叉树
- 如果left!=right,利用完全二叉树的性质,right节点一定是满二叉树
代码
class Solution {
public int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
int left = countLevel(root.left);// 左子树的层数
int right = countLevel(root.right); // 右子树的层数
if (left == right) { //
return countNodes(root.right) + (1 << left); // 左子树是满二叉树
} else {
return countNodes(root.left) + (1 << right); // 右子树是满二叉树
}
}
/**
* 计算层数
* @param root
* @return
*/
private int countLevel(TreeNode root) {
int level = 0;
while (root != null) {
level++;
root = root.left;
}
return level;
}
}
二叉树比较与变换
对称二叉树
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return isSymmetric(root.left, root.right);
}
/**
* 两颗树是否是对称的
* @param left
* @param right
* @return
*/
private boolean isSymmetric(TreeNode left, TreeNode right) {
if (left == null && right == null) {
return true;
}
if (left == null || right == null) {
return false;
}
return left.val == right.val && isSymmetric(left.right, right.left) && isSymmetric(left.left, right.right);
}
}
反转二叉树
相同二叉树
class Solution {
public boolean compare(TreeNode tree1, TreeNode tree2) {
if (tree1 == null && tree2 == null) return true;
if (tree1 == null || tree2 == null) return false;
if (tree1.val != tree2.val) return false;
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
boolean compareLeft = compare(tree1.left, tree2.left); // 左子树:左、 右子树:左
boolean compareRight = compare(tree1.right, tree2.right); // 左子树:右、 右子树:右
return compareLeft && compareRight;
}
boolean isSameTree(TreeNode p, TreeNode q) {
return compare(p, q);
}
}
二叉树的路径
二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
代码
public List<String> binaryTreePaths(TreeNode root) {
if (root == null) {
return Collections.EMPTY_LIST;
}
List<String> result = new ArrayList<>();
List<TreeNode> path = new ArrayList<>();
backTracking(root, result, path);
return result;
}
private void backTracking(TreeNode root, List<String> result, List<TreeNode> path) {
if (root == null) {
return;
}
path.add(root);
if (root.left == null && root.right == null) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < path.size(); i++) {
stringBuilder.append(path.get(i).val);
if (i != path.size() - 1) {
stringBuilder.append("->");
}
}
result.add(stringBuilder.toString());
}
backTracking(root.left, result, path);
backTracking(root.right, result, path);
path.remove(path.size() - 1);
}