數據結構與算法--樹的三種存儲結構

數據結構與算法--樹的三種存儲結構

之前學的鏈表、隊列、棧,都是線性表,因為其中每個數據元素只有一個前驅和一個后繼。是一對一的關系。

假如是一對多的關系呢?這種數據結構就是今天要學的

樹的定義

樹是由有限個結點(假設為n)構成的集合。n = 0說明這是棵空樹。一棵樹中,有且只有一個根結點,按照習慣在位于樹的頂端。根結點可以理解為始祖一般的存在,他們有若干個孩子,但是他本身沒有雙親。如圖1中結點A就是根結點。

假設把樹從中截斷(并沒有),可以得到若干個互不相交(沒有交集)的集合,每一個集合本身又是一棵樹,稱為根的子樹,以后就直接叫“子樹”。比如假想我斷開了A-B, A-C的連接,結點B和C沒有雙親成為了根結點,產生了兩棵互不相交的子樹。如下。

什么叫互不相交呢?下面粗線連接部分如左圖D和E,他們到底屬于哪棵樹?可以認為構成子樹的集合之間有交集,造成了原來的子樹T1和子樹T2相交。這樣相交的樹,不叫子樹,因為這不符合樹的定義。

樹的結點與深度

上面的圖中每一個圓圈代表的就是一個結點,結點之間的連線表示了結點之間的關系。結點擁有的子樹數目稱為該結點的,也可以簡單理解為該結點擁有的孩子個數。如上面的圖中A結點的度為2。度為0的結點稱為葉子結點——也就是沒孩子。度不為0的結點稱為非葉子結點或非終端結點。樹的度為樹中各個結點度的最大值,如圖1中結點D擁有的孩子有3個,最多,所以樹的度就是3。

樹中各個結點之間有什么關系呢?某結點它的子樹的根稱做該節點的孩子(Child),該結點稱為這些孩子的雙親(Parent),或者直接叫父結點。同一個父結點的孩子之間互稱為兄弟(Sibling)。舉例來說,圖1中結點C的孩子結點有E和F,E和F的父結點為C,而E和F之間是兄弟關系。

樹的深度就是指樹的層數,根結點處為第一層,其孩子結點為第二層,以此類推。易知圖1樹的深度為4。

樹的存儲結構

父結點(雙親)表示法

這種結構的思想比較簡單:除了根結點沒有父結點外,其余每個結點都有一個唯一的父結點。將所有結點存到一個數組中。每個結點都有一個數據域data和一個數值parent指示其雙親在數組中存放的位置。根結點由于沒有父結點,parent用-1表示。

