二叉树的基本问题:
二叉树是递归定义的,因此相关问题基本都可以用递归实现。递归在本质上就是一个栈。
二叉搜索树:对于树中的每个节点X,它的左子树中所有项的值都小于X,右子树所有值都大于X。
定义一个二叉树:
private class BSTNode<T>{
T key;
BSTNode<T> left;
BSTNode<T> right;
//constructors
public BSTNode(T key){
this(key,null,null);
}
public BSTNode(T key,BSTNode<T> left,BSTNode<T> right){
this.key= key;
this.left=left;
this.right=right;
}
}
基本方法:查找 search ,发现最大最小值,插入,打印,遍历
public class BSearchTree <T extends Comparable>{
private class BSTNode<T>{
T key;
BSTNode<T> left;
BSTNode<T> right;
//constructors
public BSTNode(T key){
this(key,null,null);
}
public BSTNode(T key,BSTNode<T> left,BSTNode<T> right){
this.key= key;
this.left=left;
this.right=right;
}
}
private BSTNode<T> mRoot;//根结点
/*
* 前序遍历 ----递归-----
* */
public void preOrder(){
if(isEmpty()){
System.out.println("Empty tree");
}else{
preOrder(mRoot);}
}
private void preOrder(BSTNode<T> tree){
if(tree!=null){
System.out.print(tree.key+" ");
preOrder(tree.left);
preOrder(tree.right);
}
}
/*
* 中序,后序遍历递归 类似。
* */
/*
* http://www.gocalf.com/blog/traversing-binary-tree.html#id9
* 前序和中序: ---非递归---
* (非递归方式使用的栈(数据结构))
* 需要自己维护一个栈来保存需要但尚未来得及处理的数据。
*
* */
public void interativePreOrder(String Order){
interativePreOrder(mRoot,Order);
}
private void interativePreOrder(BSTNode<T> tree,String Order){
Stack<BSTNode<T>> stack = new Stack<BSTNode<T>>();
//这里不可以写成 stack!=null
while(tree!=null||stack.size()>0){
while(tree!=null){
if(Order=="NLR"){
System.out.print(tree.key+" ");}
stack.push(tree);
tree=tree.left;
}
if(stack.size()>0){
tree = stack.pop();
if(Order=="RLR"){
System.out.print(tree.key+" ");
}
tree=tree.right;
}
}
}
/*
* 后序遍历: ---非递归--
* 下面
* */
/*
* 查找 ---递归---
* */
public BSTNode<T> search(T key){
return search(mRoot,key);
}
private BSTNode<T> search(BSTNode<T> x,T key){
if(x==null)
return x;
int cmp=key.compareTo(x.key);
if(cmp<0){
return search(x.left,key);
}else if(cmp>0){
return search(x.right,key);
}else
return x;
}
/*
* 查找 ---非递归---
* */
public BSTNode<T> iterativeSearch(T key){
return iterativeSearch(mRoot,key);
}
private BSTNode<T> iterativeSearch(BSTNode<T> x,T key){
while(x!=null){
int cmp = key.compareTo(x.key);
if(cmp < 0)
x=x.left;
else if(cmp > 0)
x=x.right;
else return x;
}
return x;
}
/*
* 查找最小节点:返回tree 为根节点的二叉树最小节点
* */
public T findMin(){
return findMin(mRoot).key;
}
private BSTNode<T> findMin(BSTNode<T> x){
if(x==null)
return null;
while (x.left!=null){
x=x.left;
}
return x;
}
/*
* 查找最大节点 ---递归方法----
* */
public T findMax(){
return findMax(mRoot).key;
}
private BSTNode<T> findMax(BSTNode<T> x){
if(x==null)
return null;
else if(x.right==null)
return x;
//递归实现
return findMax(x.right);
}
/*
* 插入方法 insert ----递归---
* */
public void insert(T x){
BSTNode<T> newNode = new BSTNode<T>(x,null,null);
//mRoot=insert(newNode,mRoot);
mRoot=insert(newNode,mRoot);
}
/*
* a.若当前的二叉查找树为空,则插入的元素为根节点
* b.若插入的元素值小于根节点值,则将元素插入到左子树中
* c.若插入的元素值不小于根节点值,则将元素插入到右子树中。
* 首先找到插入的位置,要么向左,要么向右,直到找到空结点,即为插入位置,
* 如果找到了相同值的结点,插入失败
* */
private BSTNode<T> insert(BSTNode<T> newNode,BSTNode<T> root){
/*
* 比如遍历到某个节点的右节点为空,
* 则让 newNode 为这个节点的根节点,即插入了该点
* */
if(root==null)
root=newNode;
int cmp = newNode.key.compareTo(root.key);
if(cmp < 0)
//忘记加 root.left,创建的是空树??是不是root就是子节点了,而不是最初的根节点了
// return insert(newNode,root.left);
//忘记加root.left 无法在root与left之间建立关系。所以最后就只有一个根
root.left=insert(newNode,root.left);
else if(cmp > 0)
//return insert(newNode,root.right);
root.right= insert(newNode,root.right);
else
;//重复节点,什么也不做
return root;
}
public boolean isEmpty(){
return mRoot==null;
}
/*
* 打印二叉树
* */
public void print(){
if(mRoot!=null){
print(mRoot,mRoot.key,0);
}
}
private void print(BSTNode<T> tree, T key, int direction){
if(tree!=null){
if(direction==0)//tree是根节点
System.out.println(tree.key+" is root;");
else if(direction==1)
System.out.print(tree.key+" is "+key + " right;");
else System.out.print(tree.key+" is "+key+" left;");
print(tree.left,tree.key,-1);
print(tree.right,tree.key,1);
}
}
public static void main(String[] args) {
int []arr={1,5,4,3,2,6};
BSearchTree<Integer> tree= new BSearchTree<Integer>();
System.out.println("依次添加");
int n=arr.length;
for(int i=0;i<n;i++){
//System.out.print(arr[i]+" ");
tree.insert(arr[i]);
}
//tree.preOrder();
System.out.println("\n===print");
tree.print();
System.out.println("\n===findmin "+tree.findMin());
System.out.println("===findmax "+tree.findMax());
System.out.println("===search 5 :"+tree.search(5).key);
System.out.println("===插入9");
tree.insert(9);
System.out.println("===递归前序遍历");
tree.preOrder();
System.out.println("\n 非递归前序");
tree.interativePreOrder("NLR");
System.out.println("\n非递归中序");
tree.interativePreOrder("RLR");
System.out.println("\n非递归后序");
tree.iterativePostorder();
}
面试中二叉树的相关问题:
二叉树深度,分层遍历,转化为双向列表,结点个数,K 层节点,叶结点个数,两棵树是否相同,是否是平衡二叉树,根据遍历结果,重建二叉树,A是否是B的子树
/*
*一, 二叉树深度(递归)
* 1) 如果二叉树为空,节点个数为0;
* 2)不为空,深度=max(左子树深度,右子树深度)
* */
public int GetDep(){
return GetDep(mRoot);
}
private int GetDep(BSTNode <T> tree){
if(tree==null)
return 0;
//每个子节点又作为根节点,判断一次左右节点深度。
int leftDep=GetDep(tree.left);
int rightDep=GetDep(tree.right);
//返回左右子树深度的最大值
return (leftDep > rightDep)?(leftDep+1):(rightDep+1);
}
/*
* 二, 分层遍历(按层次从上向下,从左向右)
* 广度搜索(前序遍历等是深度搜索)
* 假设这树的结构如下:
* 8
* 6 10
* 5 7 9 11
*那么先是打印出8,然后将它的子结点6和10保存起来,然后打印6,保存它的两个子结点5和7,接着打印10...
*到了这里,可以察觉到,比起5和7,10先存放起来,然后又先打印出来,说明这是一个"先进先出"的结构,
*也就是所谓的队列。
* */
public void LevelTraverse(){
LevelTraverse(mRoot);
}
/*
* 先将树的根节点入队,
* 如果队列不空,则进入循环:
* 将队首元素出队,并输出; 若有左孩,则左孩入队;若有右孩,则右孩入队
* */
private void LevelTraverse(BSTNode<T> tree){
if(tree==null)
return;//这里为什么是return???是跳出这个函数?
LinkedList<BSTNode<T>> queue= new LinkedList<BSTNode<T>>();
queue.addLast(tree);
while(!queue.isEmpty()){
/*
* public E pop() {
* return removeFirst();
* }
* */
BSTNode<T> cur=queue.removeFirst();
System.out.print(cur.key+" ");
/*
* public void push(E e) {
* addFirst(e);
* }
* */
if(cur.left!=null)
queue.addLast(cur.left);
if(cur.right!=null)
queue.addLast(cur.right);
}
}
/*
* 三, 二叉树转化为有序的双向列表(递归方法)
* 因为是有序列表,搜索二叉树的特点可知,中序遍历是按照顺序来的
*
* 二叉树中的左结点总是比根结点小,而右结点又比根结点大,在双向链表中,
* 每个结点都有两个指针,一个指向前面的结点,另一个指向后面的结点。
* 根据这样的特性,二叉树的确可以转换成排序的双向链表。
* */
public BSTNode<T> convertBtoDLL (){
mRoot = convertBtoDLL(mRoot);
//root 会在链表的中间位置,因此需要手动把 root 移动到链表头??
while(mRoot.left!=null){
mRoot=mRoot.left;
}
return mRoot;
}
//递归转换BST 为DLL
private BSTNode<T> convertBtoDLL(BSTNode<T> root){
if(root==null||(root.left==null&&root.right==null)){
return root;
}
BSTNode<T> tmp=null;//是什么?
//处理左子树
if(root.left!=null){
tmp=convertBtoDLL(root.left);
//寻找最右节点
while(tmp.right!=null){
tmp=tmp.right;
}
//左子树处理后的结果和root 相连
tmp.right=root;
root.left=tmp;
}
//处理右子树
if(root.right!=null){
tmp = convertBtoDLL(root.right);
//寻找最左节点
while(tmp.left!=null){
tmp=tmp.left;
}
tmp.left=root;
root.right=tmp;
}
return root;
}
//输出DLL
public void printDll(BSTNode<T> root){
while(root!=null){
System.out.print(root.key+" ");
root=root.right;
}
}
/*
* 四,求二叉树结点个数 ----递归---
* */
public int GetNum(){
return GetNum(mRoot);
}
private int GetNum(BSTNode<T> tree){
if(tree==null)
return 0;
//进入某个左节点,以他为根,继续计算左右节点个数
return GetNum(tree.left)+GetNum(tree.right)+1;
}
/*
* 二叉树节点个数 ----非递归---
* 同分层遍历类似
* */
public int GetNum1(){
return GetNum1(mRoot);
}
private int GetNum1(BSTNode<T> root){
if(root==null)
return 0;
int count=0;
LinkedList<BSTNode<T>> queue=new LinkedList<BSTNode<T>>();
queue.addFirst(root);
while(queue.size()>0){
BSTNode<T> cur=queue.removeLast();
if(cur.left!=null){
queue.addLast(cur.left);
count++;
}
if(cur.right!=null){
queue.addLast(cur.right);
count++;
}
}
return count+1;
}
/*
* 五,求二叉树第K层的节点个数 ---递归---
*
* (1)如果二叉树为空或者k<1返回0
* (2)如果二叉树不为空并且k==1,返回1
* (3)如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和
*
* 求以root为根的k层节点数目 等价于 求以root左孩子为根的k-1层(因为少了root那一层)节点数目 加上
* 以root右孩子为根的k-1层(因为少了root那一层)节点数目
*
*
*/
public int getKnum(int k){
return getKnum(mRoot,k);
}
private int getKnum(BSTNode<T> root,int k){
if(k<1||root==null)
return 0;
else if (k==1)
return 1;
int leftNum=getKnum(root.left,k-1);
int rightNum=getKnum(root.right,k-1);
return (leftNum+rightNum);
}
/*
*六, 二叉树中叶子节点的个数 ----递归----
* 注意与求 节点个数 的不同
*
*(1)如果二叉树为空,返回0
*(2)如果二叉树不为空且左右子树为空,返回1
*(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数
* */
public int getLeafNum(){
return getLeafNum(mRoot);
}
private int getLeafNum(BSTNode<T> tree){
//root 不存在,返回0
if(tree==null)
return 0;
//root左右子树都为空,是叶节点,返回1
if(tree.left==null&&tree.right==null)
return 1;
int lef=getLeafNum(tree.left);
int rig=getLeafNum(tree.right);
return lef+rig;
}
/*求叶子节点个数 ----非递归---
* 类似于层序遍历,每一层没有子节点的就是叶节点,leafCount ++
* */
public int getLeafNum1(){
return getLeafNum(mRoot);
}
private int getLeafNum1(BSTNode<T> tree){
//不能忘记初始判断 是否为空
if(tree==null)
return 0;
int leafCount=0;//叶节点计数
LinkedList<BSTNode<T>> queue=new LinkedList<BSTNode<T>>();
queue.add(tree);
/*
* 将根节点放入队列,从开始取出,判断它是否有左右节点,没有,则是叶节点,
* 有的话,就将其左右节点推入队列中(此时队列中的是该节点同层的右侧,下一层该节点子节点前面的部分),
* 等待读取。
* */
while(queue.size()>0){
tree = queue.removeLast();
if(tree.left!=null)
queue.add(tree.left);
if(tree.right!=null)
queue.add(tree.right);
if(tree.left==null&&tree.right==null)
leafCount++;
}
return leafCount;
}
/*
* 七,判断两个树是否是同一个树 ---递归---
* (1)如果两棵二叉树都为空,返回真
* (2)如果两棵二叉树一棵为空,另一棵不为空,返回假
* (3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
* */
public boolean isSame(BSTNode<T> tree1,BSTNode<T> tree2){
if(tree1==null&&tree2==null)
return true;
if(tree1==null||tree2==null)
return false;
if(tree1.key!=tree2.key)
return false;
boolean left = isSame(tree1.left,tree2.left);
boolean right = isSame(tree1.right,tree2.right);
return left&&right;
}
/*
* 判断是否是相同的树 ---非递归---
* 遍历一遍即可
* */
public boolean isSame1(BSTNode<T>tree1,BSTNode<T>tree2){
if(tree1==null&&tree2==null)
return false;
if(tree1==null||tree2==null)
return false;
Stack<BSTNode<T>> s1=new Stack<BSTNode<T>>();
Stack<BSTNode<T>> s2=new Stack<BSTNode<T>>();
s1.push(tree1);
s2.push(tree2);
while(s1.size()>0&&s2.size()>0){
BSTNode<T> t1=s1.pop();
BSTNode<T> t2=s2.pop();
if(t1==null&&t2==null){
continue;
}else if(t1!=null&&t2!=null&&t1.key==t2.key){
s1.push(t1.right);
s1.push(t1.left);
s1.push(t2.right);
s1.push(t2.right);
}else {
return false;
}
}
return true;
}
/*
*八, 是否是平衡二叉树 ---递归---
* 二叉树不为空,若左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,
* */
public boolean isAVL(){
return isAVL(mRoot);
}
private boolean isAVL(BSTNode<T> tree){
if(tree==null)
return true;
if(Math.abs(GetDep(tree.left)-GetDep(tree.right))>1){
return false ;
}
return isAVL(tree.left)&&isAVL(tree.right);
}
//http://www.cnblogs.com/wenjiang/p/3321815.html#top
//http://biaobiaoqi.github.io/blog/2013/04/27/pat1020-pat1043-rebuild-binary-tree/
//http://blog.csdn.net/likebamboo/article/details/16845661
/*十,输入某二叉树的前序遍历和中序遍历的结果,重建该二叉树
* 假设前序遍历为{1, 2, 4, 7, 3, 5, 6, 8}, 中序遍历为{4, 7, 2, 1, 5, 3, 8, 6}
* 可以知道,根节点为1,由中序遍历可知{4,7,2}为左节点,{5,3,8,6}位右节点
*
* ps:搜索二叉树中节点根据大小的排序就是中序顺序,所以,题目还可以这样:
* 输入树的前序遍历序列,判定该树是否是二叉搜索树或 BST 的镜像树,如果是,后序序列输出。
* */
建立一个搜索二叉树,并后序遍历输出:
后序遍历非递归方式,参考:http://bookshadow.com/weblog/2015/01/19/binary-tree-post-order-traversal/
import java.util.Stack;
public class BinaryTree {
private class BSTNode{
int key;
BSTNode left;
BSTNode right;
public BSTNode(int key,BSTNode left,BSTNode right){
this.key= key;
this.left=left;
this.right=right;
}
}
private BSTNode mRoot;//根结点
//插入,建立新的二叉树
public void insert(int key){
BSTNode newNode = new BSTNode(key,null,null);
//最终也要保证生成的根节点是整棵树的根,所以最后要把root return
mRoot = insert(mRoot,newNode);
}
public BSTNode insert(BSTNode root,BSTNode newNode){
if(root==null) root=newNode;
if(newNode.key < root.key){
root.left = insert(root.left,newNode);
}else if(newNode.key > root.key){
root.right = insert(root.right,newNode);
}
return root;
}
/*
* 后序遍历
* 维护一个visited标记
* 判断栈顶元素。
* 如果栈顶元素有右节点,并且不是刚刚被访问的节点,则将栈顶的右子树作为root,循环push
* 如果栈顶无右子树,或者右子树被访问过,输出栈顶元素,修改pre元素
*
* */
public void PostSort(){
PostSort(mRoot);
}
public void PostSort(BSTNode root){
if(root==null) return;
Stack<BSTNode> s = new Stack<BSTNode>();
BSTNode pre =null;
while(root!=null||!s.isEmpty()){
while(root!=null){
s.push(root);
root=root.left;
}
if(s.peek().right!=null&&s.peek().right!=pre){
root = s.peek().right;
}else{
System.out.print(s.peek().key+" ");
pre = s.pop();
}
}
}
public static void main(String[] args) {
int [] arr ={7,3,5,6,1,9,11};
BinaryTree bt = new BinaryTree();
for(int i=0;i<arr.length;i++){
bt.insert(arr[i]);
}
bt.PostSort();
}
}
根据前序and中序,前序and 层序等构建二叉树以后再看。。。
关于二叉树的题目还有 是否是子树,镜像,二叉树两节点最大距离,最低公共祖先节点等等。。。
关于链表的题目集合: http://blog.csdn.net/fightforyourdream/article/details/16353519
参考链接:
http://blog.csdn.net/luckyxiaoqiang/article/details/7518888
http://www.gocalf.com/blog/traversing-binary-tree.html#id9
http://blog.csdn.net/fightforyourdream/article/details/16843303#comments
http://www.cnblogs.com/wenjiang/p/3321815.html#top