Java 數(shù)據(jù)結(jié)構(gòu):樹(shù)

目錄:
一、樹(shù)
1. 概述
2. 一些基本術(shù)語(yǔ)

二乱凿、二叉樹(shù)
1. 概述
2. 重要特性

三、二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)
1. 順序存儲(chǔ)
2. 鏈?zhǔn)酱鎯?chǔ)

四、二叉樹(shù)的遍歷
1. 由遍歷序列確定二叉樹(shù)
2. 根據(jù)遍歷序列估計(jì)二叉樹(shù)
3. 遍歷和建樹(shù)代碼


一绑蔫、樹(shù)

1. 概述
  • 與線性表表示的一一對(duì)應(yīng)的線性關(guān)系不同,樹(shù)表示的是更為復(fù)雜的數(shù)據(jù)元素之間的非線性關(guān)系泵额。

  • 直觀來(lái)看配深,樹(shù)是以分支關(guān)系定義的層次結(jié)構(gòu),是 一對(duì)多 的關(guān)系

  • 樹(shù)的定義:樹(shù) (Tree) 是 n( n>=0 ) 個(gè)結(jié)點(diǎn)的有限集合

  • 有且僅有一個(gè) 根結(jié)點(diǎn) (Root)

  • 當(dāng)n>1的時(shí)嫁盲,其余結(jié)點(diǎn)可分為 m( m>0 ) 個(gè)互不相交的有限集T1篓叶,T2,..., Tm,其中每一個(gè)集合本身又是一棵樹(shù)澜共,并且稱之為根的子樹(shù)向叉。

樹(shù)的定義本身是一個(gè)遞歸定義,即在樹(shù)的定義中又用到樹(shù)的概念

2. 一些基本術(shù)語(yǔ)
  • 樹(shù)的結(jié)點(diǎn):包含一個(gè)數(shù)據(jù)元素和若干指向其子樹(shù)的分支
  • (degree):結(jié)點(diǎn)擁有的子樹(shù)的數(shù)目
  • 度為** 0 **的結(jié)點(diǎn)稱為 葉子嗦董,或 終端結(jié)點(diǎn)母谎。度不為 0 的結(jié)點(diǎn)稱為 非終端結(jié)點(diǎn)分支節(jié)點(diǎn)
  • 結(jié)點(diǎn)的子樹(shù)的根,稱為該結(jié)點(diǎn)的 孩子(child)京革,相應(yīng)的該結(jié)點(diǎn)稱為孩子的 雙親(parent)
  • 同一個(gè)雙親的孩子之間互稱兄弟(sibling)
  • 結(jié)點(diǎn)的 祖先 是從根到該結(jié)點(diǎn)所經(jīng)分支上所有的結(jié)點(diǎn)奇唤。
  • 以某結(jié)點(diǎn)為根的子樹(shù)中的任意一結(jié)點(diǎn)都稱為該結(jié)點(diǎn)的 子孫
  • 結(jié)點(diǎn)的 層次(Level)從根開(kāi)始定義,根為第一層匹摇,根的孩子為第二層廊勃,以此類(lèi)推
  • 樹(shù)中結(jié)點(diǎn)的最大層次稱為樹(shù)的 深度(depth)或 高度

二、二叉樹(shù)

1. 概述
  • 定義:對(duì)一般的樹(shù)加了約束:

    • 每個(gè)結(jié)點(diǎn)最多兩棵子樹(shù),即二叉樹(shù)中不存在 度大于2 的結(jié)點(diǎn)
    • 子樹(shù)有 左右次序 之分
  • 有 5 種形態(tài)

    二叉樹(shù)的形態(tài)

  • **滿二叉樹(shù) **和 完全二叉樹(shù)(對(duì)滿二叉樹(shù)最底層伏尼,從右至左刪除結(jié)點(diǎn))
2. 重要特性
  • 二叉樹(shù),在第 i 層至多有 2i-1 個(gè)結(jié)點(diǎn)
  • 深度為 k 的二叉樹(shù)至多有 **2k-1 **個(gè)結(jié)點(diǎn)
  • 高度(或深度)為 K完全二叉樹(shù)至少有 2k-2 個(gè) 葉子結(jié)點(diǎn)
  • 非空二叉樹(shù)的 葉子結(jié)點(diǎn)數(shù) 等于度為 2 的結(jié)點(diǎn)數(shù)加 1芭碍,即:n0 = n2 + 1

