目录
二叉树由于其结构的递归特性(即一棵树的左右子树仍然可以看作一棵树),使得其许多操作可以通过递归算法实现,下面给出五道java二叉树的经典进阶OJ题及题解供读者参考。
一、判断一颗二叉树是否为对称二叉树
1.题目描述:
2.代码示例:
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 isSymmetric(TreeNode root) {
return isSymmetricChild(root.left,root.right);
}
//判断一棵树是否对称的底层方法
public boolean isSymmetricChild(TreeNode left,TreeNode right){
//两棵树根节点都为空则对称,直接返回true
if(left==null&&right==null){
return true;
}
//两棵树一棵根节点为空,另一颗不为空,不对称直接返回false
if(left==null&&right!=null||left!=null&&right==null){
return false;
}
//两棵树均不为空,比较节点的值,若不同则不对称,直接返回false
if(left.val!=right.val){
return false;
}
//递归判断左树的左子树与右树的右子树是否对称,左树的右子树与右树的左子树是否对称
return isSymmetricChild(left.left,right.right)&&isSymmetricChild(left.right,right.left);
}
}
3.通过演示与分析:
假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。
二、根据先序遍历结果构造二叉树
1.题目描述:
2.代码示例:
import java.util.Scanner;
class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val){
this.val=val;
}
}
public class Main {
public static int i=0;
public static TreeNode createTree(String str){
TreeNode root=null;
//遍历字符串
if(str.charAt(i)!='#'){
//字符表示不为空则创建节点
root=new TreeNode(str.charAt(i));
i++;
//递归创建左子树
root.left=createTree(str);
//递归创建右子树
root.right=createTree(str);
}else{
//字符表示为空则不创建节点
i++;
}
return root;
}
//中序遍历方法
public static void inorder(TreeNode root){
if(root==null){
return;
}
inorder(root.left);
System.out.print(root.val+" ");
inorder(root.right);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 注意 while 处理多个 case
String str=in.nextLine();
TreeNode root=createTree(str);
inorder(root);
}
}
}
3.通过演示与分析:
假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。
三、层序遍历的非递归实现
1.题目描述:
2.代码示例:
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 List<List<Integer>> levelOrder(TreeNode root) {
//设定返回值(这里有向上转型)
List<List<Integer>> ret=new ArrayList<>();
//根节点为空直接返回空
if(root==null){
return ret;
}
//创建一个新的队列(这里有向上转型)
Queue<TreeNode> queue=new LinkedList<>();
//根节点入队
queue.offer(root);
//队列不为空则进入循环
while(!queue.isEmpty()){
//创建本一层的List
List<Integer>list=new ArrayList<>();
//获取当前队列大小(本层节点个数)
int size=queue.size();
//对队列所有节点执行以下操作
while(size>0){
//设定cur记录出队节点
TreeNode cur=queue.poll();
//将该节点的值放入本层List
list.add(cur.val);
//该节点左子树不为空则左子树根节点入队
if(cur.left!=null){
queue.offer(cur.left);
}
//该节点右子树不为空则右子树根节点入队
if(cur.right!=null){
queue.offer(cur.right);
}
size--;
}
//返回值中添加新一层List的引用
ret.add(list);
}
return ret;
}
}
借助创建队列实现层序遍历的非递归操作。
3.通过演示与分析:
假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。
四、判断是否为完全二叉树
1.题目描述:
判断一棵树是否为完全二叉树(定义如下)
完全二叉树是由满二叉树而引出来的,若设二叉树的
深度为h
,除第 h 层外
,其它各层 (1~h-1) 的结点数都达到最大个数(即1~h-1层为一个满二叉树)
,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
2.代码示例:
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;
}
}
public class IsCompleteTree {
public boolean isCompleteTree(TreeNode root){
//新建一个队列(这里发生了向上转型)
Queue<TreeNode> queue=new LinkedList<>();
//根节点为空认为是完全二叉树
if(root==null){
return true;
}
//根节点入队
queue.offer(root);
//队列不为空则进入循环
while(!queue.isEmpty()){
//设定cur记录出队元素
TreeNode cur=queue.poll();
//遇到空节点中止循环
if(cur==null){
break;
}
//将cur左子树根节点入队
queue.offer(cur.left);
//将cur右子树根节点入队
queue.offer(cur.right);
}
//循环结束后判断队列中是否仍然存在非空节点
while(!queue.isEmpty()){
TreeNode cur=queue.peek();
if(cur!=null){
//若有非空节点则说明不为完全二叉树
return false;
}else{
queue.poll();
}
}
//程序全部运行完毕说明此树为完全二叉树
return true;
}
}
本题思路类似于层序遍历的非递归实现,同样是 借助创建队列实现层序遍历的非递归操作。
3.通过演示与分析:
演示请读者自行尝试。
假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。
五、寻找二叉树的最近公共祖先
1.题目描述:
2.代码示例:
方法一:
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 TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
//根节点为空则直接返回空
if(root==null){
return null;
}
//如果其中有一个根节点等于root,则最近公共祖先即为root
if(root==p||root==q){
return root;
}
//开始递归判断
TreeNode leftTreeLowestCommonAncestor=lowestCommonAncestor1(root.left,p,q);
TreeNode rightTreeLowestCommonAncestor=lowestCommonAncestor1(root.right,p,q);
if(leftTreeLowestCommonAncestor!=null&&rightTreeLowestCommonAncestor!=null){
//左右子树返回值都不为空,说明p与q分别位于根节点两侧, 根节点为最近公共祖先
return root;
}else if(leftTreeLowestCommonAncestor!=null){
//左子树有最近公共祖先
return leftTreeLowestCommonAncestor;
}else{
//右子树有最近公共祖先
return rightTreeLowestCommonAncestor;
}
}
}
方法二:
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 TreeNode lowestCommonAncestor2(TreeNode root,TreeNode p,TreeNode q){
//根节点为空则直接返回
if(root==null){
return root;
}
//为两个指定节点分别创建两个存储其各自到根节点路径的栈
Stack<TreeNode>stackp=new Stack<>();
getPath(root,p,stackp);
Stack<TreeNode>stackq=new Stack<>();
getPath(root,p,stackq);
//计算两个栈的大小并比较
int sizep=stackp.size();
int sizeq=stackq.size();
//将较长的栈的元素出栈直到元素个数相等
if(sizep>sizeq) {
int size = sizep - sizeq;
while (size != 0) {
stackp.pop();
size--;
}
}else{
int size=sizeq-sizep;
while(size!=0){
stackq.pop();
size--;
}
}
//将剩余元素全部出栈直到寻找到相同的节点,此节点即为最近公共祖先
while(!stackp.isEmpty()&&!stackq.isEmpty()){
if(stackp.peek()==stackq.peek()){
return stackp.peek();
}else{
stackp.pop();
stackq.pop();
}
}
return null;
}
//将指定节点到根节点的路径存放到指定栈的底层方法
public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack){
//为空返回false
if(root==null){
return false;
}
//将根节点入栈
stack.push(root);
//根节点为指定节点则直接返回true
if(root==node){
return true;
}
//递归左子树,获取路径
boolean leftFlg=getPath(root.left,node,stack);
//成功获取到路径,直接返回标志true
if(leftFlg==true){
return true;
}
//递归右子树,获取路径
boolean rightFlg=getPath(root.right,node,stack);
//成功获取到路径,直接返回标志true
if(rightFlg==true){
return true;
}
//程序全部执行完毕,说明未找到路径,将当前根节点弹出并返回标志false
stack.pop();
return false;
}
}
3.通过演示与分析:
假设这棵树的节点个数为n,则方法一算法的时间复杂度为O(n),方法二的时间复杂度也为O(n)。
以上便是 Java二叉树经典进阶OJ题及题解的全部内容,如有不当,敬请斧正!