二叉樹的基本問題:
二叉樹是遞歸定義的,因此相關問題基本都可以用遞歸實現。遞歸在本質上就是一個棧。
二叉搜索樹:對於樹中的每個節點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