完全二叉樹(shù)的 n1 只能是 0 或者 1

  • 一顆度為 m 的二叉樹(shù)杉女,度為 1 的結(jié)點(diǎn)為 n1晌砾,度為 2 的結(jié)點(diǎn)為 n2,... ...,度為 m 的結(jié)點(diǎn)數(shù)為 nm,則葉子結(jié)點(diǎn)數(shù):n0 = 1 + n2 + 2n3 +...+ (m-1)nm

  • 具有 n 個(gè)結(jié)點(diǎn)的完全二叉樹(shù),深度為 log2n + 1

  • 編號(hào)性質(zhì):n 個(gè)結(jié)點(diǎn)的完全二叉樹(shù)(其深度為 log2n + 1)漱病,對(duì)各結(jié)點(diǎn)從上到下漓穿,從左到右依次編號(hào)(1~n)則:若 i 是某結(jié)點(diǎn) a 的編號(hào):

  • 如果 i 不等于 1,則 a 的雙親結(jié)點(diǎn)的編號(hào)為: **? i/2 ? **

  • 如果 2i ≤ n, 則 a 的左孩子編號(hào)為 2i;如果** 2i > n, 則 a 無(wú)左孩子**;

  • 如果** 2i + 1 ≤ n, 則 a 的右孩子編號(hào)為 2i + 1;如果 2i + 1> n**, 則 a 無(wú)右孩子画切;


三丧枪、二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)

1. 順序存儲(chǔ)
  • 數(shù)組 來(lái)存儲(chǔ)數(shù)據(jù)元素
  • 從存儲(chǔ)的角度來(lái)看,這種順序存儲(chǔ)結(jié)構(gòu)庞萍,僅適用于 完全二叉樹(shù)

因?yàn)樵谧顗牡那闆r下拧烦,一個(gè)深度為 k 且只有 k 個(gè)結(jié)點(diǎn)的單支樹(shù)( 樹(shù)中不存在度為 2 的結(jié)點(diǎn) ),卻需要長(zhǎng)度為 2k-1 的一維數(shù)組钝计。

2. 鏈?zhǔn)酱鎯?chǔ)
  • 鏈表 的形式恋博,存儲(chǔ)數(shù)據(jù)元素以及數(shù)據(jù)元素之間的關(guān)系。
鏈?zhǔn)酱鎯?chǔ)

四私恬、二叉樹(shù)的遍歷

