转载注明!!https://blog.csdn.net/qq_31842777/article/details/90478093
写这篇博文的主要目的是作为自己的学习笔记,方便后续复习,也希望对大家有所帮助。
主要有以下9题:
以下是程序实现,含测试:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
//定义二叉树
public class TreeNode{
int value=0; //数据域
TreeNode left=null;// 左子树根节点
TreeNode right=null;// 右子树根节点
public TreeNode() {}
public TreeNode(int value){
this.value=value;
}
public TreeNode(int value,TreeNode left,TreeNode right) {
this.value=value;
this.left=left;
this.right=right;
}
/**
* 以下是常见的二叉树算法题
*/
//1. 求二叉树中的节点个数
/**
* 非递归算法O(n)
* 使用queue的好处:得益于queue先进先出的特点
* 当添加root-->left-->right
* --l--r --l--r
*queue每次需要poll,才能记录sum,并offer l、r,只有queue才能保证顺序不出错
*/
public static int sumOfNode(TreeNode root) {
if(root==null) {
return 0;
}
Queue queue=new LinkedList();
queue.offer(root);
int sum=0;
while(!queue.isEmpty()){
TreeNode node=queue.poll();// 从队头位置移除
sum++;
if(node.left!=null) { // 如果有左孩子,加到队尾
queue.offer(node.left);
}
if(node.right!=null) { // 如果有右孩子,加到队尾
queue.offer(node.right);
}
}
return sum;
}
/**递归算法O(n) 左子树+右子树+1
* 递归解法: O(n)
* 如果二叉树为空,节点个数为0
*如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1
*/
public static int sumOfNodeRec(TreeNode root) {
if(root==null) {
return 0;
}
int leftSum=sumOfNode(root.left);
int rightSum=sumOfNode(root.right);
return leftSum+rightSum+1;
}
//2.求二叉树的深度(高度)
//非递归算法O(n) 记录结点数来计算深度 Queue
public static int TreeDepth(TreeNode root) {
if(root==null) {
return 0;
}
if(root.left==null&&root.right==null) {
return 1;
}
int depth=0;
int currentLevelNodes=1;
int nextLevelNodes=0;
Queue queue=new LinkedList();
queue.offer(root);
while(!queue.isEmpty()) {
TreeNode node=queue.poll();
currentLevelNodes--;
if(node.left!=null) {
queue.offer(node.left);
nextLevelNodes++;
}
if(node.right!=null) {
queue.offer(node.right);
nextLevelNodes++;
}
//当前level结点为0,将下一level结点赋值
if(currentLevelNodes==0) {
currentLevelNodes=nextLevelNodes;
nextLevelNodes=0;//赋值,进行下一轮计算
depth++;
}
}
return depth;
}
/**递归算法O(n)
* 如果二叉树为空,二叉树的深度为0
* 如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
*/
public static int TreeDepthRec(TreeNode root) {
if(root==null) {
return 0;
}
if(root.left==null&&root.right==null) {
return 1;
}
int leftLength=TreeDepthRec(root.left);
int rightLength=TreeDepthRec(root.right);
return Math.max(leftLength,rightLength)+1;
}
//3. 求二叉树第k层的节点个数
/**
* 等价于求以root左孩子为根的k-1层节点数目 加上以root右孩子为根的k-1层节点数目
* 如果二叉树为空或者k<1,返回0
* 如果二叉树不为空并且k==1,返回1
* 如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和
* 因为二叉树的度为2,使用某一个结点的左/右结点只有1/0个,如果结点空返回0,不空则1,然后递归左+右就是结果
*/
public static int getNodeNumKthLevelRec(TreeNode root,int k) {
if(root==null) {
return 0;
}
if(k==1) {
return 1;
}
return getNodeNumKthLevelRec(root.left,k-1)+getNodeNumKthLevelRec(root.right,k-1);
}
//4. 求二叉树中叶子节点的个数
/**
* 叶子结点:度为0的结点
* 遍历二叉树,当root.left==null&&root.right==null,返回1
* 即该节点度为0,leaf++
*/
public static int getNodeNumLeaf(TreeNode root) {
if(root==null) {
return 0;
}
Queue queue=new LinkedList();
queue.offer(root);
int leaf=0;
while(!queue.isEmpty()){
TreeNode node=queue.poll();// 从队头位置移除
if(node.left==null&&node.right==null) {
leaf++;
}
if(node.left!=null) { // 如果有左孩子,加到队尾
queue.offer(node.left);
}
if(node.right!=null) { // 如果有右孩子,加到队尾
queue.offer(node.right);
}
}
return leaf;
}
/**递归算法
* 遍历二叉树,当root.left==null&&root.right==null,返回1,然后递归
*/
public static int getNodeNumLeafRec(TreeNode root) {
if(root==null) {
return 0;
}
if(root.left==null&&root.right==null) {//二叉树本身为叶子节点
return 1;
}
int leftSum=getNodeNumLeafRec(root.left);
int rightSum=getNodeNumLeafRec(root.right);
return leftSum+rightSum;
}
//5. 判断两棵二叉树是否相同的树
/**
* 递归算法
* 如果两棵二叉树都为空,返回真
* 如果两棵二叉树一棵为空,另外一棵不为空,返回假
* 如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
* 递归到叶子节点都不出现错误,即返回true root1==null&&root2==null
*/
public static boolean isSameRec(TreeNode root1,TreeNode root2) {
if(root1==null&&root2==null) {
return true;
}
else if(root1==null||root2==null) {
return false;
}
if(root1.value!=root2.value) {
return false;
}
//递归遍历左右子节点
return isSameRec(root1.left,root2.left)&&isSameRec(root1.right,root2.right);
}
/**
* 非递归算法 Stack
* 双栈法,将两棵树的结点存入两个栈,进行比较
*/
public static boolean isSame(TreeNode root1,TreeNode root2) {
if(root1==null&&root2==null) {
return true;
}
else if(root1==null||root2==null) {
return false;
}
Stack stack1=new Stack();
Stack stack2=new Stack();
stack1.push(root1);
stack2.push(root2);
while(!stack1.isEmpty()&&!stack2.isEmpty()) {
TreeNode temp1=stack1.pop();
TreeNode temp2=stack2.pop();
//执行到最后,temp1,temp2为空时,跳出本次循环
if (temp1 == null && temp2 == null) { // 两个元素都为空,因为添加的时候没有对空节点做判断
continue;
}
else if(temp1!=null&&temp2!=null&&temp1.value==temp2.value) {
//分别将root1 root2的左右结点压入stack1 stack2
stack1.push(temp1.left);
stack1.push(temp1.right);
stack2.push(temp2.left);
stack2.push(temp2.right);
}
else {
return false;
}
}
return true;
}
//6. 判断二叉树是不是平衡二叉树AVL
/**
* 平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树
*具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
*
*递归实现:借助前面实现好的求二叉树高度的函数TreeDepthRec(TreeNode root)
*如果二叉树为空, 返回真
*如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
*/
public static boolean isAVLTree(TreeNode root) {
if(root==null) {
return true;
}
if(Math.abs(root.TreeDepthRec(root.left)-root.TreeDepthRec(root.right))>1) {
return false;
}
return isAVLTree(root.left)&&isAVLTree(root.right);//递归判断左右子树
}
//7. 求二叉树的镜像
/**
* 递归实现:破坏原来的树,把原来的树改成其镜像
* 如果二叉树为空,返回空
* 如果二叉树不为空,求左子树和右子树的镜像,然后交换左右子树
*/
public static TreeNode mirrorRec(TreeNode root) {
if(root==null) {
return root;
}
TreeNode left=mirrorRec(root.right);
TreeNode right=mirrorRec(root.left);
//更新根结点的左右子树形成镜像,但破坏了原来的树
root.left=left;
root.right=right;
return root;
}
/**
* 递归实现:不能破坏原来的树,把原来的树改成其镜像
* 如果二叉树为空,返回空
* 如果二叉树不为空,求左子树和右子树的镜像,然后交换左右子树
*/
public static TreeNode mirrorRecCopy(TreeNode root) {
if(root==null) {
return root;
}
TreeNode newRoot=new TreeNode(root.value);
newRoot.left=mirrorRecCopy(root.right);
newRoot.right=mirrorRecCopy(root.left);
return newRoot;
}
/**
* 非递归实现:破坏原来的树,把原来的树改成其镜像
* Stack
*/
public static TreeNode mirror(TreeNode root) {
if(root==null) {
return root;
}
Stack stack=new Stack();
stack.push(root);
while(!stack.isEmpty()) {
TreeNode cur=stack.pop();
//交换左右孩子
TreeNode temp=cur.right;
cur.right=cur.left;
cur.left=temp;
if(cur.left!=null) {
stack.push(cur.left);
}
if(cur.right!=null) {
stack.push(cur.right);
}
}
return root;
}
/**
* 非递归实现:破坏原来的树,把原来的树改成其镜像
* Stack
* 新建一个Tree,用两个辅助stack
*/
public static TreeNode mirrorCopy(TreeNode root) {
if(root==null) {
return root;
}
Stack stack1=new Stack();
Stack stack2=new Stack();
stack1.push(root);
TreeNode newRoot=new TreeNode(root.value);
stack2.push(newRoot);
while(!stack1.isEmpty()) {
TreeNode cur=stack1.pop();
TreeNode newCur=stack2.pop();
if(cur.right!=null) {
stack1.push(cur.right);
newCur.left=new TreeNode(cur.right.value);
stack2.push(newCur.left);
}
if(cur.left!=null) {
stack1.push(cur.left);
newCur.right=new TreeNode(cur.left.value);
stack2.push(newCur.right);
}
}
return newRoot;
}
//8. 判断两个二叉树是否互相镜像
/*
* 递归解法:与比较两棵二叉树是否相同解法一致(题5),非递归解法省略。
* 比较r1的左子树的镜像是不是r2的右子树
* 比较r1的右子树的镜像是不是r2的左子树
*/
public static boolean isMirrorRec(TreeNode r1, TreeNode r2) {
if (r1==null && r2==null) {
return true;
} else if (r1==null||r2==null) {
return false;
}
if (r1.value!=r2.value) {
return false;
}
// 递归比较r1的左子树的镜像是不是r2右子树
// 和r1的右子树的镜像是不是r2的左子树
return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);
}
//9. 判断是否为二分查找树BST
/**
* 又称二叉排序树,BST,中序遍历后递增
* 递归解法:中序遍历的结果应该是递增的
*root 根节点
*pre上一个保存的节点
* 是否为BST树
*/
public static boolean isValidBST(TreeNode root){
Stack stack = new Stack();
//设置前驱节点
TreeNode pre = null;
while(root != null || !stack.isEmpty()){
while (root != null) { // 将当前节点,以及左子树一直入栈,循环结束时,root==null
stack.push(root);
root = root.left;
}
/**把根结点及左子树一直入栈,完成后抛出,
* 相当于抛出了中序遍历的结果,每次抛出一次比较当前值与前驱的大小
* 当前值大于前驱,继续,否则,返回false
*/
root = stack.pop();
//比较并更新前驱,与普通遍历的区别就在下面四行
if(pre != null && root.value <= pre.value){
return false;
}
pre = root;
root = root.right; //访问右子树
}
return true;
}
//测试
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeNode node1=new TreeNode(2,new TreeNode(1),new TreeNode(4));
TreeNode bTree=new TreeNode(7,node1,new TreeNode(8));
System.out.println("结点数非递归算法: "+TreeNode.sumOfNode(bTree));
System.out.println("结点数递归算法: "+TreeNode.sumOfNodeRec(bTree));
System.out.println("二叉树的深度非递归算法: "+TreeNode.TreeDepth(bTree));
System.out.println("二叉树的深度递归算法: "+TreeNode.TreeDepthRec(bTree));
System.out.println("二叉树第3层的节点个数: "+TreeNode.getNodeNumKthLevelRec(bTree,3));
System.out.println("二叉树叶子节点数递归算法: "+TreeNode.getNodeNumLeafRec(bTree));
System.out.println("二叉树叶子节点数非递归算法: "+TreeNode.getNodeNumLeaf(bTree));
//判断两棵二叉树是否相同的树
TreeNode node2=new TreeNode(2,new TreeNode(7),new TreeNode(8));
TreeNode node3=new TreeNode(4,new TreeNode(10),new TreeNode(8));
TreeNode bTree1=new TreeNode(3,node2,node3);
System.out.println("判断两棵二叉树是否相同的树递归算法: "+TreeNode.isSameRec(bTree,bTree1));
System.out.println("判断两棵二叉树是否相同的树非递归算法: "+TreeNode.isSame(bTree,bTree1));
System.out.println("判断两棵二叉树是否二叉平衡树: "+TreeNode.isAVLTree(bTree1));
System.out.println("二叉树的镜像递归算法(不破坏原树前序遍历输出): "+TreeNode.preorderTraversal(mirrorRecCopy(bTree)));
//未被破坏,执行结果与上一行一致
System.out.println("二叉树的镜像递归算法(破坏原树前序遍历输出): "+TreeNode.preorderTraversal(mirrorRec(bTree)));
System.out.println("二叉树的镜像非递归算法(不破坏原树前序遍历输出): "+TreeNode.preorderTraversal(mirrorCopy(bTree1)));
System.out.println("二叉树的镜像非递归算法(破坏原树前序遍历输出): "+TreeNode.preorderTraversal(mirror(bTree1)));
/*注意新建的两棵树均被破坏!!!!!
* 故新建一棵树,执行下面的算法!!!
*/
TreeNode node4=new TreeNode(2,new TreeNode(1),new TreeNode(4));
TreeNode bTree2=new TreeNode(7,node4,new TreeNode(8));
//判断两棵树是否镜像
System.out.println("判断两棵二叉树是否镜像: "+TreeNode.isMirrorRec(bTree2,mirrorRecCopy(bTree2)));
//判断二叉树是否BST
System.out.println("判断二叉树是否BST: "+TreeNode.isValidBST(bTree2));
}
}