重温数据结构:二叉树的常见问题汇总

来源:互联网 发布:淘宝关键词搜索 编辑:程序博客网 时间:2024/06/14 16:46

最近复习数据结构,自己将二叉树的常见问题写了一篇。部分代码参考了如下博文:

1.http://blog.csdn.net/fightforyourdream/article/details/16843303

2.http://blog.csdn.net/luckyxiaoqiang/article/details/7518888

解决的常见问题为:

1. 求二叉树中的节点个数
2. 求二叉树的深度
3. 前序遍历,中序遍历,后序遍历
4. 分层遍历二叉树
5. 将二叉查找树变为有序的双向链表
6. 求二叉树第K层的节点个数
7. 求二叉树中叶子节点的个数
8. 求二叉树的镜像
9.由前序遍历序列和中序遍历序列重建二叉树
10.判断二叉树是不是完全二叉树
11.获取任意节点与根节点的路径
12. 求二叉树中两个节点的最低公共祖先节点

13.求二叉树的宽度

14.判断二叉树是否是AVL树(平衡二叉树)

源代码如下:

package edu.njupt.zhb;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Queue;import java.util.Stack;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.LinkedBlockingQueue;/* *@author: ZhengHaibo   *web:     http://blog.csdn.net/nuptboyzhb *mail:    zhb931706659@126.com *2014-3-13  Nanjing,njupt,China *//** * 二叉树的实现 */public class MyBinaryTree {/** * 二叉树的节点 */public class TreeNode{Object data;//数据区TreeNode left;//左子树TreeNode right;//右子树}public MyBinaryTree(){}/** * 先序遍历的递归实现 * 根->左子树->右子树 * @param root * @param list */public void preOrderTraverse(TreeNode root,List list){if(root==null){return;}list.add(root.data);preOrderTraverse(root.left, list);preOrderTraverse(root.right, list);}/** * 先序遍历的非递归实现,借助单栈 * @param root * @param list */public void preOrderLoop(TreeNode root,List list){if(root==null){return;}Stack<TreeNode> stack = new Stack<TreeNode>();stack.push(root);while(!stack.isEmpty()){TreeNode node = stack.pop();list.add(node.data);if(node.right!=null){//如果右子树不为空,入栈stack.push(node.right);}if(node.left!=null){////如果左子树不为空,入栈stack.push(node.left);}}}/** * 中序遍历的递归实现 * 左子树->根->右子树 * @param root * @param list */public void inOrderTraverse(TreeNode root,List list){if(root==null){return;}inOrderTraverse(root.left, list);list.add(root.data);inOrderTraverse(root.right, list);}/** * 中序遍历的非递归实现(双栈) * 左子树->根->右子树 * @param root * @param list */public void inOrderLoop(TreeNode root,List list){if(root==null){return ;}Stack<TreeNode> stack = new Stack<TreeNode>();TreeNode pNode=root;while(pNode!=null||!stack.isEmpty()){while(pNode!=null){//一直将该节点的左子树入栈stack.push(pNode);pNode=pNode.left;}if(!stack.isEmpty()){pNode=stack.pop();list.add(pNode.data);pNode=pNode.right;//转向右子树进行下一次循环}}}/** * 后序遍历的递归实现 * 左子树->右子树->根 * @param root * @param list */public void postOrderTraverse(TreeNode root,List list){if(root==null){return;}postOrderTraverse(root.left, list);postOrderTraverse(root.right, list);list.add(root.data);}public void postOrderTraverse2(TreeNode root,List<TreeNode> list){if(root==null){return;}postOrderTraverse2(root.left, list);postOrderTraverse2(root.right, list);list.add(root);}/** * 后序编列的非递归方式(双栈法) * 后序遍历为:左子树->右子树->根节点 * 然而,我们知道,前序遍历的非递归方式非常容易实现。我们倒着输出 * 根节点->右子树->左子树的方式就是后序遍历。而根节点->右子树->左子树的 * 非递归实现和前序遍历的非递归实现非常相似,只是颠倒一下左右子树的顺序 * @param root * @param list */public void postOrderLoop(TreeNode root,List list){if(root==null){return;}Stack<TreeNode> stack = new Stack<TreeNode>();//用于遍历Stack<TreeNode> output = new Stack<TreeNode>();//用于保存逆序stack.push(root);while (!stack.isEmpty()) {TreeNode treeNode = stack.pop();output.push(treeNode);if(treeNode.left!=null){//先将左子树入栈stack.push(treeNode.left);}if(treeNode.right!=null){//再将右子树入栈stack.push(treeNode.right);}}/** * 将output栈中的数据放入list中即可完成了反转 */while(!output.isEmpty()){list.add(output.pop().data);}}//public void postOrderLoop(TreeNode root,List list){//if(root==null){//return;//}//TreeNode pNode=root;//Stack<TreeNode> stack=new Stack<TreeNode>();//while(pNode!=null||!stack.isEmpty()){//while(pNode!=null){//左右不断深入//stack.push(pNode);//先将根节点入栈//if(pNode.left!=null){//pNode=pNode.left;//}else {//pNode=pNode.right;//}//}//if(!stack.isEmpty()){//pNode=stack.pop();//取出栈顶根节点,访问之//list.add(pNode.data);//}////满足条件时,说明栈顶根节点右子树已经访问,应出栈访问之//while(!stack.isEmpty()&&stack.peek().right.equals(pNode)){//pNode=stack.pop();//list.add(pNode.data);//}////转向栈顶根节点的右子树继续后续遍历//if(!stack.isEmpty()){//pNode=stack.peek().right;//}else {//pNode=null;//}//}//}/** * 层次遍历 * @param root * @param list */public void levelOrderTraverse(TreeNode root,List list){if(root==null){return;}Queue queue = new LinkedBlockingDeque();queue.add(root);while(!queue.isEmpty()){TreeNode node = (TreeNode)queue.remove();list.add(node.data);if(node.left!=null){queue.add(node.left);}if(node.right!=null){queue.add(node.right);}}}/** * 获取二叉树中节点的个数 * 递归解法: * (1)如果二叉树为空,节点个数为0 * (2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1 * @param root * @return */public int getNodeNum(TreeNode root){if(root==null){return 0;}return getNodeNum(root.left)+getNodeNum(root.right)+1;}/** * 求二叉树第K层的节点个数 * 递归解法: * (1)如果二叉树为空或者k<1返回0 * (2)如果二叉树不为空并且k==1,返回1 * (3)如果二叉树不为空且k>1,返回k-1层中左子树的节点个数与右子树节点个数之和 * @param root * @param k * @return */public int getKthNodeNum(TreeNode root,int k){//*if(root==null||k<1){return 0;}if(k==1){return 1;}int leftNum = getKthNodeNum(root.left, k-1);int rightNum = getKthNodeNum(root.right, k-1);return leftNum+rightNum;}/** * 求二叉树中叶子节点的个数 * 递归解法: * (1)如果二叉树为空,返回0 * (2)如果二叉树不为空且左右子树为空,返回1 * (3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数 * @param root * @return */public int getLeafNodeNum(TreeNode root){if(root==null){return 0;}if(root.left==null&&root.right==null){return 1;}int leftNum = getLeafNodeNum(root.left);int rightNum = getLeafNodeNum(root.right);return leftNum+rightNum;}/** * 求二叉树的镜像 * 递归解法: * (1)如果二叉树为空,返回空 * (2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树 * @param root * @return */public TreeNode mirrorTree(TreeNode root){if(root==null){return null;}TreeNode leftNode = mirrorTree(root.left);TreeNode rightNode = mirrorTree(root.right);root.left=rightNode;root.right=leftNode;return root;}/** * 求解二叉树的镜像(非递归方式) * 思路:借助前序遍历的非递归方式改进 * @param root */public void mirrorTreeLoop(TreeNode root){if(root==null){return;}Stack stack=new Stack();stack.push(root);while(!stack.isEmpty()){TreeNode treeNode=(TreeNode)stack.pop();//交换左右子树TreeNode temp = treeNode.right;treeNode.right=treeNode.left;treeNode.left=temp;if(treeNode.right!=null){stack.push(treeNode.right);}if(treeNode.left!=null){stack.push(treeNode.left);}}}/** * 查找元素object所在的节点 * @param root * @param object * @return */public TreeNode searchNode(TreeNode root,Object object){if(root==null){return null;}if(root.data.equals(object)){return root;}TreeNode node = searchNode(root.left, object);if(node==null){node = searchNode(root.right, object);}return node;}public boolean isContains(TreeNode root,TreeNode node){if(root==null){return false;}if(root==node){return true;}boolean isFound=false;isFound=isContains(root.left, node);if(!isFound){isFound=isContains(root.right, node);}return isFound;}/** * 二叉树的深度 * 递归解法: * (1)如果二叉树为空,二叉树的深度为0 * (2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1 * @param root * @return */public int getTreeDepth(TreeNode root){if(root==null){return 0;}int leftDepth=getTreeDepth(root.left);int rigthDepth=getTreeDepth(root.right);int depth=leftDepth>=rigthDepth?leftDepth+1:rigthDepth+1;return depth;}/** * 获取二叉树的宽度 * 宽度的定义:二叉树每一层中节点的最多个数 * @param root * @return */public int getTreeWidth(TreeNode root){if(root==null){return 0;}int depth=getTreeDepth(root);int width=0;for(int i=1;i<=depth;i++){int temp=getKthNodeNum(root, i);if(temp>width){width=temp;}}return width;}/** * 判断二叉树是不是完全二叉树(层次遍历) * 定义: * 若设二叉树的深度为h,除第 h 层外, * 其它各层 (1~h-1) 的结点数都达到最大个数, * 第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。 * 算法判断: * 按层次(从上到下,从左到右)遍历二叉树, * 当遇到一个节点的左子树为空时,则该节点右子树必须为空, * 且后面遍历的节点左右子树都必须为空,否则不是完全二叉树。 * @param root * @return */public boolean isCompleteBinaryTree(TreeNode root){if (root==null) {return false;}Queue queue = new LinkedBlockingQueue();queue.add(root);boolean flag=false;//判断是否出现了有左子树但无右子树的节点while(!queue.isEmpty()){TreeNode node = (TreeNode)queue.remove();if(flag){//剩下的节点只能是叶子节点if(node.left!=null||node.right!=null){//不是叶子节点的话,直接反回falsereturn false;}}else{if(node.left!=null&&node.right!=null){queue.add(node.left);queue.add(node.right);}else if(node.left!=null&&node.right==null){queue.add(node.left);flag=true;}else if(node.left==null&&node.right!=null){return false;//肯定不是完全二叉树}else if(node.left==null&&node.right==null){flag=true;}}}return true;}/** * 获取根节点到任意节点node的路径(递归) * 通过递归的方式,在左右子树上寻找,如果左右子树均没有,则删除该节点 * 否者,该节点是其路径上的节点之一 * @param root * @param node * @param list */public boolean getTreePath(TreeNode root,TreeNode node,List list){if(root==null){return false;}list.add(root);if(root==node){return true;}boolean isFound=false;isFound=getTreePath(root.left, node, list);//在左子树上寻找if(!isFound){//如果左子树没找,在右子树上寻找isFound=getTreePath(root.right, node, list);}if(!isFound){//左右子树都没有找到,说明这个节点没有,删除之,并返回falselist.remove(list.size()-1);return false;}return true;}/** * 寻找任意两个节点的最低公共祖先节点 * 思路:寻找根节点到node1的路径和到node2的路径 * 两条路径最后一个相同的节点即为最低公共祖先节点 * @param root * @param node1 * @param node2 * @return */public TreeNode getNearComParentNode(TreeNode root,TreeNode node1,TreeNode node2){List<TreeNode> path1=new LinkedList<TreeNode>();List<TreeNode> path2=new LinkedList<TreeNode>();boolean flag1=getTreePath(root, node1, path1);boolean flag2=getTreePath(root, node2, path2);if(!flag1||!flag2){//其中有节点不在二叉树中return null;}int i=0,j=0,index=0;//搜索两条路径的最后一个相同的节点while (i<path1.size()&&j<path2.size()) {if(path1.get(i).equals(path2.get(j))){index=i;}else{break;}i++;j++;}return path1.get(index);}/** * 寻找任意两个节点的最低公共祖先节点 * 思路2:首先后序遍历,最低的公共祖先节点一定在这个两个节点的后面 * 然后逐步遍历后面的节点,第一个既包含node1又包含node2的节点即是所求节点 * @param root * @param node1 * @param node2 * @return */public TreeNode getNearComParentNode2(TreeNode root,TreeNode node1,TreeNode node2){List<TreeNode> list= new ArrayList<TreeNode>();postOrderTraverse2(root, list);//后序遍历int flag=0;int i=0;for(i=0;i<list.size();i++){//查找两个节点之后的索引值TreeNode node=list.get(i);if(node.equals(node1)||node.equals(node2)){flag++;if(flag==2){break;}}}if(flag==2){//已查到两个节点,后面的节点中依次遍历,第一个既包含node1又包含node2的节点即是for(int j=i;i<list.size();i++){TreeNode node=list.get(i);if(isContains(node, node1)&&isContains(node, node2)){return node;}}}return null;}/** * 判断是否是平衡二叉树 * (1)如果二叉树为空,返回真      * (2)如果二叉树不为空,如果左子树和右子树都是AVL树     * 并且左子树和右子树高度相差不大于1,返回真,其他返回假 * @param root * @return */public boolean isAVLTree(TreeNode root){if(root==null){return true;}int leftDepth=getTreeDepth(root.left);int rightDepth=getTreeDepth(root.right);if(Math.abs(leftDepth-rightDepth)>1){return false;}boolean isLeft=isAVLTree(root.left);boolean isRight=isAVLTree(root.right);return isLeft&&isRight;}/** * 根据先序遍历和中序遍历重建二叉树 * 思路:递归 * 例如:先序遍历:ABDEFC 中序遍历:DBEFAC * 1.先取出先序遍历的第一个节点,为根节点 * 2.根据根节点在中序遍历中的位置,将中序遍历分割成2部分 * 3.根据中序遍历分割后的长度,将先序遍历对应分割 * 4.递归方式,重建左子树和右子树 * @param perOrder * @param inOrder * @return */public TreeNode rebuildTree(List perOrder,List inOrder){if(perOrder.size()!=inOrder.size()){//参数有误return null;}TreeNode root=null;if(perOrder.size()>0){root=new TreeNode();//新建节点Object perRoot=perOrder.get(0);//前序遍历的第一个节点为根节点root.data=perRoot;int indexInOrder=inOrder.indexOf(perRoot);//查找根节点在中序节点的位置//根据根节点的位置,接那个中序遍历分成左右两个中序序列List leftInOrder=inOrder.subList(0,indexInOrder);List rightInOrder=inOrder.subList(indexInOrder+1,inOrder.size());//根据左右中序遍历的长度,依次分割其对应的前序遍历List leftPerOrder=perOrder.subList(1, leftInOrder.size()+1);List rightPerOrder=perOrder.subList(leftInOrder.size()+1,perOrder.size());//递归的方式,依次重建root.left=rebuildTree(leftPerOrder, leftInOrder);root.right=rebuildTree(rightPerOrder, rightInOrder);}return root;}public void test(){TreeNode root=new TreeNode();root.data="A";TreeNode nodeB=new TreeNode();nodeB.data="B";TreeNode nodeC=new TreeNode();nodeC.data="C";TreeNode nodeD=new TreeNode();nodeD.data="D";TreeNode nodeE=new TreeNode();nodeE.data="E";TreeNode nodeF=new TreeNode();nodeF.data="F";        //////////////////////设置节点的关系root.left=nodeB;root.right=nodeC;nodeB.left=nodeD;nodeB.right=nodeE;nodeE.right=nodeF;///////////一下测试上述方法的正确性List list = new LinkedList();//preOrderTraverse(root, list);//preOrderLoop(root,list);//inOrderTraverse(root, list);//inOrderLoop(root, list);//postOrderTraverse(root, list);//postOrderLoop(root, list);//levelOrderTraverse(root, list);//for(Object object:list){//System.out.print((String)object+" ");//}//System.out.println(getKthNodeNum(root, 4));//System.out.println(getLeafNodeNum(root));//System.out.println(getNodeNum(root));//TreeNode treeNode = searchNode(root, "E");//System.out.println(treeNode.right.data);//F//System.out.println(getTreeDepth(root));//TreeNode newRootNode = mirrorTree(root);//levelOrderTraverse(newRootNode, list);//for(Object object:list){//    System.out.print((String)object+" ");//    }//mirrorTreeLoop(root);//levelOrderTraverse(root,list);//for(Object object:list){//    System.out.print((String)object+" ");//    }//System.out.println(isCompleteBinaryTree(root));//getTreePath(root, nodeC, list);//for(Object object:list){//System.out.print((String)((TreeNode)object).data+" ");//}//TreeNode node= getNearComParentNode2(root, nodeE, nodeF);//System.out.println(node.data);//System.out.println(getTreeWidth(root));//System.out.println(isAVLTree(root));List perOrderList = new LinkedList();List inOrderList = new LinkedList();preOrderTraverse(root, perOrderList);inOrderTraverse(root, inOrderList);TreeNode treeNode=rebuildTree(perOrderList, inOrderList);List postOrderList=new LinkedList();postOrderTraverse(treeNode, postOrderList);for (Object object : postOrderList) {System.out.print((String) object + " ");}}public static void main(String[] args) {MyBinaryTree myBinaryTree = new MyBinaryTree();myBinaryTree.test();}}


测试用例中所用的二叉树结构如下图:



0 0
原创粉丝点击