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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【数据结构】二叉树的存储和遍历

發(fā)布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【数据结构】二叉树的存储和遍历 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

二叉樹的存儲結(jié)構(gòu)

順序存儲結(jié)構(gòu)

二叉樹的順序存儲結(jié)構(gòu)是指用一組地址連續(xù)的存儲單元依次自上而下、從左到右存儲完全二叉樹上的結(jié)點,即將完全二叉樹上編號為 i 的結(jié)點存儲在數(shù)組下標(biāo)為 i?1的數(shù)組分量中,然后通過一定方式確定結(jié)點在邏輯上的父子或兄弟關(guān)系。
???
空間浪費比較大

鏈?zhǔn)酱鎯Y(jié)構(gòu)

二叉樹每個結(jié)點最多兩個孩子,所以設(shè)計二叉樹的結(jié)點結(jié)構(gòu)時考慮兩個指針指向該結(jié)點的兩個孩子。

二叉樹結(jié)點結(jié)構(gòu):

typedef struct BiTNode{ElemType data; //定義數(shù)據(jù)域struct BiTNode *lchild, *rchild; //定義左、右指針 }BiTNode, *BiTree;


這種結(jié)構(gòu)的鏈表由于有兩個指針,所以叫二叉鏈表
另外還可以增加指針指向該結(jié)點的雙親結(jié)點,那這時三個指針的鏈表就叫三叉鏈表

二叉樹的遍歷(遞歸)

遍歷,是指按照某條搜索路徑訪問樹中的每一個結(jié)點,使得每個結(jié)點均會被訪問一次,而且僅被訪問一次

由二叉樹的遞歸定義可知,遍歷一棵二叉樹首先需要確定對根結(jié)點、左子樹和右子樹的訪問順序。常見的遍歷次序有先序遍歷 (NLR) 、中序遍歷 (LNR) 和后序遍歷 (LRN) 三種。

遞歸先序遍歷

若二叉樹為空,則不采取操作,否則:

  • 訪問根結(jié)點;
  • 先序遍歷左子樹;
  • 先序遍歷右子樹。
  • 對應(yīng)的遞歸算法:

    void PreOrder(BiTree T){if(T != NULL){visit(T); //訪問根結(jié)點PreOrder(T->lchild); //遞歸遍歷左子樹PreOrder(T->rchid); / /遞歸遍歷右子樹} }

    遞歸中序遍歷

    若二叉樹為空,則不進(jìn)行操作,否則:

  • 中序遍歷左子樹;
  • 訪問根結(jié)點;
  • 中序遍歷右子樹。
  • 對應(yīng)的遞歸算法:

    void InOrder(BiTree T){if(T != NULL){InOrder(T->lchild); //遞歸訪問左子樹visit(T); //訪問根結(jié)點InOrder(T->rchild); //遞歸訪問右子樹} }

    遞歸后序遍歷

    若二叉樹為空,則不進(jìn)行操作,否則:

  • 后序遍歷左子樹;
  • 后序遍歷右子樹;
  • 訪問根結(jié)點。
  • 對應(yīng)的遞歸算法:

    void PostOrder(BiTree T){if(T != NULL){PostOrder(T->lchild); //遞歸遍歷左子樹PostOrder(T->rchild); //遞歸遍歷右子樹visit(T); //訪問根結(jié)點} }

    二叉樹的遍歷(非遞歸)

    借助到棧

    非遞歸先序遍歷

    Void PreOrderTraverse(BiTree b){InitStack(S);BiTree p=b; //工作指針pwhile(p || !IsEmpty(S)){while(p){printf(“%c”,p->data); //先序先遍歷結(jié)點Push(S,p); //進(jìn)棧保存p=p->lchild; //指向左孩子}if(!IsEmpty(S)){p=Pop(S); //出棧p=p->rchild; //指向右孩子}} }

    例子:

    過程: p指向A,打印A, A進(jìn)棧, p指向B,打印B,B進(jìn)棧, p指向D,打印D,D進(jìn)棧 p指向NULL,棧不為空,彈出棧頂D,p指向D的右孩子G 打印G,G進(jìn)棧, p指向NULL,棧不為空,彈出棧頂G, p指向NULL,棧不為空,彈出棧頂B, p指向NULL,棧不為空,彈出棧頂A, p指向C,打印C,C進(jìn)棧, p指向E,打印E,E進(jìn)棧 p指向NULL,棧不為空,彈出棧頂E p指向NULL,棧不為空,彈出棧頂C, p指向F,打印F,F進(jìn)棧 p指向NULL,棧不為空,彈出棧頂F p指向NULL,棧空,結(jié)束 打印的序列為:A B D G C E F

    非遞歸中序遍歷

    Void PreOrderTraverse(BiTree b){InitStack(S);BiTree p=b; //工作指針pwhile(p || !IsEmpty(S)){while(p){ //中序先將結(jié)點進(jìn)棧保存Push(S,p);p=p->lchild;}//遍歷到左下角盡頭再出棧訪問p=Pop(S);printf(“%c”,p->data);p=p->rchild; //遍歷右孩子} }

    非遞歸后序遍歷

    Void PostOrderTraverse(BiTree b){InitStack(S);BiTree p=b,r=NULL; //工作指針p 輔助指針rwhile(p || !IsEmpty(S)){//1.從根結(jié)點到最左下角的左子樹都入棧if(p){Push(S,p);p=p->lchild;}//2.返回棧頂?shù)膬煞N情況else{GetTop(S,p); //取棧頂 注意不是出棧!//①右子樹還未訪問,而且右子樹不空,第一次棧頂if(p->rchild &&p->rchild !=r) p=p->rchild;//②右子樹已經(jīng)訪問或為空,接下來出棧訪問結(jié)點else{Pop(S,p);printf(“%c”,p->data);r=p; //指向訪問過的右子樹根結(jié)點p=NULL; //使p為空從而繼續(xù)訪問棧頂}}} }

    層序遍歷


    如圖所示為二叉樹的層次遍歷,即按照箭頭所指方向,按照層次1,2,3,4的順序,橫向遍歷對二叉樹中的結(jié)點進(jìn)行訪問。

    進(jìn)行層次遍歷需要借助隊列:先將二叉樹根結(jié)點入隊,然后出隊并訪問該結(jié)點,若有左子樹,則將左子樹根結(jié)點入隊;若有右子樹,則將右子樹根結(jié)點入隊。然后出隊并對出隊結(jié)點進(jìn)行訪問,重復(fù)上述操作直到隊列為空。
    思路:出隊→訪問→左右孩子入隊

    二叉樹的層次遍歷算法:

    void LevelOrder(BiTree T){InitQueue(Q); //創(chuàng)建空隊列 QBiTree p; //創(chuàng)建遍歷指針 pEnQueue(Q, T); //根結(jié)點入隊while(! IsEmpty Q){ //隊列非空則繼續(xù)循環(huán)進(jìn)行遍歷DeQueue(Q, p); //隊頭元素出隊visit(p); //訪問出隊結(jié)點if(p->lchild != NULL) //若左子樹存在EnQueue(Q, p->Lchild); //左子樹根結(jié)點入隊if(p->child != NULL) //若右子樹存在EnQueue(Q, p->rchild); //右子樹根結(jié)點入隊} }

    線索二叉樹


    二叉鏈表表示的二叉樹存在大量空指針
    N個結(jié)點的二叉鏈表,每個結(jié)點都有指向左右孩子的結(jié)點指針,所以一共有2N個指針,而N個結(jié)點的二叉樹一共有N-1條分支,也就是說存在2N-(N-1)=N+1個空指針。比如上圖二叉樹中有6個結(jié)點,那么就有7個空指針。

    大量空余指針能否利用起來?

    指向前驅(qū)和后繼的指針成為線索,加上線索的二叉鏈表就成為線索鏈表,相應(yīng)的二叉樹被稱為線索二叉樹
    對二叉樹以某種次序遍歷使其變?yōu)榫€索二叉樹的過程就叫做線索化

    如何區(qū)分指針是指向左孩子還是前驅(qū),或者指向右孩子還是后繼?

    在二叉鏈表結(jié)點的結(jié)構(gòu)基礎(chǔ)上增加兩個標(biāo)志位ltagrtag

    typedef struct ThreadNode{ElemType data;struct ThreadNode *lchild,*rchild;int ltag,rtag; }ThreadNode,*ThreadTree; //線索鏈表 lchildltagdatartagrchild

    ltag == 0 代表lchild指向該結(jié)點的左孩子
    ltag ==1 代表lchild指向該結(jié)點的前驅(qū)

    rtag == 0 代表rchild指向該結(jié)點的右孩子
    rtag ==1 代表rchild指向該結(jié)點的后繼

    void InThread(ThreadTree &p,ThreadTree &pre){ //中序遍歷對二叉樹線索化的遞歸算法if(p){InThread(p->lchild,pre);//修改前驅(qū)if(p->lchild==NULL){p->lchild=pre;p->ltag=1;}//修改后繼if(pre!=NULL&&pre->rchild==NULL){pre->rchild=p;pre->rtag=1;}pre=p;InThread(p->rchild,pre);} }

    遍歷線索二叉樹

    void InOrderTraverse(ThreadTree T){ThreadTree p=T;while(p){while(p->ltag==0)p=p->lchild;printf(“%c”,p->data);while(p->rtag==1&&p->rchild){p=p->rchild;printf(“%c”,p->data);}p=p->rchild;} }

    參考資料

    王道數(shù)據(jù)結(jié)構(gòu)

    總結(jié)

    以上是生活随笔為你收集整理的【数据结构】二叉树的存储和遍历的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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