java遞迴之“二叉樹”

來源:互聯網
上載者:User

標籤:代碼   一個   center   指令   朋友   treenode   ==   入行   new   

物有本末,事有始終,知所先後,則近道矣。-----題記。

BotWong半路入行it做碼農,也就半年時間,竟“不知天高地厚”地來到了深圳闖天下。一口氣投了百個簡曆,一周后終於有公司邀約面試,除了基礎的java文法和開發經驗,大一點的公司都會出幾道題給你做(演算法題)。BotWong是一頭霧水,而且心裡很生氣!氣!氣!氣!以前自己學的是“人類心理學理論與實踐”專業,唯一的電腦基礎也就是用過word、excel給客戶報過價。自己硬著頭皮把java文法和javaEE架構學了一遍,像我這樣的個例已經算是出類拔萃的了,為什麼還要對我這麼苛刻,哼!BotWong從那以後就開始討厭那些喜歡考試的公司,並發誓以後再也不去那樣的公司面試!

我和BotWong是好朋友,他提出了自己的困惑:到底要不要學這些看著“沒用”的演算法,問我該怎麼看待這件事,該如何做。好在BotWang還是個頭腦清醒的人,要不然也不會問這樣的問題了。我看他挺有誠意,就把自己以前寫的一些代碼,其中就包含一些演算法,拿出來泛泛地說了一遍,建議他自己都去實現一遍。BotWang消失了三月,再見之時,已能感知其功力大漲,已非三月前的那個BotWong了。我心甚慰,特此將自己以前寫過的集合類都貼出來,方便自己和網上的寶寶們隨時翻看,隨時補充能量。

一、二叉樹類:

 

package tree;/** * 二叉樹資料載體類 * @author tery * * @param <T> */public class BinaryTreeNode<T> {    private T data;//權值    private BinaryTreeNode<T> left;//左孩子    private BinaryTreeNode<T> right;//右孩子    public BinaryTreeNode(T data){        this.data=data;        left=right=null;    }    public T getData(){        return this.data;    }    public void setData(T data){        this.data=data;    }    public BinaryTreeNode<T> getLeft() {        return left;    }    public void setLeft(BinaryTreeNode<T> left) {        this.left = left;    }    public BinaryTreeNode<T> getRight() {        return right;    }    public void setRight(BinaryTreeNode<T> right) {        this.right = right;    }
  /**
  *插入權值
  */
@SuppressWarnings("unchecked") public BinaryTreeNode<T> insert(BinaryTreeNode<T> node,Integer data){ if(node==null){ return new BinaryTreeNode<T>((T)data); }
    //如果當前節點的權值大於data,那麼插入到左孩子節點上 if(Integer.valueOf(node.getData().toString())>data){ node.left=insert(node.getLeft(),data); }
   //如果當前節點的權值大於data,那麼插入到左孩子節點上
    if(Integer.valueOf(node.getData().toString())<data){ node.right=insert(node.getRight(),data); }
    //相等拋異常 
    if(Integer.valueOf(node.getData().toString())==data){
    throw new IllegalArgumentException("the data:"+data+"is already exsist in the tree");
    }
    return node;
}
}

insert方法中涉及到的遞迴:

        0: aload_1        1: ifnonnull     13        4: new           #1                  // class tree/BinaryTreeNode        7: dup        8: aload_2        9: invokespecial #45                 // Method "<init>":(Ljava/lang/Object;)V        12: areturn        13: aload_1        14: getfield      #20                 // Field data:Ljava/lang/Object;          23: aload_2        30: if_icmple     79        36: getfield      #24                 // Field left:Ltree/BinaryTreeNode;        39: aload_2        40: invokevirtual #53                 // Method insert:(Ltree/BinaryTreeNode;Ljava/lang/Object;)Ltree/BinaryTreeNode;        43: putfield      #24                 // Field left:Ltree/BinaryTreeNode;           79: aload_1        80: areturn

 

上面是insert方法的jvm執行的指令,我摘抄了部分關鍵點說明一下遞迴是怎麼做的:

insert函數棧中的局部變數表中的參數:第一個是當前樹節點對象(暫且稱為ref0),第二個是函數中傳進來的node對象的引用(稱為ref1),第三個是傳進來的常量data(在常量池中#20)

0: aload_1 將第一個參考型別局部變數推送至棧頂,那個引用也就是insert方法中的node對象的引用(ref1)

1 : ifnonnull 13 如果ref1不為空白,則跳到13行執行

4-12 : 如果是空,則此節點即是我們要插入的節點,調用它的建構函式<init>方法,將data值賦給它,然後areturn(將這個新產生的對象引用返回)

13 : aload_1 將ref1壓入棧頂

14:getfield  將常量池中當前對象ref1的data的值壓入棧頂

23:aload_2 將傳入的data值壓入棧頂

30:if_icmple 79,如果傳入的data值小於ref1的權值,那麼就跳到79執行

36:getfield #24 ,將常量池中#24,即left壓入棧中,即left常量的引用

39:aload_2 將data值壓入棧中。實際上36、39兩步的指令都是在為下面40行做準備,40行調用insert方法,傳遞給它需要的參數

40:invokevirtrual #53,調用insert方法,將36、39行準備的兩個參數帶過去

43:putfield #24 ,給當前執行個體ref1的left欄位賦值

79:aload_1 ,將當前執行個體ref1壓入棧中

80:areturn ,將棧頂的ref1返回

如果你不太懂,我還給你畫了個圖,協助你理解:

假設jvm只執行了在左邊樹插入的遞迴步驟:

上面的位元組碼指令和圖解是截取了部分關鍵點進行講解的,如果想進一步探討的寶寶們,可以聯絡我詳細討論。

 

三、二叉樹工具類:

 

1. 前根序遍曆:先遍曆根結點,然後遍曆左子樹,最後遍曆右子樹。(ABDHECFG)

2.中根序遍曆:先遍曆左子樹,然後遍曆根結點,最後遍曆右子樹。(HDBEAFCG)

3.後根序遍曆:先遍曆左子樹,然後遍曆右子樹,最後遍曆根節點(HDEBFGCA)

package tree;import java.util.ArrayList;import java.util.List;import java.util.Stack;public class BinaryTreeUtil {    /**     * 用遞迴的方式實現二叉樹的前序走訪     * @param root     * @return     */    public static<T> List<T> preOrderVisit(BinaryTreeNode<T> root){        List<T> result=new ArrayList<>();        preOrderVisit(root,result);        return result;    }    private static<T> void preOrderVisit(BinaryTreeNode<T> node, List<T> result) {        //如果節點為空白,返回        if(node==null){            return;        }        //不為空白,則加入節點的值        result.add(node.getData());        //先遞迴左孩子        preOrderVisit(node.getLeft(),result);        //再遞迴右孩子        preOrderVisit(node.getRight(),result);    }    /**     * 用遞迴的方式實現二叉樹的中序遍曆     * @param root     * @return     */    public static<T> List<T> inOrderVisit(BinaryTreeNode<T> root){        List<T> result=new ArrayList<T>();        inOrderVisit(root,result);        return result;    }    private static<T> void inOrderVisit(BinaryTreeNode<T> node, List<T> result) {        if(node==null){            return;        }        inOrderVisit(node.getLeft(),result);        result.add(node.getData());        inOrderVisit(node.getRight(),result);    }    /**     * 用遞迴的方式實現二叉樹的後遍曆     * @param root     * @return     */    public static<T> List<T> postOrderVisit(BinaryTreeNode<T> root){        List<T> result=new ArrayList<T>();        postOrderVisit(root,result);        return result;    }    private static<T> void postOrderVisit(BinaryTreeNode<T> node, List<T> result) {        if(node==null){            return;        }        postOrderVisit(node.getLeft(),result);        postOrderVisit(node.getRight(),result);        result.add(node.getData());    }    /**     * 用非遞迴的方式實現前序走訪     * @param root     * @return     */    public static<T> List<T> preOrderVisitWithoutRecursion(BinaryTreeNode<T> root){        List<T> result=new ArrayList<T>();        Stack<BinaryTreeNode<T>> stack=new Stack<>();        if(root!=null){            stack.push(root);        }        while(!stack.isEmpty()){            BinaryTreeNode<T> node=stack.pop();            result.add(node.getData());            if(node.getRight()!=null){                stack.push(node.getRight());            }            if(node.getLeft()!=null){                stack.push(node.getLeft());            }        }        return result;    }    /**     * 用非遞迴的方式實現中序遍曆     * @param root     * @return     */    public static<T> List<T> inOrderVisitWithoutRecursion(BinaryTreeNode<T> root){        List<T> result=new ArrayList<T>();        Stack<BinaryTreeNode<T>> stack=new Stack<>();        BinaryTreeNode<T> node=root;        while(node!=null || !stack.isEmpty()){            while(node!=null){                stack.push(node);                node=node.getLeft();            }            BinaryTreeNode<T> currentNode=stack.pop();            result.add(currentNode.getData());            node=currentNode.getRight();        }        return result;    }

二叉樹部分上面總共列舉了15個方法,主要的思想還是用遞迴。關於遞迴,為了讓寶寶們明白是怎麼回事,我也是花了點心思去解釋的,請各位再深入思考一下。裡面涉及到了jvm函數調用層面的知識,如果不懂,建議大家去看看jvm的書,瞭解jvm怎麼執行代碼,那就可以輕輕鬆鬆地理解遞迴到底是怎麼執行的了。

java遞迴之“二叉樹”

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.