1. 由遍歷序列確定二叉樹(shù)
  • 先序和中序债沮,可以確定
  • 后序和中序,可以確定(但是注意后序最后一個(gè)為根本鸣,下一個(gè)是右子樹(shù)根
  • 層次和中序疫衩,可以確定
2. 根據(jù)遍歷序列估計(jì)二叉樹(shù)
  • 前序 遍歷序列 和 后序 遍歷序列 相同 的樹(shù):只有根結(jié)點(diǎn)

  • 前序 遍歷 和 中序 遍歷 相同 的二叉樹(shù):所有結(jié)點(diǎn)沒(méi)有左子樹(shù)(右單分支樹(shù)

  • 中序 遍歷 和 后序 遍歷 相同 的二叉樹(shù):所有結(jié)點(diǎn)沒(méi)有右子樹(shù)(左單分支樹(shù))

  • 前序遍歷 和 后序 遍歷 相反 的二叉樹(shù):沒(méi)有左子樹(shù)或者沒(méi)有右子樹(shù)(只有一個(gè)葉子結(jié)點(diǎn))<u>**高度等于其結(jié)點(diǎn)數(shù) **</u>

  • 前序 遍歷 和 中序 遍歷 相反 的二叉樹(shù):所有結(jié)點(diǎn)沒(méi)有右子樹(shù)(左單分支樹(shù))

  • 中序 遍歷 和 后序 遍歷 相反 的二叉樹(shù):所有結(jié)點(diǎn)沒(méi)有左子樹(shù)(右單分支樹(shù))

3. 遍歷和建樹(shù)代碼
  • 二叉樹(shù)的建樹(shù)
  • 深度優(yōu)先遍歷(先序,中序和后序)
  • 廣度優(yōu)先遍歷(先序荣德,后序)
/* BitTree.java */

package com.java.tree;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Created by Jaco.Young.
 * 2018-06-13 18:26
 */
public class BitTree {

    //代表由先序和中序唯一確定的樹(shù)的根結(jié)點(diǎn)
    private TreeNode root;

    /**
     * 提供給外部調(diào)用的方法
     * 字符數(shù)組pre表示先序遍歷序列闷煤,mid表示中序遍歷序列
     */
    public void build(char[] pre, char[] mid){
        //將創(chuàng)建樹(shù)的根結(jié)點(diǎn)賦值給 root
        root = buildTree(pre,0, pre.length-1, mid, 0, mid.length-1);
    }

    /**
     * 前提條件,樹(shù)中不存在重復(fù)元素
     * 由先序遍歷序列和中序遍歷序列涮瞻,構(gòu)造二叉樹(shù)的方法
     * 我們建樹(shù)的過(guò)程總是將序列不斷地分割成左子樹(shù)鲤拿、右子樹(shù)
     * lPre、rPre和lMid署咽、rMid近顷,分別就表示要對(duì)先序和中序的哪一部分進(jìn)行建樹(shù)
     */
    private TreeNode buildTree(char[] pre, int lPre, int rPre, char[] mid, int lMid, int rMid){
        //在先序遍歷序列中,找到當(dāng)前這棵樹(shù)的根結(jié)點(diǎn)
        char root = pre[lPre];

        //在中序遍歷序列中宁否,根據(jù)先序中的根結(jié)點(diǎn)來(lái)查找在中序中的位置
        int rootIndex = getRootIndex(mid, lMid, rMid, root);

        //如果沒(méi)有找到窒升,說(shuō)明所給的參數(shù)異常
        if(rootIndex == -1){
            throw new IllegalArgumentException("Illegal Argument!");
        }

        //計(jì)算當(dāng)前這棵樹(shù),左右子樹(shù)的個(gè)數(shù)
        //整個(gè)中序序列:[左子樹(shù)(lMid)  root(rootIndex)  右子樹(shù)(rMid)]
        //左子樹(shù)[lMid,rootIndex-1]
        int lNum = rootIndex - lMid; //rootIndex-1 -lMid + 1
        //右子樹(shù)[rootIndex+1,rMid]
        int rNum = rMid - rootIndex;  //rMid - (rootIndex + 1) + 1

        //開(kāi)始構(gòu)建當(dāng)前根結(jié)點(diǎn)的左子樹(shù)和右子樹(shù)
        //先構(gòu)建左子樹(shù)
        TreeNode lchild;  //作為左子樹(shù)的根結(jié)點(diǎn)
        //以當(dāng)前結(jié)點(diǎn)為根的樹(shù)慕匠,沒(méi)有左子樹(shù)
        if(lNum == 0){
            lchild = null;
        }else{
            //當(dāng)前這個(gè)樹(shù)的左子樹(shù)异剥,仍然是一棵樹(shù),遞歸構(gòu)造這棵樹(shù)的左子樹(shù)
            //設(shè)x為當(dāng)前樹(shù)先序中左子樹(shù)最后一個(gè)元素的下標(biāo)絮重,則:x - (lpre + 1) = lNum
            //得:x = lPre + lNum
            lchild = buildTree(pre, lPre + 1, lPre+lNum, mid, lMid, rootIndex - 1);
        }

        //構(gòu)建右子樹(shù)
        TreeNode rchild;
        if(rNum == 0){
            rchild = null;
        }else{
            //當(dāng)前結(jié)點(diǎn)的右子樹(shù)冤寿,仍然包含很多節(jié)點(diǎn)歹苦,需要遞歸的構(gòu)造其右子樹(shù)
            rchild = buildTree(pre, lPre + lNum + 1, rPre, mid, rootIndex + 1, rMid);
        }

        //構(gòu)造完整的二叉樹(shù)
        return new TreeNode(root,lchild,rchild);
    }

    //在中序遍歷序列中,根據(jù)先序中的根結(jié)點(diǎn)來(lái)查找在中序中的位置
    private int getRootIndex(char[] mid, int lMid, int rMid, char root) {
        for(int i = lMid; i <= rMid; i++){
            if(mid[i] == root){
                return i;
            }
        }
        return -1;
    }


    //二叉樹(shù)每一個(gè)結(jié)點(diǎn)的結(jié)構(gòu)
    private class TreeNode{
        //結(jié)點(diǎn)中存儲(chǔ)的數(shù)據(jù)
        char item;
        //指向左孩子結(jié)點(diǎn)
        TreeNode lChild;
        //指向右孩子結(jié)點(diǎn)
        TreeNode rChild;

        //構(gòu)造方法,完成初始化
        public TreeNode(char item, TreeNode lChild, TreeNode rChild){
            this.item = item;
            this.lChild = lChild;
            this.rChild = rChild;
        }
    }

    //提供三個(gè)讓外界調(diào)用的方法
    public  void preTraverse() {
        preOrder(root);
    }

    public void midTraverse() {
        midOrder(root);
    }

    public void postTraverse() {
        postOrder(root);
    }

    //先序遍歷  DLR
    private void preOrder(TreeNode root) {
        if( root != null) {
            //先訪問(wèn)根節(jié)點(diǎn)
            System.out.print(root.item + " ");
            //遞歸訪問(wèn)左子樹(shù)
            preOrder(root.lChild);
            //遞歸訪問(wèn)右子樹(shù)
            preOrder(root.rChild);
        }
    }


    //中序遍歷   LDR
    private void midOrder(TreeNode root) {
        if(root != null) {
            //遞歸訪問(wèn)左子樹(shù)
            midOrder(root.lChild);
            //訪問(wèn)根
            System.out.print(root.item + " ");
            //遞歸訪問(wèn)右子樹(shù)
            midOrder(root.rChild);
        }
    }

    //后續(xù)遍歷
    // LRD
    private void postOrder(TreeNode root) {
        if(root != null) {
            //遞歸訪問(wèn)左子樹(shù)
            postOrder(root.lChild);
            //遞歸訪問(wèn)右子樹(shù)
            postOrder(root.rChild);
            //訪問(wèn)根
            System.out.print(root.item + " ");
        }
    }


    //廣度優(yōu)先遍歷  BFS
    public void BFS() {
        //創(chuàng)建一個(gè)能放TreeNode對(duì)象的隊(duì)列
        Queue<TreeNode> queue = new LinkedList<>();
        //將樹(shù)的根節(jié)點(diǎn)入隊(duì)列
        queue.add(root);
        //循環(huán)執(zhí)行廣度優(yōu)先遍歷
        while(!queue.isEmpty()) {
            //將當(dāng)前的隊(duì)頭元素出隊(duì)列
            TreeNode node = queue.remove();
            //訪問(wèn)出隊(duì)列的節(jié)點(diǎn)
            System.out.print(node.item + " ");

            //出隊(duì)列的節(jié)點(diǎn)是否有左孩子督怜,有則將其左孩子入隊(duì)列
            if(node.lChild != null) {
                //有左孩子
                queue.add(node.lChild);
            }
            //出隊(duì)列的節(jié)點(diǎn)是否有右孩子殴瘦,如果右,將其右孩子如隊(duì)列
            if(node.rChild != null) {
                queue.add(node.rChild);
            }
        }
    }
}

/* Test.java*/
package com.java.tree;

/**
 * 測(cè)試類(lèi)
 * Created by Jaco.Young.
 * 2018-06-13 20:16
 */
public class Test {
    public static void main(String[] args){
        //構(gòu)造先序遍歷序列和中序遍歷序列
        char[] pre = {'A','B','E', 'K', 'L', 'F', 'D', 'H', 'J'};
        char[] mid = {'K', 'E', 'L', 'B', 'F', 'A', 'H', 'D', 'J'};

        BitTree bitTree = new BitTree();
        //根據(jù)遍歷序列構(gòu)建二叉樹(shù)
        bitTree.build(pre, mid);

        //先序遍歷
        bitTree.preTraverse();
        System.out.println();
        //中序遍歷
        bitTree.midTraverse();
        System.out.println();
        //后序遍歷
        bitTree.postTraverse();
        System.out.println();
        //廣度優(yōu)先遍歷
        bitTree.BFS();
    }
}

運(yùn)行結(jié)果為:
A B E K L F D H J
K E L B F A H D J
K L E F B H J D A
A B D E F H J K L

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末号杠,一起剝皮案震驚了整個(gè)濱河市蚪腋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姨蟋,老刑警劉巖屉凯,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異眼溶,居然都是意外死亡悠砚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)堂飞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)灌旧,“玉大人,你說(shuō)我怎么就攤上這事绰筛∈嗵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵铝噩,是天一觀的道長(zhǎng)衡蚂。 經(jīng)常有香客問(wèn)我,道長(zhǎng)骏庸,這世上最難降的妖魔是什么讳窟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮敞恋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谋右。我一直安慰自己硬猫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布改执。 她就那樣靜靜地躺著啸蜜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辈挂。 梳的紋絲不亂的頭發(fā)上衬横,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音终蒂,去河邊找鬼蜂林。 笑死遥诉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的噪叙。 我是一名探鬼主播矮锈,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼睁蕾!你這毒婦竟也來(lái)了苞笨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤子眶,失蹤者是張志新(化名)和其女友劉穎瀑凝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體臭杰,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粤咪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硅卢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片射窒。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖将塑,靈堂內(nèi)的尸體忽然破棺而出脉顿,到底是詐尸還是另有隱情,我是刑警寧澤点寥,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布艾疟,位于F島的核電站,受9級(jí)特大地震影響敢辩,放射性物質(zhì)發(fā)生泄漏蔽莱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一戚长、第九天 我趴在偏房一處隱蔽的房頂上張望盗冷。 院中可真熱鬧,春花似錦同廉、人聲如沸仪糖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锅劝。三九已至,卻和暖如春蟆湖,著一層夾襖步出監(jiān)牢的瞬間故爵,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工隅津, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诬垂,地道東北人劲室。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像剥纷,于是被迫代替她去往敵國(guó)和親痹籍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容