104、难度简单:
方法一:深度优先搜索:时间复杂度:O(n) 空间复杂度:O(height)
/**
* 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;
} else {
int leftHeight = maxDepth(root.left);
int rightHeight = maxDepth(root.right);
return Math.max(leftHeight, rightHeight) + 1;
}
}
}
原理:若我们直到左子树和右子树的最大深度l和r,那么该二叉树的最大深度为max(l,r) + 1
而左子树和右子树的最大深度又可以用相同的方式进行计算
所以,先用递归思想计算出其左子树和右子树的最大深度,然后在O(1)时间内计算出当前二叉树的最大深度
递归在访问到空节点时退出,开始返回到上一轮
代码过程:
先遍历到最底下的节点,然后发现再无左右子树,返回0,此时对该最底下的节点,其Math.max(leftHeight, rightHeight)+1=0+1=1,代表它的最大深度为1,然后返回1
然后对于其根节点,等到该根节点的左右子树最大深度都return出后,开始再进行 Math.max(leftHeight, rightHeight)+1 运算
直到最后
方法二:广度优先搜索:时间复杂度:O(n) 空间复杂度:此方法空间的消耗取决于队列存储的元素数量,其在最坏情况下会达到 O(n)
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root); //将指定的元素插入此队列(如果立即可行且不会违反容量限制)
int ans = 0;
while (!queue.isEmpty()) { //队列不为空,为空时说明已到达最底层节点的下一层null
int size = queue.size(); //取出当前这一层的节点数量
while (size > 0) { //从队列当中不断取出节点,并判断其有无下一层,进行更替
TreeNode node = queue.poll(); //检索队列的head元素,然后删除队列的head元素
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
size--; //由于队列是先进后出,所以最新添加的下一层的元素会在因size而不会被弹出(size为0就脱离循环)
}
ans++; //最大深度+1
}
return ans;
}
}
我们也可以用「广度优先搜索」的方法来解决这道题目,但我们需要对其进行一些修改,此时我们广度优先搜索的队列里存放的是「当前层的所有节点」。每次拓展下一层的时候,不同于广度优先搜索的每次只从队列里拿出一个节点,我们需要将队列里的所有节点都拿出来进行拓展,这样能保证每次拓展完的时候队列里存放的是当前层的所有节点,即我们是一层一层地进行拓展,最后我们用一个变量 ans 来维护拓展的次数,该二叉树的最大深度即为ans。
98、难度中等:通过率35%
方法一: 递归:
/**
* 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 boolean isValidBST(TreeNode root) { //主入口
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode node, long lower, long upper) { //递归函数
//遇到空节点返回true,因为空节点是合理的二叉搜索树
if (node == null) {
return true;
}
//若不满足左右节点小于大于根节点,那就返回false
if (node.val <= lower || node.val >= upper) {
return false;
}
//调用自己,只有左右子树都满足条件时才返回true
return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
}
}
该题二叉搜索树的性质:
如果该二叉树的左子树不为空,则左子树上所有节点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;它的左右子树也为二叉搜索树。
不仅左子节点的值要小于该节点,真个左子树的元素都应该小于该节点。右节点同理。这就意味在遍历节点时要保留节点的上界和下界,比较时不仅比较子节点的值还需要和上下界比较,函数设计:
boolean recurse(TreeNode node, int lower, int upper)
如果上下界存在,判断当前节点的值是否在界内,若不在,返回false。
将当前节点的值作为上界,继续对 node.left 进行递归;
将当前节点的值作为下界,继续对 node.right 进行递归
例如如下图中树:开始时5节点无上下界。所以 lower 和 upper = Null。
开始对node.left进行递归,upper为5,无lower。对1进行递归,upper为4,无lower。
此时1无左子树,回到4的右子树开始递归。7的lower是4,upper是5(上界不为4,而是从4那里继承来的5,只有4的左子树上界为4)
方法二:中序遍历:时间复杂度 : O(n) 空间复杂度 : O(n)
class Solution {
public boolean isValidBST(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<TreeNode>();
double inorder = -Double.MAX_VALUE;
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
// 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
if (root.val <= inorder) {
return false;
}
inorder = root.val;
root = root.right;
}
return true;
}
}
由于该二叉搜索树的性质,中序遍历其得到一定是升序的。
所以在中序遍历的时候实时检查当前节点的值是否大于前一个中序遍历到的节点的值即可