104.二叉树的最大深度
559.N叉树的最大深度
111.二叉树的最小深度
222.完全二叉树的节点个数
语言:Java
节点的高度:节点到最近叶子节点的距离,后序遍历
节点的深度:根节点到节点的距离,前序遍历
根节点的最大高度就是树的最大深度
根节点的最小高度就是树的最小深度
所以可以用后序遍历求树的最大/小深度
链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/
后序遍历递归
/**
* 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 maxDepth(TreeNode root) {
if(root == null){
return 0;
}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
前序遍历回溯
现在理解的还不太透彻,先写上
class Solution {
int result;
public void getDepth(TreeNode node, int depth){
result = result > depth ? result : depth;
if(node.left == null && node.right == null){
return;
}
if(node.left != null){
depth++;
getDepth(node.left, depth);
depth--;
}
if(node.right != null){
depth++;
getDepth(node.right, depth);
depth--;
}
}
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
result = 0;
getDepth(root, 1);
return result;
}
}
层序遍历迭代
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
int result = 0;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
result++;
int size = queue.size();
for(int i = 0; i < size; i++){
TreeNode cur = queue.poll();
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
return result;
}
}
链接:https://leetcode.cn/problems/maximum-depth-of-n-ary-tree/
后序遍历递归
/*
// Definition for a Node.
class Node {
public int val;
public List children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public int maxDepth(Node root) {
if(root == null){
return 0;
}
List<Node> children = root.children;
int curDepth = 0;
while(!children.isEmpty()){
curDepth = Math.max(maxDepth(children.get(0)), curDepth);
children.remove(0);
}
return curDepth + 1;
}
}
层序遍历迭代
class Solution {
public int maxDepth(Node root) {
if(root == null){
return 0;
}
Queue<Node> queue = new LinkedList<Node>();
queue.offer(root);
int result = 0;
while(!queue.isEmpty()){
result++;
int size1 = queue.size();
for(int i = 0; i < size1; i++){
Node cur = queue.poll();
List<Node> children = cur.children;
int size2 = children.size();
for(int j = 0; j < size2; j++){
queue.offer(children.get(0));
children.remove(0);
}
}
}
return result;
}
}
链接:https://leetcode.cn/problems/minimum-depth-of-binary-tree/
后序遍历递归
/**
* 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;
}
int leftDepth = minDepth(root.left); //左
int rightDepth = minDepth(root.right); //右
//必须写下面的if判断,否则会将没有左孩子/右孩子的非叶子节点当作最低层
if(root.left == null && root.right != null){
return rightDepth + 1;
}
else if(root.left != null && root.right == null){
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1; //根
}
}
前序遍历回溯
class Solution {
int result;
public void getDepth(TreeNode node, int depth){
if(node.left == null && node.right == null){
result = result < depth ? result : depth;
return;
}
if(node.left != null){
depth++;
getDepth(node.left, depth);
depth--;
}
if(node.right != null){
depth++;
getDepth(node.right, depth);
depth--;
}
}
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
result = Integer.MAX_VALUE;
getDepth(root, 1);
return result;
}
}
层序遍历迭代
class Solution {
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
int result = 0;
while(!queue.isEmpty()){
result++;
int size = queue.size();
for(int i = 0; i < size; i++){
TreeNode cur = queue.poll();
if(cur.left == null && cur.right == null){
return result;
}
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
return result;
}
}
链接:https://leetcode.cn/problems/count-complete-tree-nodes/
后序遍历递归
/**
* 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 countNodes(TreeNode root) {
if(root == null){
return 0;
}
int leftNodes = countNodes(root.left);
int rightNodes = countNodes(root.right);
return leftNodes + rightNodes + 1;
}
}
时间复杂度:O(N)
空间复杂度:O(logN),每次递归都是向下走一层,栈最多需要的空间等于二叉树的高度
层序遍历迭代
class Solution {
public int countNodes(TreeNode root) {
if(root == null){
return 0;
}
int result = 0;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
result++;
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
if(cur.left != null){
queue.offer(cur.left);
result++;
}
if(cur.right != null){
queue.offer(cur.right);
result++;
}
}
return result;
}
}
时间复杂度:O(N)
空间复杂度:O(N)
针对完全二叉树的递归
完全二叉树的节点有两种情况:
① 满二叉树:用 2^n - 1 计算
② 最后一层叶子节点没有满:可以通过继续递归得到最终满二叉树
满二叉树:向左遍历的深度等于向右遍历的深度,如果不相等,则继续递归;
计算满二叉树的总节点个数不需要知道这个节点具体在第几层,只需要知道节点当前所在的满二叉树有几层即可,所以每次递归时 leftDepth 和 rightDepth 都从头开始计算即可。
class Solution {
public int countNodes(TreeNode root) {
if(root == null){
return 0;
}
TreeNode left = root.left;
TreeNode right = root.right;
int leftDepth = 1, rightDepth = 1;
while(left != null){
leftDepth++;
left = left.left;
}
while(right != null){
rightDepth++;
right = right.right;
}
//满二叉树
if(leftDepth == rightDepth){
return (1 << leftDepth) - 1;
}
//最后一层叶子节点没有满, 继续递归
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
时间复杂度:O(logN × logN)(我的理解:如果需要继续递归,则每一层都要进行一次寻找到最左/右的遍历,遍历一次时间复杂度是O(logN),一共有O(logN)层)
空间复杂度:O(logN)
二分查找+位运算
很妙的解法,要充分利用完全二叉树的性质
关于判断二分查找计算mid时该向上取整还是向下取整的小技巧:
就以0和1为例,left=0, right=1
,进入循环判断,将计算出的mid = 0 / mid = 1 代入 if 和 else 分别验证,如果下一次的 left 和 right 无法满足循环退出条件,说明会陷入死循环,需要调整取整方式。
以左闭右闭解法为例:如果是向下取整,mid = 0,if 分支下一次left=0, right=1
,不满足循环退出条件,else分支下一次left=0, right=0
,满足循环退出条件;如果是向上取整,mid = 1,if 分支下一次left=1, right=1
,满足循环退出条件,else 分支下一次left=0, right=0
,满足循环退出条件;所以应该向上取整。
以左闭右开向下取整为例:mid = 0,if 分支下一次left=0, right=1
,满足循环退出条件,else 分支下一次left=0, right=0
,满足循环退出条件;mid = 1,if 分支下一次left=1, right=1
,满足循环退出条件,else 分支下一次 left=0, right=1
,满足循环退出条件。
class Solution {
public boolean exist(TreeNode node, int num, int depth){
//利用完全二叉树的特点
//12: 1100,对应二叉树的路径:根节点-右-左-左
int bits = 1 << (depth - 1); //因为是1左移,所以与操作得到的就是该位本身的值
TreeNode cur = node;
while(bits > 0 && cur != null){
//& 优先级小于 !=
if((bits & num) != 0){
cur = cur.right; //1就向右走
}
else{
cur = cur.left; //0就向左走
}
bits >>= 1;
}
return cur != null;
}
public int countNodes(TreeNode root) {
if(root == null){
return 0;
}
int depth = 0;
TreeNode cur = root;
while(cur.left != null){
depth++;
cur = cur.left;
}
//左闭右闭
int left = 1 << depth; //最后一层最左元素编号是 2^depth
int right = (1 << (depth + 1)) - 1; //最右一层最右元素编号是 2^(depth + 1) - 1
while(left < right){
int mid = left + ((right - left + 1) >> 1);
//mid对应元素存在
if(exist(root, mid, depth)){
left = mid;
}
else{
right = mid - 1;
}
}
return left; //right也可
//左闭右开写法
// int left = 1 << depth; //最后一层最左元素编号是 2^depth
// int right = (1 << (depth + 1)); //最右一层最右元素编号是 2^(depth + 1) - 1
// while(left < right - 1){
// int mid = left + ((right - left) >> 1); //向下取整和向上取整都可以
// //mid对应元素存在
// if(exist(root, mid, depth)){
// left = mid;
// }
// else{
// right = mid;
// }
// }
// return left; //right-1 也可
}
}
时间复杂度:O(logN × logN)(最后一层有 2^depth 个元素,二分查找的时间复杂度是 O(log2^depth) = O(depth) = O(logN),而每个元素都要进行一次路径的遍历,遍历一次的时间复杂度是O(logN),合在一起就是O(logN × logN))
空间复杂度:O(1)