搜索二叉樹

二叉樹的基本問題:
二叉樹是遞歸定義的,因此相關問題基本都可以用遞歸實現。遞歸在本質上就是一個棧。

二叉搜索樹:對於樹中的每個節點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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章