樹結構與Java實現

樹結構與Java實現html

目錄java

前言

提到『樹』這種數據結構,相信不少人首先想到的就是『二叉樹』。node

的確,二叉樹做爲一種重要的數據結構,它結合了數組和鏈表的優勢,有不少重要的應用。mysql

咱們都知道,數組的特色是查詢迅速,根據index能夠快速定位到一個元素。可是,若是要插入一個元素,就須要將這個元素位置以後的全部元素後移。平均來說,一個長度爲N的有序數組,插入元素要移動的元素個數爲N/2。有序數組的插入的時間複雜度爲O(N),刪除操做的時間複雜度也爲O(N)。git

對於插入和刪除操做頻繁的數據,不建議採用有序數組。github

鏈表的插入和刪除效率都很高,只要改變一些值的引用就好了,時間複雜度爲O(1)。可是鏈表的查詢效率很低,每次都要從頭開始找,依次訪問鏈表的每一個數據項。平均來講,要從一個有N個元素的鏈表查詢一個元素,要遍歷N/2個元素,時間複雜度爲O(N)redis

對於查找頻繁的數據,不建議使用鏈表。算法

本節先不介紹二叉樹,而是先講一下樹這種數據結構。相信有了本節的知識做爲基礎,再瞭解二叉樹就會輕鬆不少。spring

樹的概念

樹的概念

概述

在計算機科學中,樹(英語:tree)是一種抽象數據類型(ADT)或是實現這種抽象數據類型的數據結構,用來模擬具備樹狀結構性質的數據集合。sql

它是由n(n>0)個有限節點組成一個具備層次關係的集合。

把它叫作「樹」是由於它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。

它具備如下的特色:

每一個節點都只有有限個子節點或無子節點; 沒有父節點的節點稱爲根節點; 每個非根節點有且只有一個父節點; 除了根節點外,每一個子節點能夠分爲多個不相交的子樹; 樹裏面沒有環路(cycle) —— 維基百科

根據樹的定義,下面的結構就不是『樹』:

不是樹的結構

術語

樹的術語

  • 路徑

從某個節點依次到達另一個節點所通過的全部節點,就是這兩個節點之間的路徑。

樹頂端的節點被稱爲根。從根出發到達任意一個節點只有一條路徑。

  • 父節點

除了根節點以外,每一個節點均可以向上找到一個惟一的節點,這個節點就是當前節點的父節點。相應的,父節點下方的就是子節點。

  • 葉子節點

沒有子節點的「光桿司令」就被稱爲葉子節點。

  • 子樹

每一個子節點做爲根節點的樹都是一個子樹。

一個樹結構的代數就是這個樹的層。

一棵樹中,最大的節點的度稱爲樹的度。

  • 兄弟節點

具備相同父節點的節點互稱爲兄弟節點;

實際應用

樹結構有很是普遍的應用,好比咱們經常使用的文件目錄系統,就是一個樹結構。

例如在Windows10操做系統的CMD命令行輸入tree命令,就能夠輸出目錄樹:

tree
卷 Windows 的文件夾 PATH 列表
卷序列號爲 1CEB-7ABE
C:.
├─blog
│  ├─cache
│  │  └─JavaCacheGuidance
│  ├─datastructure
│  ├─editor
│  │  └─notepad++
│  ├─framework
│  │  └─guava
│  │      └─retry
│  ├─git
│  └─java
│      └─package-info
├─category
│  ├─food
│  │  ├─fruit
│  │  └─self
│  ├─job
│  │  └─bz
│  │      └─project
│  │          └─ad
│  │              └─exch
│  ├─people
│  ├─practical
│  │  └─work
│  │      └─ecommerce
│  │          └─inventory
│  ├─tech
│  │  ├─algorithm
│  │  │  └─tree
│  │  └─java
│  │      ├─concurrent
│  │      │  └─thread
│  │      ├─design
│  │      ├─i18n
│  │      ├─jcf
│  │      └─spring
│  │          └─springboot
│  └─tool
│      ├─data
│      │  └─db
│      │      ├─mysql
│      │      └─redis
│      └─site
│          └─stackoverflow
└─me
    └─phonephoto
複製代碼

實現樹

講解了樹結構的特色和相關概念之後,下面用Java實現樹結構的基本操做,並演示建立樹、添加子節點、遍歷樹和搜索指定節點等操做。

TreeNode

package net.ijiangtao.tech.algorithms.algorithmall.datastructure.tree;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/** * 實現樹結構 * * @author ijiangtao * @create 2019-04-18 15:13 **/
public class TreeNode<T> implements Iterable<TreeNode<T>> {

    /** * 樹節點 */
    public T data;

    /** * 父節點,根沒有父節點 */
    public TreeNode<T> parent;

    /** * 子節點,葉子節點沒有子節點 */
    public List<TreeNode<T>> children;

    /** * 保存了當前節點及其全部子節點,方便查詢 */
    private List<TreeNode<T>> elementsIndex;

    /** * 構造函數 * * @param data */
    public TreeNode(T data) {
        this.data = data;
        this.children = new LinkedList<TreeNode<T>>();
        this.elementsIndex = new LinkedList<TreeNode<T>>();
        this.elementsIndex.add(this);
    }

    /** * 判斷是否爲根:根沒有父節點 * * @return */
    public boolean isRoot() {
        return parent == null;
    }

    /** * 判斷是否爲葉子節點:子節點沒有子節點 * * @return */
    public boolean isLeaf() {
        return children.size() == 0;
    }

    /** * 添加一個子節點 * * @param child * @return */
    public TreeNode<T> addChild(T child) {
        TreeNode<T> childNode = new TreeNode<T>(child);

        childNode.parent = this;

        this.children.add(childNode);

        this.registerChildForSearch(childNode);

        return childNode;
    }

