day05 二叉树算法题(面试)

转载注明!!https://blog.csdn.net/qq_31842777/article/details/90478093

写这篇博文的主要目的是作为自己的学习笔记,方便后续复习,也希望对大家有所帮助。
主要有以下9题:

  1. 二叉树结点数的计算【非递归算法、递归算法】
    思路:如果二叉树为空,返回二叉树结点为0;
    如果二叉树非空,返回leftNum+rightNum+1;
    两种算法时间复杂度皆为O(n);
  2. 二叉树的深度的计算【非递归算法、递归算法】
    思路:如果二叉树为空,返回二叉树深度为0;
    如果二叉树非空,返回max(leftLength.rightLength)+1;
    两种算法时间复杂度皆为O(n);
  3. 二叉树第k层的结点个数【递归算法】
    思路:如果二叉树为空,返回0;
    如果二叉树非空且k==1,返回1;
    如果二叉树非空且k>1,返回左子树k-1层的结点数+右子树k-1层的结点数;
    时间复杂度皆为O(n);
  4. 二叉树的叶子结点数【非递归算法、递归算法】
    思路:如果二叉树为空,返回二叉树结点为0;
    如果二叉树非空,返回leftNum+rightNum+1;
  5. 判断两棵二叉树是否相同【非递归算法、递归算法】
    思路:如果两棵二叉树为空,返回true;
    如果两棵二叉树一颗为空,另一颗不为空,返回false;
    如果两棵二叉树都不为空,且左右子树相同,返回true,否则,返回false;
  6. 二叉树的镜像【非递归算法、递归算法】
    思路:如果二叉树为空,返回空;
    如果二叉树不为空,求左子树和右子树的镜像,然后交换左右子树;
  7. 判断两棵二叉树是否镜像
    思路:如果两棵二叉树为空,返回true;
    如果两棵二叉树一颗为空,另一颗不为空,返回false;
    如果两棵二叉树都不为空,且左右子树镜像相同,返回true,否则,返回false;
  8. 判断两棵二叉树是否二叉平衡树AVL【递归算法】
    思路:如果二叉树为空,返回true;
    如果二叉树不为空且左子树和右子树都是AVL树,同时左子树和右子树高度相差不大于1,返回 true,否则,返回false;
  9. 判断二叉树是否二叉排序树BST【非递归算法】
    思路:中序遍历是递增的;
    把根结点及左子树一直入栈,完成后抛出,相当于抛出了中序遍历的结果,每次抛出一次比较当前值与前驱的大小,当前值大于前驱,继续,否则,返回false;

以下是程序实现,含测试:

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));
	}
}

你可能感兴趣的:(数据结构)