您的位置:首页 > 理论基础 > 数据结构算法

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

2014-03-13 22:19 330 查看
最近复习数据结构,自己将二叉树的常见问题写了一篇。部分代码参考了如下博文:1.http://blog.csdn.net/fightforyourdream/article/details/168433032.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){//不是叶子节点的话,直接反回false
return 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){//左右子树都没有找到,说明这个节点没有,删除之,并返回false
list.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();
}
}


测试用例中所用的二叉树结构如下图:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: