午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

動(dòng)畫:二叉樹有幾種存儲(chǔ)方式?(上)

 星光閃亮圖書館 2019-12-21



作者 |  小鹿

來源 |  小鹿動(dòng)畫學(xué)編程

前邊幾篇文章的講了數(shù)組、鏈表、隊(duì)列等,今天和大家主要分享的是樹這種數(shù)據(jù)結(jié)構(gòu)。

樹這種數(shù)據(jù)結(jié)構(gòu)不像數(shù)組、鏈表一樣,它是一種非線性結(jié)構(gòu),學(xué)起來可能比其他數(shù)據(jù)結(jié)構(gòu)比較吃力,但是它在數(shù)據(jù)結(jié)構(gòu)中占有很重要的地位,也是面試中的頻繁考點(diǎn),尤其是二叉樹,一定注重起來。

由題目拋出的問題,樹到底怎么存儲(chǔ)呢?二叉樹有幾種存儲(chǔ)方式呢?如果帶著好奇心學(xué)習(xí),學(xué)習(xí)更加的高效,一顆樹橫七豎八的,咋表示?下邊小鹿帶你一起來探索。

1

什么是樹?

顧名思義,第一想到的就是路邊的樹,有樹干、樹根、樹葉,數(shù)據(jù)結(jié)構(gòu)中的樹也是這樣延伸過來的,只不過專用名詞不一樣,直接上圖。

有一些樹的專屬名詞我們總結(jié)下,A 是 B 的父節(jié)點(diǎn),B 是 A 的子節(jié)點(diǎn),D 是 B 的兄弟節(jié)點(diǎn),C 和 D 稱為葉子節(jié)點(diǎn),A 為根節(jié)點(diǎn)。

是樹它就有高度,數(shù)據(jù)結(jié)構(gòu)中的樹不僅有高度的概念,還有樹的深度、層,節(jié)點(diǎn)的高度、深度等,一些最基本的知識(shí)點(diǎn)。

高度

樹的高度就是根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的最長(zhǎng)路徑。節(jié)點(diǎn)的高度就是節(jié)點(diǎn)到葉子節(jié)點(diǎn)的高度。

深度

節(jié)點(diǎn)的深度就是該節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑,也就是邊的數(shù)量。

根節(jié)點(diǎn)為第一層,依次往下遞增。

PS:注意各個(gè)概念的方向和起始值。

2

什么是二叉樹?

我們知道什么是樹了,二叉樹的概念,就是給樹做了一個(gè)限制,除了葉子結(jié)點(diǎn),其余每個(gè)節(jié)點(diǎn)僅且只有兩個(gè)子節(jié)點(diǎn)(也就是只有兩個(gè)叉)。

二叉樹有兩個(gè)很重要的形態(tài)就是滿二叉樹和完全二叉樹。

滿二叉樹:葉子節(jié)點(diǎn)全都在最底層,除了葉子節(jié)點(diǎn)之外,每個(gè)節(jié)點(diǎn)都有左右兩個(gè)子節(jié)點(diǎn),這種二叉樹就叫作滿二叉樹。

完全二叉樹:葉子節(jié)點(diǎn)都在最底下兩層 ,最后一層的葉子節(jié)點(diǎn)都靠左排列,并且除了最后一層,其他層的節(jié)點(diǎn)個(gè)數(shù)都要達(dá)到最大。

3

二叉樹的存儲(chǔ)方式有幾種?

既然我們都基本了解了二叉樹的概念和基本常識(shí),那我們要用,就要進(jìn)行存儲(chǔ),如何存儲(chǔ)一顆二叉樹呢?

還記不記得小鹿之前的幾篇文章一直強(qiáng)調(diào)說過,所有基本常見的數(shù)據(jù)結(jié)構(gòu)都是由數(shù)組和鏈表演變而來,棧有順序棧和鏈?zhǔn)綏?、?duì)列有順序隊(duì)列和鏈?zhǔn)疥?duì)列,那么樹可以用數(shù)組存儲(chǔ)也可以用鏈表存儲(chǔ)呀。

鏈?zhǔn)酱鎯?chǔ)法

基于指針的鏈?zhǔn)酱鎯?chǔ),每個(gè)樹的節(jié)點(diǎn)都是由數(shù)據(jù)域和兩個(gè)指針域組成的。數(shù)據(jù)域用來存儲(chǔ)數(shù)據(jù),指針域用來存儲(chǔ)左右兩個(gè)子節(jié)點(diǎn)。

順序存儲(chǔ)法

順序存儲(chǔ)就是用數(shù)組來存儲(chǔ)的,雖然不如指針域那么直觀,但是存儲(chǔ)的方法挺好理解的。根節(jié)點(diǎn)存儲(chǔ)在下標(biāo) i = 1 的位置;左子節(jié)點(diǎn)存儲(chǔ)在下標(biāo)i * 2 = 2的位置,右子節(jié)點(diǎn)存儲(chǔ)在i * 2 + 1 =3的位置。

你會(huì)問,兩種方式有什么區(qū)別,這個(gè)問題其實(shí)你早就知道了,如果你看到過小鹿之前寫的文章,就知道區(qū)別就是數(shù)組和鏈表的區(qū)別。

數(shù)組的方式存儲(chǔ)不需要開辟額外的指針空間,但是數(shù)組需要的內(nèi)存空間是連續(xù)的,如果連續(xù)的內(nèi)存空間不足,就無法進(jìn)行存儲(chǔ)。

4

二叉樹的遍歷

既然存儲(chǔ)方式你也學(xué)會(huì)了,那么我們看看二叉樹存儲(chǔ)的數(shù)據(jù),我們?nèi)绾伪闅v出來呢?

共有四種遍歷的方式,分別為前序遍歷、中序遍歷、后序遍歷、按層遍歷。

這個(gè)前、中、后遍歷,初學(xué)者最不容易理解,當(dāng)初小鹿學(xué)習(xí)的時(shí)候也是這樣,那我用最簡(jiǎn)單的步驟和圖來講述一下。

我們以前序遍歷為例子,首先我要告訴你前序遍歷的規(guī)則,就是先遍歷根節(jié)點(diǎn)、然后遍歷左子節(jié)點(diǎn)最后遍歷右子節(jié)點(diǎn),上圖:

這個(gè)圖,你很快就能知道前序遍歷的順序,答案為:A->B->C

但是我換一張圖,你來前序遍歷下。

你可能就懵逼了,咋和剛才的不一樣?我就是按照剛才那樣遍歷的?無論你是否成功遍歷,小鹿來講一下自己的思路。

我們還是看根節(jié)點(diǎn)的三個(gè)節(jié)點(diǎn),按照根、左、右,我們知道先輸出根節(jié)點(diǎn) A、然后輸出左子節(jié)點(diǎn),因?yàn)?B 作為 D 和 E 的根節(jié)點(diǎn)先輸出,然后 B 的左子節(jié)點(diǎn)是 D,D 作為 F 的根節(jié)點(diǎn)也要輸出 D。

然后開始遍歷左子節(jié)點(diǎn),D 根節(jié)點(diǎn)的左子節(jié)點(diǎn)為 F,所以輸出 F, B 的左子節(jié)點(diǎn)已經(jīng)輸出完畢,所以遍歷右子節(jié)點(diǎn) E。那么整個(gè) A 的左子節(jié)點(diǎn)已經(jīng)全部輸出完畢,開始遍歷 A 額右子節(jié)點(diǎn)。

還是按照左、根、右的遍歷方法,依次輸出去 C、H。整體的前序遍歷為:A->B->D->F->E->C->H。動(dòng)畫如下:

前序遍歷小鹿講的很詳細(xì)了,剩下的中序遍歷和后續(xù)遍歷的規(guī)則不一樣。中序遍歷先遍歷左子節(jié)點(diǎn),然后是根節(jié)點(diǎn),最后是右子節(jié)點(diǎn)。而后序遍歷的順序是先遍歷左子結(jié)點(diǎn),然后遍歷右子節(jié)點(diǎn),最后遍歷根節(jié)點(diǎn)。

其實(shí)和前序遍歷道理都是一樣的,只不過是換湯不換藥,我把動(dòng)圖放到下放了,自己可以對(duì)照著遍歷一下。

5

代碼實(shí)現(xiàn)

JavaScript 版本

1//遍歷二叉查找樹
2//前序遍歷
3preorderTraversal = (tree) =>{
4    //判斷樹是否為空
5    if(tree == null) return false;
6    //根節(jié)點(diǎn)
7    console.log(tree.data)
8    //左子樹
9    this.preorderTraversal(tree.left)
10    //右子樹
11    this.preorderTraversal(tree.right)
12}
13
14//中序遍歷
15inorderTraversal = (tree) =>{
16    //判斷樹是否為空
17    if(tree == null) return false;
18    //左子樹
19    this.inorderTraversal(tree.left);
20    //根節(jié)點(diǎn)
21    console.log(tree.data)
22    //右節(jié)點(diǎn)
23    this.inorderTraversal(tree.right);
24}
25
26//后序遍歷
27postorderTraversal = (tree) =>{
28    //判斷樹是否為空
29    if(tree == null) return false;
30    //左子樹
31    this.postorderTraversal(tree.left);
32    //右子樹
33    this.postorderTraversal(tree.right);
34    //根節(jié)點(diǎn)
35    console.log(tree.data)
36}

Java 版本

 1   /**
2     * 時(shí)間:2019/2/24
3     * 功能:前序遍歷
4     * @param root 樹的根節(jié)點(diǎn)
5     */
6    public void preorderTraversal(Node root) {
7        //如果樹為空
8        if(root == null) return;
9        //根節(jié)點(diǎn)
10        System.out.print(root.data + ' ');
11        //左子樹
12        inorderTraversal(root.left);
13        //右子樹
14        inorderTraversal(root.right);
15
16    }   
17
18    /**
19     * 時(shí)間:2019/2/24
20     * 功能:中序遍歷
21     * @param root 樹的根節(jié)點(diǎn)
22     */
23    public void inorderTraversal(Node root) {
24        //如果樹為空
25        if(root == null) return;
26        //左子樹
27        inorderTraversal(root.left);
28        //根節(jié)點(diǎn)
29        System.out.print(root.data + ' ');
30        //右子樹
31        inorderTraversal(root.right);
32
33    }
34
35    /**
36     * 時(shí)間:2019/2/24
37     * 功能:后序遍歷
38     * @param root 樹的根節(jié)點(diǎn)
39     */
40    public void postorderTraversal(Node root) {
41        //如果樹為空
42        if(root == null) return;
43        //左子樹
44        inorderTraversal(root.left);
45        //右子樹
46        inorderTraversal(root.right);
47        //根節(jié)點(diǎn)
48        System.out.print(root.data + ' ');
49
50    }

Python 版本

