日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

心中有“树”:数据结构之树详解

發(fā)布時(shí)間:2025/3/21 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 心中有“树”:数据结构之树详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 前言
  • (一)樹的基礎(chǔ)定義與表示
    • 1 樹的定義
    • 2 樹的圖示
    • 3 樹的邏輯結(jié)構(gòu)表示法
  • (二)二叉樹
    • 1 二叉樹定義
    • 2 二叉樹示意圖
    • 3 程序?qū)崿F(xiàn)
      • (1)節(jié)點(diǎn)定義
      • (2)二叉樹的先序遍歷
      • (3)二叉樹非遞歸先序遍歷
      • (4)二叉樹層序遍歷
      • (5)計(jì)算二叉樹的深度
    • 4 二叉樹算法設(shè)計(jì)題目
      • 1 查找元素為X的節(jié)點(diǎn)
      • 2 判斷二叉樹是否為完全二叉樹
  • (三)樹
    • 1 樹的存儲(chǔ)結(jié)構(gòu)
    • 2 存儲(chǔ)結(jié)構(gòu)圖示
    • 3 樹的先序遍歷
    • 4 求樹的深度
  • (四)總結(jié)
  • (五)參考文獻(xiàn)

前言

樹型結(jié)構(gòu)是一類重要的非線性數(shù)據(jù)結(jié)構(gòu)。其中以樹和二叉樹最為常用。樹狀結(jié)構(gòu)在客觀世界中廣泛存在,如人類社會(huì)的族譜和社會(huì)組織機(jī)構(gòu)等,都可以用樹來(lái)形象表示。同時(shí)在計(jì)算機(jī)領(lǐng)域中,樹可以用來(lái)表示源程序的語(yǔ)法結(jié)構(gòu),并用于加密信息。本文主要討論二叉樹極其各種操作與應(yīng)用,并拓展樹的結(jié)構(gòu)與簡(jiǎn)單操作。

(一)樹的基礎(chǔ)定義與表示

1 樹的定義

(Tree)是n(n>=0)個(gè)節(jié)點(diǎn)的有限集。在任意一顆非空樹中:
(1)有且僅有一個(gè)特定的(Root)的節(jié)點(diǎn);
(2)n>1時(shí)其余節(jié)點(diǎn)可分為m(m>0)個(gè)互不相交的有限集合T1,T2,…Tm,其中每一個(gè)集合本身又是一顆樹,并且稱為根的子樹
顯然,這是一個(gè)遞歸的定義。故對(duì)于樹的操作等也需要使用遞歸操作。

2 樹的圖示


設(shè)有樹a,b,c,如上圖所示,其中a為空樹,b只有一個(gè)根節(jié)點(diǎn)為A,c的根節(jié)點(diǎn)為A,它又有三顆子樹,假設(shè)為T1,T2,T3,這三顆子樹又分別有自己的根B,C,D和相應(yīng)的更小的子樹,依此類推。

3 樹的邏輯結(jié)構(gòu)表示法

(1)層次表示法
前面的樹的圖示所用的表示法即層次表示法,它可以直觀表示樹中個(gè)節(jié)點(diǎn)的層次關(guān)系。同時(shí)可以更好地理解關(guān)于樹的各種操作
(2)嵌套表示法
嵌套表示法采用集合形式表示邏輯結(jié)構(gòu),類似于集合中的韋恩圖,應(yīng)用較少,此不過(guò)多介紹。
(3)廣義表表示法
廣義表利用根節(jié)點(diǎn)元素和括號(hào)的組合表示。括號(hào)外邊是根節(jié)點(diǎn),括號(hào)里邊是每個(gè)子樹。如(2)中的圖示中 c 樹可以表示為 A(B(E(K,L),F),C(G),D(H(M),I,J)))。

(二)二叉樹

1 二叉樹定義

二叉樹是最常用的一種樹型結(jié)構(gòu),其特點(diǎn)是每個(gè)節(jié)點(diǎn)最多只有兩顆子樹(即二叉樹中不存在度大于2的節(jié)點(diǎn)),二叉樹的子樹有左右之分,次序不能任意顛倒。

2 二叉樹示意圖

二叉樹的順序表表示法較為繁瑣,且浪費(fèi)空間,我們以二叉鏈表表示法為例:

將左圖層次表示與節(jié)點(diǎn)的存儲(chǔ)結(jié)構(gòu)關(guān)聯(lián)即右圖所示。每個(gè)節(jié)點(diǎn)有一個(gè)數(shù)據(jù)域,與兩個(gè)指針域,分別指向左孩子右孩子。同時(shí)每個(gè)左孩子又是一顆子樹。在這里我們以 ^ 符號(hào)表示指針域?yàn)榭?#xff08;NULL)表示其沒有左(右)子樹。

3 程序?qū)崿F(xiàn)

前面說(shuō)了二叉樹的定義,接下來(lái)看一下關(guān)鍵的程序代碼。

(1)節(jié)點(diǎn)定義

二叉樹的二叉鏈表存儲(chǔ)結(jié)構(gòu)中,節(jié)點(diǎn)擁有一個(gè)數(shù)據(jù)域存儲(chǔ)元素,此處元素類型我們以字符型為例,兩個(gè)指針域存儲(chǔ)左右孩子節(jié)點(diǎn)的地址。

二叉鏈表表示法

typedef struct BiTNode {char data; //數(shù)據(jù)元素BiTNode* lc, * rc;//分別指向左孩子 與 右}*BiTree;

lc,rc分別為指向左右孩子節(jié)點(diǎn)的地址的指針。BiTNode為節(jié)點(diǎn)結(jié)構(gòu)體類型,為了代碼編寫方便,同時(shí)我們將指向BiTNode的指針的類型定義為BiTree。BiTree相當(dāng)于BiTNode*。同時(shí)二叉樹還有三叉鏈表表示法,其節(jié)點(diǎn)定義多了一個(gè)指向雙親的指針域。即BiTNode* parent,這樣可以更方便的尋找某個(gè)節(jié)點(diǎn)的雙親節(jié)點(diǎn)。

(2)二叉樹的先序遍歷

二叉樹的遍歷分為先序遍歷中序遍歷后序遍歷層序遍歷
先序遍歷:
(1)訪問(wèn)根節(jié)點(diǎn)——D;
(2)遍歷左子樹——L;
(3)遍歷右子樹——R;
先序遍歷簡(jiǎn)稱為DLR遍歷,同理中序?yàn)長(zhǎng)DR遍歷,后序?yàn)長(zhǎng)RD遍歷。下面我們以先序遍歷程序?yàn)槔?#xff0c;中序和后序僅需要調(diào)整程序順序即可。

