<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    John Jiang

    a cup of Java, cheers!
    https://github.com/johnshajiang/blog

       :: 首頁(yè) ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
      131 隨筆 :: 1 文章 :: 530 評(píng)論 :: 0 Trackbacks
    樹(shù)的遍歷
    之前的工作都沒(méi)有接觸到樹(shù),也就很少研究它。幸運(yùn)地的是,在目前的工作中多次遇到樹(shù)型結(jié)構(gòu)的數(shù)據(jù),那么訪問(wèn)樹(shù)節(jié)點(diǎn)中的數(shù)據(jù)就是必然的了,而且還需要按照指定規(guī)則對(duì)節(jié)點(diǎn)中的數(shù)據(jù)進(jìn)行額外處理。經(jīng)過(guò)學(xué)習(xí)之后,對(duì)與樹(shù)相關(guān)的基本算法有了一些認(rèn)知,就計(jì)劃寫(xiě)幾篇小文。其實(shí)這樣的文章早已是汗牛充棟,而我只是把它當(dāng)作我的學(xué)習(xí)總結(jié)罷了,以加深記憶與理解,如能對(duì)其他朋友有所助益,則更感愉悅了 :-) (2009.04.03最后更新)

    這次先從最基礎(chǔ)的開(kāi)始--樹(shù)的遍歷。本文使用了兩種極常用的方法來(lái)遍歷樹(shù)中的所有節(jié)點(diǎn)--遞歸;迭代,但它們實(shí)現(xiàn)的都是深度優(yōu)先(Depth-First)算法。

    1. 樹(shù)節(jié)點(diǎn)與數(shù)據(jù)
    先定義樹(shù)節(jié)點(diǎn)及數(shù)據(jù)(用戶對(duì)象),并創(chuàng)建測(cè)試用的數(shù)據(jù)。
    TreeNode是樹(shù)節(jié)點(diǎn)的定義。
    /**
     * 樹(shù)節(jié)點(diǎn)的定義。
     
    */
    public interface TreeNode {

        
    /**
         * 獲取指定下標(biāo)處的子節(jié)點(diǎn)。
         * 
         * 
    @param index
         *            下標(biāo)。
         * 
    @return 子節(jié)點(diǎn)。
         
    */
        
    public TreeNode getChildAt(int index);

        
    /**
         * 返回指定子節(jié)點(diǎn)的下標(biāo)。
         * 
         * 
    @param index
         *            下標(biāo)。
         * 
    @return 子節(jié)點(diǎn)。
         
    */
        
    public int getChildIndex(TreeNode index);

        
    /**
         * 獲取子節(jié)點(diǎn)的數(shù)量。
         * 
         * 
    @return 子節(jié)點(diǎn)的數(shù)量。
         
    */
        
    public int getChildCount();

        
    /**
         * 返回父節(jié)點(diǎn)。
         * 
         * 
    @return 父節(jié)點(diǎn)。
         
    */
        
    public TreeNode getParent();

        
    /**
         * 設(shè)置父節(jié)點(diǎn)。注:此處不需要改變父節(jié)點(diǎn)中的子節(jié)點(diǎn)元素。
         * 
         * 
    @param parent
         *            父節(jié)點(diǎn)。
         
    */
        
    public void setParent(TreeNode parent);

        
    /**
         * 獲取所有的子節(jié)點(diǎn)。
         * 
         * 
    @return 子節(jié)點(diǎn)的集合。
         
    */
        
    public List<?> getChildren();

        
    /**
         * 是否為葉節(jié)點(diǎn)。
         * 
         * 
    @return 是葉節(jié)點(diǎn),返回true;否則,返回false。
         
    */
        
    public boolean isLeaf();
    }

    GenericTreeNode是一個(gè)通用的樹(shù)節(jié)點(diǎn)實(shí)現(xiàn)。
    public class GenericTreeNode<T> implements TreeNode {

        
    private T userObject = null;

        
    private TreeNode parent = null;

        
    private List<GenericTreeNode<T>> children = new ArrayList<GenericTreeNode<T>>();

        
    public GenericTreeNode(T userObject) {
            
    this.userObject = userObject;
        }

        
    public GenericTreeNode() {
            
    this(null);
        }

        
    /**
         * 添加子節(jié)點(diǎn)。
         * 
         * 
    @param child
         
    */
        
    public void addChild(GenericTreeNode<T> child) {
            children.add(child);
            child.setParent(
    this);
        }

        
    /**
         * 刪除指定的子節(jié)點(diǎn)。
         * 
         * 
    @param child
         *            子節(jié)點(diǎn)。
         
    */
        
    public void removeChild(TreeNode child) {
            removeChildAt(getChildIndex(child));
        }

        
    /**
         * 刪除指定下標(biāo)處的子節(jié)點(diǎn)。
         * 
         * 
    @param index
         *            下標(biāo)。
         
    */
        
    public void removeChildAt(int index) {
            TreeNode child 
    = getChildAt(index);
            children.remove(index);
            child.setParent(
    null);
        }

        
    public TreeNode getChildAt(int index) {
            
    return children.get(index);
        }

        
    public int getChildCount() {
            
    return children.size();
        }

        
    public int getChildIndex(TreeNode child) {
            
    return children.indexOf(child);
        }

        
    public List<GenericTreeNode<T>> getChildren() {
            
    return Collections.unmodifiableList(children);
        }

        
    public void setParent(TreeNode parent) {
            
    this.parent = parent;
        }

        
    public TreeNode getParent() {
            
    return parent;
        }

        
    /**
         * 是否為根節(jié)點(diǎn)。
         * 
         * 
    @return 是根節(jié)點(diǎn),返回true;否則,返回false。
         
    */
        
    public boolean isRoot() {
            
    return getParent() == null;
        }

        
    public boolean isLeaf() {
            
    return getChildCount() == 0;
        }

        
    /**
         * 判斷指定的節(jié)點(diǎn)是否為當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。
         * 
         * 
    @param node
         *            節(jié)點(diǎn)。
         * 
    @return 是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),返回true;否則,返回false。
         
    */
        
    public boolean isChild(TreeNode node) {
            
    boolean result;
            
    if (node == null) {
                result 
    = false;
            } 
    else {
                
    if (getChildCount() == 0) {
                    result 
    = false;
                } 
    else {
                    result 
    = (node.getParent() == this);
                }
            }

            
    return result;
        }

        
    public T getUserObject() {
            
    return userObject;
        }

        
    public void setUserObject(T userObject) {
            
    this.userObject = userObject;
        }

        @Override
        
    public String toString() {
            
    return userObject == null ? "" : userObject.toString();
        }
    }

    UserObject是節(jié)點(diǎn)上的用戶對(duì)象,相當(dāng)于是數(shù)據(jù)。
    public class UserObject {

        
    private String name = null;

        
    private Integer value = Integer.valueOf(0);

        
    public UserObject() {

        }

        
    public UserObject(String code, Integer value) {
            
    this.name = code;
            
    this.value = value;
        }

        
    public String getName() {
            
    return name;
        }

        
    public void setName(String code) {
            
    this.name = code;
        }

        
    public Integer getValue() {
            
    return value;
        }

        
    public void setValue(Integer value) {
            
    this.value = value;
        }

        @Override
        
    public String toString() {
            StringBuilder result 
    = new StringBuilder();
            result.append(
    "[name=").append(name).append(", value=").append(value).append("]");
            
    return result.toString();
        }
    }

    TreeUtils是用于創(chuàng)建樹(shù)的工具類。
    public class TreeUtils {

        
    public static GenericTreeNode<UserObject> buildTree() {
            GenericTreeNode
    <UserObject> root = new GenericTreeNode<UserObject>();
            root.setUserObject(
    new UserObject("ROOT", Integer.valueOf(0)));

            GenericTreeNode
    <UserObject> node1 = new GenericTreeNode<UserObject>();
            node1.setUserObject(
    new UserObject("1", Integer.valueOf(0)));
            GenericTreeNode
    <UserObject> node2 = new GenericTreeNode<UserObject>();
            node2.setUserObject(
    new UserObject("2", Integer.valueOf(0)));
            GenericTreeNode
    <UserObject> node3 = new GenericTreeNode<UserObject>();
            node3.setUserObject(
    new UserObject("3", Integer.valueOf(5)));

            root.addChild(node1);
            root.addChild(node2);
            root.addChild(node3);

            GenericTreeNode
    <UserObject> node11 = new GenericTreeNode<UserObject>();
            node11.setUserObject(
    new UserObject("11", Integer.valueOf(0)));
            GenericTreeNode
    <UserObject> node21 = new GenericTreeNode<UserObject>();
            node21.setUserObject(
    new UserObject("21", Integer.valueOf(0)));

            node1.addChild(node11);
            node2.addChild(node21);

            GenericTreeNode
    <UserObject> node111 = new GenericTreeNode<UserObject>();
            node111.setUserObject(
    new UserObject("111", Integer.valueOf(3)));
            GenericTreeNode
    <UserObject> node112 = new GenericTreeNode<UserObject>();
            node112.setUserObject(
    new UserObject("112", Integer.valueOf(9)));
            GenericTreeNode
    <UserObject> node211 = new GenericTreeNode<UserObject>();
            node211.setUserObject(
    new UserObject("211", Integer.valueOf(6)));
            GenericTreeNode
    <UserObject> node212 = new GenericTreeNode<UserObject>();
            node212.setUserObject(
    new UserObject("212", Integer.valueOf(3)));

            node11.addChild(node111);
            node11.addChild(node112);
            node21.addChild(node211);
            node21.addChild(node212);

            
    return root;
        }
    }

    2. 遞歸法
    使用遞歸法的最大好處就是--簡(jiǎn)單,但一般地,我們都認(rèn)為遞歸的效率不高。
    private static void recursiveTravel(GenericTreeNode<UserObject> node) {
        travelNode(node); 
    // 訪問(wèn)節(jié)點(diǎn),僅僅只是打印該節(jié)點(diǎn)罷了。
        List<GenericTreeNode<UserObject>> children = node.getChildren();
        
    for (int i = 0; i < children.size(); i++) {
            recursiveTravel(children.get(i)); 
    // 遞歸地訪問(wèn)當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)。
        }
    }
    大家肯定知道,系統(tǒng)在執(zhí)行遞歸方法(對(duì)于其它方法也是如此)時(shí)是使用運(yùn)行時(shí)棧。對(duì)方法的每一次調(diào)用,在棧中都會(huì)創(chuàng)建一份此次調(diào)用的活動(dòng)記錄--包括方法的參數(shù),局部變量,返回地址,動(dòng)態(tài)鏈接庫(kù),返回值等。
    既然系統(tǒng)能夠隱式地使用棧去執(zhí)行遞歸方法,那么我們就可以顯式地使用棧來(lái)執(zhí)行上述遞歸程序,這也是將遞歸程序轉(zhuǎn)化為迭代程序的常用思想。下面的iterativeTravel方法就運(yùn)用了這一思想。

    3. 迭代法
    private static void iterativeTravel(GenericTreeNode<UserObject> node) {
        Stack
    <GenericTreeNode<UserObject>> nodes = new Stack<GenericTreeNode<UserObject>>();

        nodes.push(node); 
    // 將當(dāng)前節(jié)點(diǎn)壓入棧中。
        while (!nodes.isEmpty()) {
            GenericTreeNode
    <UserObject> bufNode = nodes.pop(); // 從棧中取出一個(gè)節(jié)點(diǎn)。
            travelNode(bufNode); // 訪問(wèn)節(jié)點(diǎn)。
            if (!bufNode.isLeaf()) { // 如果該節(jié)點(diǎn)為分枝節(jié)點(diǎn),則將它的子節(jié)點(diǎn)全部加入棧中。
                nodes.addAll(bufNode.getChildren());
            }
        }
    }
    與遞歸法相比,迭代法的代碼略多了幾行,但仍然很簡(jiǎn)單。

    4. 小結(jié)
    由于上述兩種方法均(隱式或顯式地)使用了運(yùn)行棧,所以此處的迭代法并不能提高整個(gè)程序的效率。相反地,由于在應(yīng)用程序中顯式地使用棧(java.util.Stack),iterativeTravel方法的效率可能反而更低。但iterativeTravel的最大好處是,能夠有效地避免運(yùn)行時(shí)棧溢出(java.lang.StackOverflowError)。
    如果樹(shù)的層次不太深,每層的子節(jié)點(diǎn)數(shù)不太多,那么使用遞歸法應(yīng)該是沒(méi)有問(wèn)題的。畢竟,簡(jiǎn)潔地程序會(huì)提供更多的好處。

    posted on 2009-04-01 20:40 John Jiang 閱讀(5301) 評(píng)論(4)  編輯  收藏 所屬分類: JavaAlgorithm原創(chuàng)

    評(píng)論

    # re: 樹(shù)的遍歷(原)[未登錄](méi) 2009-04-03 08:59 GreatGhoul
    很喜歡你的文章,學(xué)習(xí)這個(gè)迭代算法.  回復(fù)  更多評(píng)論
      

    # re: 樹(shù)的遍歷(原) 2009-04-03 21:37 Sha Jiang
    嘿嘿,謝謝!
    我也逛了一下你的blogspot  回復(fù)  更多評(píng)論
      

    # re: 樹(shù)的遍歷(原) 2010-07-27 22:19 Salazar
    看了你的迭代的方法,實(shí)現(xiàn)的應(yīng)該是廣度優(yōu)先,STACK-FIFO原理,拿二叉樹(shù)舉例,先訪問(wèn)根節(jié)點(diǎn)(將左右節(jié)點(diǎn)入棧),然后訪問(wèn)左節(jié)點(diǎn)(左節(jié)點(diǎn)的子節(jié)點(diǎn)入棧),然后是訪問(wèn)右節(jié)點(diǎn)(右節(jié)點(diǎn)的子節(jié)點(diǎn)入棧),接下來(lái)就是同上順序。  回復(fù)  更多評(píng)論
      

    # re: 樹(shù)的遍歷(原) 2010-08-26 20:11 Sha Jiang
    @Salazar
    對(duì)于深度優(yōu)先算法,正是使用棧;而對(duì)于廣度優(yōu)先算法,則應(yīng)該使用隊(duì)列。  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 久久爰www免费人成| gogo免费在线观看| 亚洲AV无码乱码在线观看代蜜桃 | 亚洲欧洲免费无码| 国产成人1024精品免费| 免费二级毛片免费完整视频| 亚洲国产精品va在线播放| 亚洲视频在线观看地址| 无码亚洲成a人在线观看| 国产羞羞的视频在线观看免费| 2020久久精品国产免费| 免费国产小视频在线观看| AV激情亚洲男人的天堂国语| 最近中文字幕无免费| 亚洲成av人片一区二区三区 | 亚洲?V无码乱码国产精品| 亚洲av无码国产综合专区| 在线观看特色大片免费视频| 亚洲亚洲人成综合网络| 亚洲日本在线电影| 国产又大又粗又硬又长免费| 久久综合亚洲鲁鲁五月天| 嘿嘿嘿视频免费网站在线观看 | 亚洲精华国产精华精华液好用| 无码国产精品一区二区免费16| 国产精品二区三区免费播放心| 91亚洲自偷手机在线观看| 国产va精品免费观看| 日韩毛片在线免费观看| h视频在线免费观看| 久久亚洲精品中文字幕| 另类图片亚洲校园小说区| 成年18网站免费视频网站| 亚洲天天做日日做天天欢毛片| 亚洲五月午夜免费在线视频| 亚洲AV成人片色在线观看| 在线看片免费人成视频播| 亚洲国产成人精品无码久久久久久综合| 国产裸体美女永久免费无遮挡| 国产免费无遮挡精品视频| 99久久免费国产精品热|