1class BTree(object):
2    def __init__(self):
3        self._root = None
4        self._size = 0
5
6    def preOrder(self):
7        '''
8        先遍歷順序:前序遍歷
9        1,根節(jié)點(diǎn)
10        2,遍歷左子樹
11        3,遍歷右子樹
12        '''
13        btree = []
14
15        def recurse(node):
16            if node != None:
17                btree.append(node.data)
18                recurse(node.lft)
19                recurse(node.rgt)
20
21        recurse(self._root)
22        return btree
23
24class BTree(object):
25    def __init__(self):
26        self._root = None
27        self._size = 0
28
29    def preOrder(self):
30        '''
31        先遍歷順序:中序遍歷
32        1,根節(jié)點(diǎn)
33        2,遍歷左子樹
34        3,遍歷右子樹
35        '''
36        btree = []
37
38        def recurse(node):
39            if node != None:
40                btree.append(node.data)
41                recurse(node.lft)
42                recurse(node.rgt)
43
44        recurse(self._root)
45        return btree
46
47class BTree(object):
48    def __init__(self):
49        self._root = None
50        self._size = 0
51
52    # 后序遍歷
53    def postOrder(self):
54        '''
55        后序遍歷順序:后續(xù)遍歷
56        1,遍歷左子樹
57        2,遍歷右子樹
58        3,根節(jié)點(diǎn)
59        '''
60        btree = []
61
62        def recurse(node):
63            if node != None:
64                recurse(node.lft)
65                recurse(node.rgt)
66                btree.append(node.data)
67
68        recurse(self._root)
69        return btree

C 語(yǔ)言版本

 1#include <stdio.h>
2#include <stdlib.h>
3
4/* 定義數(shù)據(jù)類型 */
5typedef char TypeData ;
6
7/* 定義二叉樹 */
8typedef struct stBiTreeNode
9{
10    TypeData data;
11    struct stBiTreeNode *lchild, *rchild;
12}BITREENODE;
13
14/* 初始化二叉樹 */
15BITREENODE* createBiTree()
16{
17    char chTempData = 0;
18
19    BITREENODE *pstNewNode = NULL;
20
21    scanf('%c',&chTempData);
22    if(chTempData == '#')
23    {
24        pstNewNode = NULL;
25    }
26    else
27    {
28        /* 分配內(nèi)存 */
29        pstNewNode = (BITREENODE*)malloc(sizeof(BITREENODE) + 1);
30        pstNewNode->data = chTempData;
31
32        /* 遞歸調(diào)用產(chǎn)生二叉樹 */
33        pstNewNode->lchild = createBiTree();
34        pstNewNode->rchild = createBiTree();
35    }
36
37    return pstNewNode;
38}
39
40/* 前序遍歷二叉樹 */
41int preVisitBiTree(BITREENODE* InRoot)
42{
43    if(InRoot)
44    {
45        /* 先遍歷根節(jié)點(diǎn) */
46        printf('%c ',InRoot->data);
47
48        /* 遍歷左子樹 */
49        preVisitBiTree(InRoot->lchild);
50
51        /* 遍歷右子樹 */
52        preVisitBiTree(InRoot->rchild);
53
54    }
55    return 0;
56}
57
58
59/* 中序遍歷二叉樹 */
60int inVisitBiTree(BITREENODE* InRoot)
61{
62    if(InRoot)
63    {
64        /* 遍歷左子樹 */
65        preVisitBiTree(InRoot->lchild);
66
67
68        /* 先遍歷根節(jié)點(diǎn) */
69        printf('%c ',InRoot->data);
70
71        /* 遍歷右子樹 */
72        preVisitBiTree(InRoot->rchild);
73
74    }
75    return 0;
76}
77
78/* 后序遍歷二叉樹 */
79int postVisitBiTree(BITREENODE* InRoot)
80{
81    if(InRoot)
82    {
83        /* 遍歷左子樹 */
84        preVisitBiTree(InRoot->lchild);
85
86
87        /* 遍歷右子樹 */
88        preVisitBiTree(InRoot->rchild);
89
90
91        /* 先遍歷根節(jié)點(diǎn) */
92        printf('%c ',InRoot->data);
93
94    }
95    return 0;
96}

6

小結(jié)

今天小鹿分享了二叉樹的基礎(chǔ)部分,二叉樹的基本概念以及二叉樹的存儲(chǔ)方式,還有二叉樹的前中后遍歷,最后留一個(gè)問題就是,二叉樹除了前中后序遍歷外,還有按層遍歷,可以自己去探索一下二叉樹按層是怎么遍歷的?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多