    /** * 獲取當前節點的層 * * @return */
    public int getLevel() {
        if (this.isRoot()) {
            return 0;
        } else {
            return parent.getLevel() + 1;
        }
    }

    /** * 遞歸爲當前節點以及當前節點的全部父節點增長新的節點 * * @param node */
    private void registerChildForSearch(TreeNode<T> node) {
        elementsIndex.add(node);
        if (parent != null) {
            parent.registerChildForSearch(node);
        }
    }

    /** * 從當前節點及其全部子節點中搜索某節點 * * @param cmp * @return */
    public TreeNode<T> findTreeNode(Comparable<T> cmp) {
        for (TreeNode<T> element : this.elementsIndex) {
            T elData = element.data;
            if (cmp.compareTo(elData) == 0)
                return element;
        }

        return null;
    }

    /** * 獲取當前節點的迭代器 * * @return */
    @Override
    public Iterator<TreeNode<T>> iterator() {
        TreeNodeIterator<T> iterator = new TreeNodeIterator<T>(this);
        return iterator;
    }

    @Override
    public String toString() {
        return data != null ? data.toString() : "[tree data null]";
    }

}
複製代碼

TreeNodeIterator

package net.ijiangtao.tech.algorithms.algorithmall.datastructure.tree;

import java.util.Iterator;

/** * * 迭代器 * * @author ijiangtao * @create 2019-04-18 15:24 **/
public class TreeNodeIterator<T> implements Iterator<TreeNode<T>> {

    enum ProcessStages {
        ProcessParent, ProcessChildCurNode, ProcessChildSubNode
    }

    private ProcessStages doNext;

    private TreeNode<T> next;

    private Iterator<TreeNode<T>> childrenCurNodeIter;

    private Iterator<TreeNode<T>> childrenSubNodeIter;

    private TreeNode<T> treeNode;

    public TreeNodeIterator(TreeNode<T> treeNode) {
        this.treeNode = treeNode;
        this.doNext = ProcessStages.ProcessParent;
        this.childrenCurNodeIter = treeNode.children.iterator();
    }

    @Override
    public boolean hasNext() {

        if (this.doNext == ProcessStages.ProcessParent) {
            this.next = this.treeNode;
            this.doNext = ProcessStages.ProcessChildCurNode;
            return true;
        }

        if (this.doNext == ProcessStages.ProcessChildCurNode) {
            if (childrenCurNodeIter.hasNext()) {
                TreeNode<T> childDirect = childrenCurNodeIter.next();
                childrenSubNodeIter = childDirect.iterator();
                this.doNext = ProcessStages.ProcessChildSubNode;
                return hasNext();
            } else {
                this.doNext = null;
                return false;
            }
        }

        if (this.doNext == ProcessStages.ProcessChildSubNode) {
            if (childrenSubNodeIter.hasNext()) {
                this.next = childrenSubNodeIter.next();
                return true;
            } else {
                this.next = null;
                this.doNext = ProcessStages.ProcessChildCurNode;
                return hasNext();
            }
        }

        return false;
    }

    @Override
    public TreeNode<T> next() {
        return this.next;
    }

    /** * 目前不支持刪除節點 */
    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }

}
複製代碼

測試

下面實現的樹結構,與前面圖中的樹結構徹底相同。

package net.ijiangtao.tech.algorithms.algorithmall.datastructure.tree;

/** * tree * * @author ijiangtao * @create 2019-04-18 15:03 **/
public class TreeDemo1 {

    public static void main(String[] args) {

        System.out.println("********************測試遍歷*************************");

        TreeNode<String> treeRoot = getSetA();
        for (TreeNode<String> node : treeRoot) {
            String indent = createIndent(node.getLevel());
            System.out.println(indent + node.data);
        }

        System.out.println("********************測試搜索*************************");

        Comparable<String> searchFCriteria = new Comparable<String>() {
            @Override
            public int compareTo(String treeData) {
                if (treeData == null)
                    return 1;
                boolean nodeOk = treeData.contains("F");
                return nodeOk ? 0 : 1;
            }
        };
        TreeNode<String> foundF = treeRoot.findTreeNode(searchFCriteria);
        System.out.println("F: parent=" + foundF.parent + ",children=" + foundF.children);

    }

    private static String createIndent(int depth) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            sb.append(' ');
        }
        return sb.toString();
    }

    public static TreeNode<String> getSetA() {

        TreeNode<String> A = new TreeNode<String>("A");
        {
            TreeNode<String> B = A.addChild("B");
            TreeNode<String> C = A.addChild("C");
            TreeNode<String> D = A.addChild("D");
            {
                TreeNode<String> E = B.addChild("E");
                TreeNode<String> F = C.addChild("F");
                TreeNode<String> G = C.addChild("G");
                {
                    TreeNode<String> H = F.addChild("H");
                    TreeNode<String> I = F.addChild("I");
                    TreeNode<String> J = F.addChild("J");
                }
            }
        }

        return A;
    }


}
複製代碼
  • 輸出
********************測試遍歷*************************
A
 B
  E
 C
  F
   H
   I
   J
  G
 D
********************測試搜索*************************
F: parent=C,children=[H, I, J]
複製代碼

總結

本節我帶領你們一塊兒瞭解了樹這種重要的數據結構,而且講解了樹相關的概念和術語,最後爲你們實現了基本的樹操做。

學習完本節內容,對咱們下面要介紹的二叉樹,以及Java中TreeSetTreeMap的源碼,都會有所幫助。

相關連接

做者資源

參考資源

相關文章
相關標籤/搜索