package Chap6;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TreeParent<Item> {

    public static class Node<T> {
        private T data;
        private int parent;

        public Node(T data, int parent) {
            this.data = data;
            this.parent = parent;
        }

        public T getData() {
            return data;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    ", parent=" + parent +
                    '}';
        }
    }

    // 樹的容量,能容納的最大結點數
    private int treeCapacity;
    // 樹的結點數目
    private int nodesNum;
    // 存放樹的所有結點
    private Node<Item>[] nodes;

    // 以指定樹大小初始化樹
    public TreeParent(int treeCapacity) {
        this.treeCapacity = treeCapacity;
        nodes = new Node[treeCapacity];

    }

    // 以默認的樹大小初始化樹
    public TreeParent() {
        treeCapacity = 128;
        nodes = new Node[treeCapacity];
    }

    public void setRoot(Item data) {
        // 根結點
        nodes[0] = new Node<>(data, -1);
        nodesNum++;
    }

    public void addChild(Item data, Node<Item> parent) {
        if (nodesNum < treeCapacity) {
            // 新的結點放入數組中第一個空閑位置
            nodes[nodesNum] = new Node<>(data, index(parent));
            nodesNum++;
        } else {
            throw new RuntimeException("樹已滿,無法再添加結點!");
        }
    }

    // 用nodeNum是因為其中無null,用treeCapacity里面很多null值根本無需比較
    private int index(Node<Item> parent) {
        for (int i = 0; i < nodesNum; i++) {
            if (nodes[i].equals(parent)) {
                return i;
            }
        }
        throw new RuntimeException("無此結點");
    }

    public void createTree(List<Item> datas, List<Integer> parents) {
        if (datas.size() > treeCapacity) {
            throw new RuntimeException("數據過多,超出樹的容量!");
        }

        setRoot(datas.get(0));
        for (int i = 1; i < datas.size(); i++) {
            addChild(datas.get(i), nodes[parents.get(i - 1)]);
        }
    }

    // 是否為空樹
    public boolean isEmpty() {
        return nodesNum == 0;
        // or return nodes[0] == null
    }

    public Node<Item> parentTo(Node<Item> node) {
        return nodes[node.parent];
    }

    // 結點的孩子結點
    public List<Node<Item>> childrenFromNode(Node<Item> parent) {
        List<Node<Item>> children = new ArrayList<>();
        for (int i = 0; i < nodesNum; i++) {
            if (nodes[i].parent == index(parent)) {
                children.add(nodes[i]);
            }
        }
        return children;
    }

    // 樹的度
    public int degreeForTree() {
        int max = 0;
        for (int i = 0; i < nodesNum; i++) {
            if (childrenFromNode(nodes[i]).size() > max) {
                max = childrenFromNode(nodes[i]).size();
            }
        }
        return max;
    }

    public int degreeForNode(Node<Item> node) {
        return childrenFromNode(node).size();
    }

    // 樹的深度
    public int depth() {
        int max = 0;
        for (int i = 0; i < nodesNum; i++) {
            int currentDepth = 1;
            int parent = nodes[i].parent;
            while (parent != -1) {
                // 向上繼續查找父結點,知道根結點
                parent = nodes[parent].parent;
                currentDepth++;
            }
            if (currentDepth > max) {
                max = currentDepth;
            }
        }
        return max;
    }


    // 樹的結點數
    public int nodesNum() {
        return nodesNum;
    }

    // 返回根結點
    public Node<Item> root() {
        return nodes[0];
    }

    // 讓樹為空
    public void clear() {
        for (int i = 0; i < nodesNum; i++) {
            nodes[i] = null;
            nodesNum = 0;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Tree{\n");
        for (int i = 0; i < nodesNum - 1; i++) {
            sb.append(nodes[i]).append(", \n");
        }
        sb.append(nodes[nodesNum - 1]).append("}");
        return sb.toString();
    }

    public static void main(String[] args) {
        // 按照以下定義,生成樹
        List<String> datas = new ArrayList<>(Arrays.asList("Bob", "Tom", "Jerry", "Rose", "Jack"));
        List<Integer> parents = new ArrayList<>(Arrays.asList(0, 0, 1, 2));

        TreeParent<String> tree = new TreeParent<>();
        tree.createTree(datas, parents);
        TreeParent.Node<String> root = tree.root();
        // root的第一個孩子
        TreeParent.Node<String> aChild = tree.childrenFromNode(root).get(0);
        System.out.println(aChild.getData() + "的父結點是" + tree.parentTo(aChild).getData());
        System.out.println("根結點的孩子" + tree.childrenFromNode(root));
        System.out.println("該樹深度為" + tree.depth());
        System.out.println("該樹的度為" + tree.degreeForTree());
        System.out.println("該樹的結點數為" + tree.nodesNum());
        System.out.println(tree);


    }
}

/* Outputs

Tom的父結點是Bob
根結點的孩子[Node{data=Tom, parent=0}, Node{data=Jerry, parent=0}]
該樹深度為3
該樹的度為2
該樹的結點數為5
Tree
{Node{data=Bob, parent=-1}, 
Node{data=Tom, parent=0}, 
Node{data=Jerry, parent=0}, 
Node{data=Rose, parent=1}, 
Node{data=Jack, parent=2}}
*/

setRoot方法必須首先被調用,可以看到根結點始終被放置在數組中第一個位置(下標為0),之后才能調用addChild方法。createTree將創建樹的過程簡化了,我們只需輸入一組數據datas,和這組數據對應的parents傳給createTree就行,注意datas的第一個數據是根結點信息,在代碼中默認使用-1表示其parent,所以它在parents中沒有對應的parent值,也就是說datas的第二個值才和parents的第一個值對應,以此類推。樹創建完成后,若想再添加結點到樹,調用addChild就行。

childrenFromNode方法獲取某個結點的所有孩子結點,由代碼看出它需要遍歷所有結點,復雜度為O(n)。parentTo方法獲取某結點的父結點,復雜度O(1)。

另外求樹的度的時候,也是遍歷了所有結點,從中選出最大的度作為樹的度,復雜度為O(n)。求樹的深度也類似,遍歷了所有結點,從下往上,一直追溯到根結點,用currentDepth記錄了當前結點的深度,從所有結點中選擇最大深度值作為樹的深度。

孩子表示法

換種思路,既然雙親表示法獲取某結點的所有孩子有點麻煩,我們索性讓每個結點記住他所有的孩子。但是由于一個結點擁有的孩子個數是一個不確定的值,雖然最多只有樹的度那么多,但是大多數結點的孩子個數并沒有那么多,如果用數組來存放所有孩子,對于大多數結點來說太浪費空間了。自然我們容易想到用一個可變容量的表來存,選用Java內置的LinkedList是個不錯的選擇。先用一個數組存放所有的結點信息,該鏈表只需存儲結點在數組中的下標就行了。

package Chap6;


import java.util.*;

public class TreeChildren<Item> {

    public static class Node<T> {
        private T data;
        private List<Integer> children;

        public Node(T data) {
            this.data = data;
            this.children = new LinkedList<>();
        }

        public Node(T data, int[] children) {
            this.data = data;
            this.children = new LinkedList<>();
            for (int child : children) {
                this.children.add(child);
            }
        }

        public T getData() {
            return data;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    ", children=" + children +
                    '}';
        }
    }

    // 樹的容量,能容納的最大結點數
    private int treeCapacity;
    // 樹的結點數目
    private int nodesNum;
    // 存放樹的所有結點
    private Node<Item>[] nodes;

    public TreeChildren(int treeCapacity) {
        this.treeCapacity = treeCapacity;
        nodes = new Node[treeCapacity];
    }

    public TreeChildren() {
        treeCapacity = 128;
        nodes = new Node[treeCapacity];
    }

    public void setRoot(Item data) {
        nodes[0].data = data;
        nodesNum++;
    }


    public void addChild(Item data, Node<Item> parent) {
        if (nodesNum < treeCapacity) {
            // 新的結點放入數組中第一個空閑位置
            nodes[nodesNum] = new Node<>(data);
            // 父結點添加其孩子
            parent.children.add(nodesNum);
            nodesNum++;
        } else {
            throw new RuntimeException("樹已滿,無法再添加結點!");
        }
    }

    public void createTree(Item[] datas, int[][] children) {
        if (datas.length > treeCapacity) {
            throw new RuntimeException("數據過多,超出樹的容量!");
        }

        for (int i = 0; i < datas.length; i++) {
            nodes[i] = new Node<>(datas[i], children[i]);
        }

        nodesNum = datas.length;
    }

    // 根據給定的結點查找再數組中的位置
    private int index(Node<Item> node) {
        for (int i = 0; i < nodesNum; i++) {
            if (nodes[i].equals(node)) {
                return i;
            }
        }
        throw new RuntimeException("無此結點");
    }

    public List<Node<Item>> childrenFromNode(Node<Item> node) {
        List<Node<Item>> children = new ArrayList<>();
        for (Integer i : node.children) {
            children.add(nodes[i]);
        }
        return children;
    }

    public Node<Item> parentTo(Node<Item> node) {
        for (int i = 0; i < nodesNum; i++) {
            if (nodes[i].children.contains(index(node))) {
                return nodes[i];
            }
        }
        return null;
    }

    // 是否為空樹
    public boolean isEmpty() {
        return nodesNum == 0;
        // or return nodes[0] == null
    }

    // 樹的深度
    public int depth() {
        return nodeDepth(root());
    }

    // 求以node為根結點的子樹的深度
    public int nodeDepth(Node<Item> node) {
        if (node == null) {
            return 0;
        }
        // max是某個結點所有孩子中的最大深度
        int max = 0;
        // 即使沒有孩子,返回1也是正確的
        if (node.children.size() > 0) {
            for (int i : node.children) {
                int depth = nodeDepth(nodes[i]);
                if (depth > max) {
                    max = depth;
                }
            }
        }
        // 這里需要+1因為depth -> max是當前結點的孩子的深度, +1才是當前結點的深度
        return max + 1;
    }

    public int degree() {
        int max = 0;
        for (int i = 0; i < nodesNum; i++) {
            if (nodes[i].children.size() > max) {
                max = nodes[i].children.size();
            }
        }
        return max;
    }

    public int degreeForNode(Node<Item> node) {
        return childrenFromNode(node).size();
    }

    public Node<Item> root() {
        return nodes[0];
    }

    // 樹的結點數
    public int nodesNum() {
        return nodesNum;
    }

    // 讓樹為空
    public void clear() {
        for (int i = 0; i < nodesNum; i++) {
            nodes[i] = null;
            nodesNum = 0;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Tree{\n");
        for (int i = 0; i < nodesNum - 1; i++) {
            sb.append(nodes[i]).append(", \n");
        }
        sb.append(nodes[nodesNum - 1]).append("}");
        return sb.toString();
    }

    public static void main(String[] args) {
        String[] datas = {"Bob", "Tom", "Jerry", "Rose", "Jack"};
        int[][] children = {{1, 2}, {3}, {4}, {}, {}};
        TreeChildren<String> tree = new TreeChildren<>();
        tree.createTree(datas, children);

        TreeChildren.Node<String> root = tree.root();
        TreeChildren.Node<String> rightChild = tree.childrenFromNode(root).get(1);
        System.out.println(rightChild.getData() + "的度為" + tree.degreeForNode(rightChild));
        System.out.println("該樹的結點數為" + tree.nodesNum());
        System.out.println("該樹根結點" + tree.root());
        System.out.println("該樹的深度為" + tree.depth());
        System.out.println("該樹的度為" + tree.degree());
        System.out.println(tree.parentTo(rightChild));

        tree.addChild("Joe", root);
        System.out.println("該樹的度為" + tree.degree());
        System.out.println(tree);

    }
}

/* Outputs

Jerry的度為1
該樹的結點數為5
該樹根結點Node{data=Bob, children=[1, 2]}
該樹的深度為3
該樹的度為2
Node{data=Bob, children=[1, 2]}
該樹的度為3
Tree{
Node{data=Bob, children=[1, 2, 5]}, 
Node{data=Tom, children=[3]}, 
Node{data=Jerry, children=[4]}, 
Node{data=Rose, children=[]}, 
Node{data=Jack, children=[]}, 
Node{data=Joe, children=[]}}
*/

有些方法的實現和雙親表示法一樣,有些方法的實現改變了。

createTree方法中可以接受一個二維int數組,存放著每個結點的children,傳參的時候注意一點,如果某個結點沒有孩子,那么也應該填入空值。就像下面這樣,否則會引發空指針。

int[][] children = {{1, 2}, {3}, {4}, {}, {}};

addChild參數列表沒變,實現變為新添加的結點在數組中的下標(其實就是數組的第一個空閑位置)add進父結點的孩子鏈表中。createTree可以按照定義一次性生成樹,只需傳入結點信息的表和對應的孩子鏈表就行,復雜度也是O(n)。childrenFromNode獲得某個結點的所有孩子,這就比雙親表示法好點了,它沒有遍歷所有結點也無需進行if判斷,而僅僅將該結點的孩子鏈表中的內容(整型值)轉換成Node對象返回而已。不過該實現要獲取某個結點的父結點就沒有雙親法好了,孩子表示法必須遍歷所有結點,復雜度為O(n)。獲取父結點方法中,遍歷所有結點,如果有某個結點的孩子鏈表中包含了所求結點,則返回該結點。

求樹的深度方法也變成了遞歸實現,在雙親法實現中由于存在parent域,所以從下至上查找比較方便;而在孩子表示法中,獲取孩子結點比較方便,所以從根結點開始從上至下查找,這里使用到了遞歸的思想。由于返回的是max + 1(為什么是這個值后面會解釋),所以需要對樹空的情況進行正確的處理。若是葉子結點,循環不會執行,應該返回max + 1 = 1,正確;其他情況,該結點有孩子,進入循環開始遞歸,遞歸直到遇到葉子結點停止,開始返回,葉子結點返回1。回到父結點的nodeDepth函數,max被賦值為1?,F在說說這個max到底是什么意思,代碼中for (int i: node.children),遍歷當前結點的所有孩子,它們共享同一個max,所以max的意義就是某結點所有孩子結點的深度的最大值。于是max + 1就是當前結點的深度。接著說,函數一直返回,每次返回實際就是往上一層,到所求結點的孩子結點處,其孩子結點中的最大深度賦值給max,那么最后返回的max + 1就是所求結點作為根結點時的子樹深度。

孩子表示法的優化

再說獲取某結點父結點的方法,從代碼看出它遍歷了所有結點。如果要改進,可以將雙親表示法融合進去,增加一個parent域就行。也就是說,Node類改成如下就行,這種實現可以稱為雙親孩子表示法。

public static class Node<T> {
    private int parent;
    private T data;
    private List<Integer> children;
}

這樣獲取父結點的復雜度就變成了O(1),就懶得實現了,稍微改改代碼就好了。

孩子兄弟表示法

還有一種表示法,關注某結點的孩子結點之間的關系,他們互為兄弟。一個結點可能有孩子,也有可能有兄弟,也可能兩者都有,或者兩者都沒?;谶@種思想,可以用具有兩個指針域(一個指向當前結點的孩子,一個指向其兄弟)的鏈表實現,這種鏈表又稱為二叉鏈表。特別注意的是,雙親表示法和孩子結點表示法,都使用了數組存放每一個結點的信息,若稍加分析,使用數組是有必要的。但在這種結構中,我們摒棄了數組,根結點可以作為頭指針,以此開始可以遍歷到樹的全部結點——根結點肯定是沒有兄弟的(根結點如果有兄弟這棵樹就有兩個根結點了),如果它沒有孩子,則這棵樹只有根結點;若有孩子,就如下圖,它的nextChild的指針域就不為空,現在看這個左孩子,有兄弟(實際就是根結點的第二個孩子)還有孩子,則左孩子的兩個指針域都不為空,再看這個左孩子的nextSib,他有個孩子...一直這樣下去,對吧,能夠訪問到樹的全部結點的。

整個結構就是一條有兩個走向的錯綜復雜的鏈表,垂直走向是深入到結點的子子孫孫;水平走向就是查找它的兄弟姐妹。這種結構也能直觀反映樹的結構的,上圖其實就是下面這棵樹。

說了這么多,反正把它當鏈表就行了,就是多了一個指針域而已。(和雙向鏈表區別開,雙向鏈表是a.next =b,必然有b.prev = a;但是這里二叉鏈表卻沒有這個限制,它指向任意一個結點都可以)。

好了現在來實現吧!

package Chap6;

import java.util.ArrayList;
import java.util.List;

public class TreeChildSib<Item> {

    public static class Node<T> {
        private T data;
        private Node<T> nextChild;
        private Node<T> nextSib;

        public T getData() {
            return data;
        }

        public Node(T data) {
            this.data = data;
        }

        public Node<T> getNextChild() {
            return nextChild;
        }

        public Node<T> getNextSib() {
            return nextSib;
        }

        @Override
        public String toString() {
            String child = nextChild == null ? null : nextChild.getData().toString();
            String sib = nextSib == null ? null : nextSib.getData().toString();

            return "Node{" +
                    "data=" + data +
                    ", nextChild=" + child +
                    ", nextSib=" + sib +
                    '}';
        }
    }

    private Node<Item> root;
    // 存放所有結點,每次新增一個結點就add進來
    private List<Node<Item>> nodes = new ArrayList<>();

    // 以指定的根結點初始化樹
    public TreeChildSib(Item data) {
        setRoot(data);
    }

    // 空參數構造器
    public TreeChildSib() {

    }

    public void setRoot(Item data) {
        root = new Node<>(data);
        nodes.add(root);
    }

    public void addChild(Item data, Node<Item> parent) {
        Node<Item> node = new Node<>(data);
        // 如果該parent是葉子結點,沒有孩子
        if (parent.nextChild == null) {
            parent.nextChild = node;
            // parent有孩子了,只能放在n其第一個孩子的最后一個兄弟之后
        } else {
            // 從parent的第一個孩子開始,追溯到最后一個兄弟
            Node<Item> current = parent.nextChild;
            while (current.nextSib != null) {
                current = current.nextSib;
            }
            current.nextSib = node;
        }
        nodes.add(node);
    }

    public List<Node<Item>> childrenFromNode(Node<Item> node) {
        List<Node<Item>> children = new ArrayList<>();
        for (Node<Item> cur = node.nextChild; cur != null; cur = cur.nextSib) {
            {
                children.add(cur);
            }
        }
        return children;
    }

    public Node<Item> parentTo(Node<Item> node) {
        for (Node<Item> eachNode : nodes) {
            if (childrenFromNode(eachNode).contains(node)) {
                return eachNode;
            }
        }
        return null;
    }

    public boolean isEmpty() {
        return nodes.size() == 0;
    }

    public Node<Item> root() {
        return root;
    }

    public int nodesNum() {
        return nodes.size();
    }

    public int depth() {
        return nodeDepth(root);
    }

    public int nodeDepth(Node<Item> node) {
        if (node == null) {
            return 0;
        }

        int max = 0;
        if (childrenFromNode(node).size() > 0) {
            for (Node<Item> child : childrenFromNode(node)) {
                int depth = nodeDepth(child);
                if (depth > max) {
                    max = depth;
                }
            }
        }
        return max + 1;
    }

    public int degree() {

        int max = 0;
        for (Node<Item> node : nodes) {
            if (childrenFromNode(node).size() > max) {
                max = childrenFromNode(node).size();
            }
        }
        return max;
    }

    public int degreeForNode(Node<Item> node) {
        return childrenFromNode(node).size();
    }

    public void deleteNode(Node<Item> node) {
        if (node == null) {
            return;
        }
        deleteNode(node.nextChild);
        deleteNode(node.nextSib);
        node.nextChild = null;
        node.nextSib = null;
        node.data = null;
        nodes.remove(node);
    }

    public void clear() {
        deleteNode(root);
        root = null;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Tree{\n");
        for (int i = 0; i < nodesNum() - 1; i++) {
            sb.append(nodes.get(i)).append(", \n");
        }
        sb.append(nodes.get(nodesNum() - 1)).append("}");
        return sb.toString();
    }

    public static void main(String[] args) {
        TreeChildSib<String> tree = new TreeChildSib<>("A");
        TreeChildSib.Node<String> root = tree.root();
        tree.addChild("B", root);
        tree.addChild("C", root);
        tree.addChild("D", root);
        TreeChildSib.Node<String> child1 = tree.childrenFromNode(root).get(0);
        TreeChildSib.Node<String> child2 = tree.childrenFromNode(root).get(1);
        TreeChildSib.Node<String> child3 = tree.childrenFromNode(root).get(2);
        tree.addChild("E", child1);
        tree.addChild("F", child2);
        tree.addChild("G", child1);
        tree.addChild("H", child3);

        System.out.println(tree);
        System.out.println("該樹結點數為" + tree.nodesNum());
        System.out.println("該樹深度為" + tree.depth());
        System.out.println("該樹的度為" + tree.degree());
        System.out.println(child1.getData() + "的度為" + tree.degreeForNode(child1));
        System.out.println(child2.getData() + "的父結點為" + tree.parentTo(child2).getData());

        tree.clear();
        System.out.println(child1);
        System.out.println(tree.isEmpty());
    }
}

/* Outputs
Tree{
Node{data=A, nextChild=B, nextSib=null}, 
Node{data=B, nextChild=E, nextSib=C}, 
Node{data=C, nextChild=F, nextSib=D}, 
Node{data=D, nextChild=H, nextSib=null}, 
Node{data=E, nextChild=null, nextSib=G}, 
Node{data=F, nextChild=null, nextSib=null}, 
Node{data=G, nextChild=null, nextSib=null}, 
Node{data=H, nextChild=null, nextSib=null}}
該樹結點數為8
該樹深度為3
該樹的度為3
B的度為2
C的父結點為A
Node{data=null, nextChild=null, nextSib=null}
true
*/

由于有的方法需要遍歷樹的所有結點,所以自建一個表List<Node<Item>> nodes來存放,具體來說就是每次添加結點的同時將這個結點加入到該表中。

addChild方法很重要,如果需要依附的父結點還沒有孩子(if分支),那需要添加的結點成為它的第一個孩子;如果父結點有孩子了(else分支),那么就從父結點的第一個孩子開始,一直到它最后一個兄弟之后,新添加的結點位于此處。

獲取某結點的所有孩子childrenFromNode方法,就是從所求結點的第一個孩子開始,不斷找到其兄弟,第一個孩子與其所有兄弟全部就是所求結點的所有孩子。

求度,求深度的算法和孩子表示法差不多,就不再贅述。來看clear清空樹的方法,需要把每個結點得信息都置為空,就必須有個方法能遍歷這棵樹,這里用了后序遍歷的方法,這之后還沒完,存放結點的表也應該清空才是。

若是想讓獲取父結點變得方便些,也可以多設置一個parent域,見孩子表示法的優化。

孩子兄弟表示法有一個優點,可以將一棵普通樹轉化成二叉樹,由于二叉樹有諸多特征,使得處理起來變得簡單。孩子兄弟表示法上面的那個鏈表,稍微拉伸下改變下結構,就能變成一棵二叉樹,如下。


by @sunhaiyu

2017.9.8

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1.樹(Tree): 樹是 n(n>=0) 個結點的有限集。當 n=0 時稱為空樹。在任意一顆非空樹中:有且僅有一...
    ql2012jz閱讀 1,054評論 0 3
  • 前面講到的順序表、棧和隊列都是一對一的線性結構,這節講一對多的線性結構——樹?!敢粚Χ唷咕褪侵敢粋€元素只能有一個前...
    Alent閱讀 2,286評論 1 28
  • B樹的定義 一棵m階的B樹滿足下列條件: 樹中每個結點至多有m個孩子。 除根結點和葉子結點外,其它每個結點至少有m...
    文檔隨手記閱讀 13,355評論 0 25
  • 數據結構和算法--二叉樹的實現 幾種二叉樹 1、二叉樹 和普通的樹相比,二叉樹有如下特點: 每個結點最多只有兩棵子...
    sunhaiyu閱讀 6,532評論 0 14
  • 1.樹的定義 樹是n(n>=0)個結點的有限集.n=0時稱為空樹.在任意一顆非空樹種:(1)有且僅有一個特定的稱為...
    e40c669177be閱讀 2,904評論 1 14