void Preorder(BiTree T,void visit(TElemType)) {if(!T)return;visit(T->data);//訪問(wèn)根節(jié)點(diǎn)Preorder(T->lc,visit);//遍歷左子樹Preorder(T->rc,visit);//遍歷右子樹 }

其中visit為函數(shù)作為形參,為了使得代碼具有通用性。具體visit實(shí)現(xiàn)可以是最簡(jiǎn)單的輸出,或者對(duì)于節(jié)點(diǎn)進(jìn)行的一系列操作。函數(shù)作為形參參考下列代碼。

#include<iostream> int num[] = { 1,2,3,4 }; void Print(int m) { std::cout << m << std::endl; } void ArrayPrint(int num[], void visit(int)) {for (int i = 0; i < 4; i++)visit(num[i]); } int main() {ArrayPrint(num, Print); }

(3)二叉樹非遞歸先序遍歷

由于遞歸實(shí)際是棧的壓棧出棧操作,所以我們可以將遞歸的先序遍歷改為非遞歸的棧操作。
算法思路:從根節(jié)點(diǎn)開始,每訪問(wèn)一個(gè)節(jié)點(diǎn),按照前序遍歷規(guī)則走左子樹,如果系欸點(diǎn)的右子樹存在,那么將右指針入棧,以便正確遍歷相應(yīng)子樹。非遞歸操作不常用,對(duì)二叉樹的操作基本采用遞歸形式容易理解。

void PreorderStack(BiTree T) {BiTree p;StackInit(S);//棧初始化為空push(S,T);//根節(jié)點(diǎn)入棧while(!empty(S)){ p=pop(S);//出棧 同時(shí)p為棧頂元素while(p){visit(p);//訪問(wèn)p節(jié)點(diǎn)if(p->rc)push(S,p->rc);//右子樹存在 則進(jìn)棧p=p->lc;//向左走一步}} }

(4)二叉樹層序遍歷

二叉樹層序遍歷需要用到隊(duì)列。即訪問(wèn)根節(jié)點(diǎn)的同時(shí),將其左右孩子一次入隊(duì)列,較為簡(jiǎn)單。利用STL封裝的queue實(shí)現(xiàn)。代碼程序如下:

void QueuePreorder(BiTree T) {queue <BiTree> q;if(!T)q.push(T);while (!q.empty()) //隊(duì)列不為空判斷{visit(q.front()->data);if(!q.front()->lc)q.push(q.front()->lc);if(!q.front()->rc)q.push(q.front()->rc);q.pop(); //已遍歷節(jié)點(diǎn)出隊(duì)} }

(5)計(jì)算二叉樹的深度

//求二叉樹的深度 int depth(BiTree T) {if (!T)return 0;int dl, dr;//分別表示左子樹深度與右子樹深度dl = depth(T->lc); dr = depth(T->rc);return dl > dr ? dl + 1 : dr + 1;//加一為 加上根節(jié)點(diǎn)所在深度 }

4 二叉樹算法設(shè)計(jì)題目

1 查找元素為X的節(jié)點(diǎn)

題目:再二叉樹中查找是否有元素為X的節(jié)點(diǎn),找到則返回節(jié)點(diǎn)地址,否則返回空指針
分析:題目比較簡(jiǎn)單。可以直接在二叉樹中遞歸查找,如果根是空,返回NULL。如果元素為X,返回地址。否則新建節(jié)點(diǎn)M接收左子樹返回的值,如果非空返回M。否則返回右子樹返回的值。同時(shí)遞歸時(shí),我們只需要考慮當(dāng)前一層的情況,具體內(nèi)部遞歸回代實(shí)現(xiàn)可以不必詳細(xì)了解,否則很容易被繞進(jìn)去。程序?qū)崿F(xiàn)為

BiTree TreeSearch(BiTree T,char e)//BiTree類型 {//遞歸考慮當(dāng)前一層 BiTree M;if (!T)return NULL;if (T->data == e)return T;else{M = TreeSearch(T->lc, e);if (M)return M;//左子樹存在 返回地址return TreeSearch(T->rc, e);//無(wú)論右子樹是否存在,均返回} }

在這里我們需要用M來(lái)保存遞歸左子樹返回的值,否則若只有兩個(gè)TreeSearch的順序結(jié)構(gòu),則只有第二個(gè)TreeSearch起作用,左子樹是否右X不得而知。局部錯(cuò)誤代碼為:

else {//錯(cuò)誤TreeSearch(T->lc,e);TreeSearch(T->rc,e); }

2 判斷二叉樹是否為完全二叉樹

完全二叉樹:深度為k的,有n個(gè)節(jié)點(diǎn)的二叉樹,當(dāng)且僅當(dāng)其每一個(gè)節(jié)點(diǎn)都與深度為k的滿二叉樹的1至n的節(jié)點(diǎn)一一對(duì)應(yīng)時(shí),稱為完全二叉樹。
簡(jiǎn)單來(lái)說(shuō),就是只可能最后一層節(jié)點(diǎn)是不滿的,同時(shí)節(jié)點(diǎn)應(yīng)盡可能向左補(bǔ)充。
算法思路:可以利用層序遍歷,首先將根節(jié)點(diǎn)入隊(duì)。當(dāng)可以從隊(duì)列取出元素且不空,則將左右孩子入隊(duì)。當(dāng)出現(xiàn)空指針結(jié)束循環(huán)。最后再用一個(gè)while循環(huán)如果遍歷到了非空元素則為非完全二叉樹,否則最終為完全二叉樹。(因?yàn)橥耆鏄渲挥凶詈笠慌趴梢钥展?jié)點(diǎn),也就是遇到空指針后,后面的層序遍歷只能都是空指針)。代碼實(shí)現(xiàn)為:

bool ComTreeJudge(BiTree T) {CSQueue q; QueueInit(q); BiTree p;if (!T)return false; Enqueue(q, T);while (Dequeue(q, p))//有元素可以出隊(duì){//遍歷到空指針結(jié)束遍歷if (!p)break;Enqueue(q, p->lc);Enqueue(q, p->rc);}//訪問(wèn)后續(xù)隊(duì)列元素,若出現(xiàn)非空,那么非完全二叉樹while (Dequeue(q, p)){if (p)return false;}return true; }

(三)樹

1 樹的存儲(chǔ)結(jié)構(gòu)

此處推廣至更為普遍的樹結(jié)構(gòu),一個(gè)節(jié)點(diǎn)可以擁有多個(gè)孩子節(jié)點(diǎn)。常見表示法為雙親表示法孩子表示法孩子-兄弟表示法(二叉樹表示法),此處以更為常見的孩子兄弟表示法為例。

typedef struct CSNode {int data;CSNode* firstchild,*nextsibling;// }*Tree;

2 存儲(chǔ)結(jié)構(gòu)圖示


孩子-兄弟表示法又稱為二叉樹表示法或二叉鏈表表示法。鏈表中節(jié)點(diǎn)的兩個(gè)指針域分別指向該節(jié)點(diǎn)的第一個(gè)孩子節(jié)點(diǎn)和下一個(gè)兄弟節(jié)點(diǎn)(即與第一個(gè)孩子節(jié)點(diǎn)在同一層)。

3 樹的先序遍歷

如果根節(jié)點(diǎn)為空,返回;否則訪問(wèn)根節(jié)點(diǎn)。然后依次遍歷第一個(gè)孩子節(jié)點(diǎn)和它的兄弟節(jié)點(diǎn)(以其為根的子樹),此處可以用一個(gè)for循環(huán)。下面以fc代表第一個(gè)孩子節(jié)點(diǎn)指針域,ns代表兄弟節(jié)點(diǎn)指針域。程序?qū)崿F(xiàn)為:

void TreePreorder(Tree T,void visit(TElemType)) {Tree p;if(!T)return;visit(T->data);for(p=T->fc;p;p=p->ns)TreePreorder(p,visit); }

讀者可依據(jù)二叉樹的某些操作實(shí)現(xiàn)樹的一些操作,算法思路沒有太大變化。

4 求樹的深度

樹的深度為各個(gè)子樹的最大深度加一,可一次遍歷遞歸所有子樹,最后返回深度加一。程序?qū)崿F(xiàn):

int TreeDepth(Tree T) {int m1;Tree p;int m2;//記錄最大深度if(!T)return 0;for(m2=0,p=T->fc;p;p=p->ns)//循環(huán)求出子樹深度最大值,最后加上根節(jié)點(diǎn)層{m1=TreeDepth(p);//以p為根節(jié)點(diǎn)的子樹深度if(m1>m2)m2=m1;}return m2+1; }

(四)總結(jié)

1 本篇文章總結(jié)了以二叉樹為主的樹的一些常用算法,包括對(duì)其的遍歷與一些算法設(shè)計(jì)題目。大部分通過(guò)遞歸實(shí)現(xiàn),可在理解遞歸基礎(chǔ)上編寫相應(yīng)算法。
2 有關(guān)于樹的數(shù)據(jù)結(jié)構(gòu)還有很多,例如Huffman樹,二叉排序樹等主要數(shù)據(jù)結(jié)構(gòu)和算法,讀者可以自行學(xué)習(xí),此處沒有詳細(xì)介紹。
3 本章討論的樹是數(shù)據(jù)結(jié)構(gòu)中非線性數(shù)據(jù)結(jié)構(gòu)中很重要的一類,是一種層次模型的數(shù)據(jù)結(jié)構(gòu)。它可以較好地描述數(shù)據(jù)間地層次關(guān)系,能對(duì)數(shù)據(jù)機(jī)構(gòu)隨機(jī)再組織,故樹一般成為動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)。

(五)參考文獻(xiàn)

【1】嚴(yán)蔚敏.吳偉民. 數(shù)據(jù)結(jié)構(gòu)(C語(yǔ)言版). 北京:清華大學(xué)出版社,1997.
【2】齊悅.夏克儉.姚琳. 數(shù)據(jù)結(jié)構(gòu)、算法與應(yīng)用. 北京:清華大學(xué)出版社.2015.
【3】聶立新.桑兆陽(yáng).張華清. 數(shù)據(jù)結(jié)構(gòu)與算法. 中國(guó)石油大學(xué)出版社.2017.

小白第二篇,受限于知識(shí)水平與寫作能力,文中不足之處,敬請(qǐng)大家批評(píng)指正!

總結(jié)

以上是生活随笔為你收集整理的心中有“树”:数据结构之树详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。