c語言中二叉樹中總結點,C語言二叉樹的三種遍歷方式的實現及原理

二叉樹遍歷分為三種:前序、中序、后序,其中序遍歷最為重要。為啥叫這個名字?是根據根節點的順序命名的。

57d857b794cb23ac3b03bc7d7ee6a75a.png

比如上圖正常的一個滿節點,A:根節點、B:左節點、C:右節點,前序順序是ABC(根節點排最先,然后同級先左后右);中序順序是BAC(先左后根最后右);后序順序是BCA(先左后右最后根)。

ab81855b43334ce5e7db0cef1e19a2df.png

比如上圖二叉樹遍歷結果

前序遍歷:ABCDEFGHK

中序遍歷:BDCAEHGKF

后序遍歷:DCBHKGFEA

分析中序遍歷如下圖,中序比較重要(java很多樹排序是基于中序,后面講解分析)

f9138ec7719e6f6682ce892313c4f11f.png

下面介紹一下,二叉樹的三種遍歷方式,其中每一種遍歷方式都有三種實現方式。

節點定義:

struct TreeNode

{

int val;

TreeNode *left,*right;

TreeNode(int val){

this->val = val;

this ->left = this->right = NULL;

}

};

先序遍歷

904aaa8ce7e2ee8b636f9b22e26ca598.png

以上面這張圖為例:我們講講樹的三種遍歷方式:

先序遍歷:先訪問根節點,然后訪問左孩子,最后訪問右孩子。

所以,上面遍歷的結果是:GEDACHS。

下面,我們來看看具體代碼實現

1.遞歸實現

void preOrder(TreeNode *root){

if (root==NULL)

return;

cout<val<

preOrder(root->left);

preOrder(root->right);

}

2.使用輔助棧

實現思路:1.將根節點入棧

2.每次從棧頂彈出一個節點,訪問該節點

3.把當前節點的右孩子入棧

4.把當前節點的左孩子入棧

具體實現:

void preOrder2(TreeNode *root){

if (root == NULL)

return;

stack stk; //開辟一個棧空間

stk.push(root);

while(!stk.empty()){

TreeNode* pNode = stk.pop();

cout<val;

if (pNode->right!=NULL)

stk.push(pNode->right);

if (pNode->left!=NULL)

stk.push(pNode->left);

}

}

3.Morris遍歷

Morris遍歷,常數的空間即可在O(n)時間內完成二叉樹的遍歷。

O(1)空間進行遍歷困難之處在于在遍歷的子結點的時候如何重新返回其父節點?

在Morris遍歷算法中,通過修改葉子結點的左右空指針來指向其前驅或者后繼結點來實現的。

其本質:線索二叉樹(Threaded Binary Tree),通過利用葉子節點空的right指針,指向中序遍歷的后繼節點,從而避免了對 stack 的依賴。

具體實現:

void preOrder(TreeNode* root){

if (root == NULL)

return;

TreeNode* pNode = root;

while(pNode != NULL){

if (pNode->left == NULL)

{

cout<val<

pNode = pNode->right;

}

else{

TreeNode* pPre = pNode->left;

while(pPre->right != NULL && pPre->right != pNode){

pPre = pPre->right;

}

if (pPre->right == NULL)

{

/* code */

pPre->right = pNode;

cout<val<

pNode = pNode->left;

}

else{

pPre->right = NULL;

pNode = pNode->right;

}

}

}

}

中序遍歷

中序遍歷:先訪問左孩點,然后訪問根節點,最后訪問右孩子。

所以,上面遍歷的結果是:DEAGHCS。

下面,我們來看看具體代碼實現

1.遞歸實現

void InOrder(TreeNode *root){

if (root==NULL)

return;

InOrder(root->left);

cout<val<

InOrder(root->right);

}

2.使用輔助棧

實現思路:

初始化一個二叉樹結點pNode指向根結點;

若pNode非空,那么就把pNode入棧,并把pNode變為其左孩子;(直到最左邊的結點)

若pNode為空,彈出棧頂的結點,并訪問該結點,將pNode指向其右孩子(訪問最左邊的結點,并遍歷其右子樹)

具體實現:

void InOrder(TreeNode *root){

if (root==NULL)

{

return;

}

stack stk;

TreeNode *pNode = root;

while(pNode!=NULL || !stk.empty()){

if (pNode != NULL)

{

stk.push(pNode);

pNode = pNode->left;

}

else{

pNode = stk.pop();

stk.pop();

cout<val<

pNode = pNode->right;

}

}

}

3.Morris遍歷

實現思路:

1.如果當前節點pNode的左孩子為空,那么輸出該節點,并把該節點的右孩子作為當前節點

2.如果當前節點pNode的左孩子非空,那么找出該節點在中序遍歷的前驅結點prev

當第一次訪問該前驅結點prev時,其右孩子必定為空,那么就將其右孩子設置為當前結點,以便根據這個指針返回到當前結點pNode中,并將當前結點pNode設置為其左孩子;

當該前驅結點pPre的右孩子為當前結點,那么就輸出當前結點,并把前驅結點的右孩子設置為空(恢復樹的結構),將當前結點更新為當前結點的右孩子;

3.重復以上兩步,直到當前結點為空。

具體實現:

void InOrder(TreeNode *root){

if (root == NULL)

return;

TreeNode* pNode = root;

while(pNode != NULL){

if (pNode->left == NULL)

{

cout<val<

pNode = pNode->right;

}

else{

TreeNode* pPre = pNode->left;

while(pPre->right != NULL && pPre->right != pNode){

pPre = pPre->right;

}

if (pPre->right == NULL)

{

/* code */

pPre->right = pNode;

pNode = pNode->left;

}

else{

pPre->right = NULL;

cout<val<

pNode = pNode->right;

}

}

}

}

后序遍歷

后序遍歷:先訪問左孩子,然后訪問右孩子,最后訪問根節點。

所以,上面遍歷的結果是:DAEHSCG。

下面,我們來看看具體代碼實現:

1.遞歸實現

void PostOrder(TreeNode *root){

if (root==NULL)

return;

PostOrder(root->left);

PostOrder(root->right);

cout<val<

}

2.使用輔助棧

void postOrder(TreeNode *root) {

if(root == NULL)

return;

stack stk;

stk.push(root);

TreeNode *prev = NULL;

while(!stk.empty()) {

TreeNode *pNode = stk.top();

if(!prev || prev->left == pNode || prev->right == pNode) { // traverse down

if(pNode->left)

stk.push(pNode->left);

else if(pNode->right)

stk.push(pNode->right);

/* else {

cout << pNode->val << endl;

stk.pop();

}

*/

}

else if(pNode->left == prev) { // traverse up from left

if(pNode->right)

stk.push(pNode->right);

}

/* else if(pNode->right == prev) { // traverse up from right

cout << pNode->val << endl;

stk.pop();

}

*/

else {

cout << pNode->val << endl;

stk.pop();

}

prev = pNode;

}

}

雙輔助棧實現思路:

設置兩個棧stk, stk2;

將根結點壓入第一個棧stk;

彈出stk棧頂的結點,并把該結點壓入第二個棧stk2;

將當前結點的左孩子和右孩子先后分別入棧stk;

當所有元素都壓入stk2后,依次彈出stk2的棧頂結點,并訪問之。

第一個棧的入棧順序是:根結點,左孩子和右孩子;于是,壓入第二個棧的順序是:根結點,右孩子和左孩子。

因此,彈出的順序就是:左孩子,右孩子和根結點。

void PostOrder2(TreeNode *root){ //兩個棧實現

if (root == NULL)

return;

stack stk,stk2;

stk.push(root);

while(!stk.empty()){

TreeNode* pNode = stk.top();

stk.pop();

stk2.push(pNode);// 將根節點壓棧

if (pNode->left != NULL) // 如果左孩子不為空,則壓棧

{

stk.push(pNode->left);

}

if (pNode->right != NULL) // 如果左孩子不為空,則壓棧

{

stk.push(pNode->right);

}

}

while(!stk2.empty()){

cout<val<

stk2.pop();

}

}

3.Morris遍歷實現

實現思路:

1.先建立一個臨時結點dummy,并令其左孩子為根結點root,將當前結點設置為dummy;

2.如果當前結點的左孩子為空,則將其右孩子作為當前結點;

3.如果當前結點的左孩子不為空,則找到其在中序遍歷中的前驅結點,

-如果前驅結點的右孩子為空,將它的右孩子設置為當前結點,將當前結點更新為當前結點的左孩子;

-如果前驅結點的右孩子為當前結點,倒序輸出從當前結點的左孩子到該前驅結點這條路徑上所有的結點。將前驅結點的右孩子設置為空,將當前結點更新為當前結點的右孩子。

4.重復以上過程,直到當前結點為空。

具體實現:

void reverse(TreeNode* p1,TreeNode *p2){

if (p1 == p2)

return;

TreeNode* x = p1;

TreeNode* y = p1->right;

while(true){

TreeNode* tmp = y->right;

y->right = x;

x = y;

y = tmp;

if (x == p2)

break;

}

}

void printReverse(TreeNode* p1,TreeNode *p2){

reverse(p1,p2);

TreeNode* pNode = p2;

while(true){

cout<val<

if (pNode == p1)

break;

pNode = pNode->right;

}

reverse(p2,p1);

}

void PostOrder3(TreeNode* root){

if(root == NULL)

return;

TreeNode *dummy = new TreeNode(-1);

dummy->left = root;

TreeNode *pNode = dummy;

while(pNode != NULL) {

if(pNode->left == NULL)

pNode = pNode->right;

else {

TreeNode *pPrev = pNode->left;

while(pPrev->right != NULL && pPrev->right != pNode)

pPrev = pPrev->right;

if(pPrev->right == NULL) {

pPrev->right = pNode;

pNode = pNode->left;

}

else {

printReverse(pNode->left, pPrev);

pPrev->right = NULL;

pNode = pNode->right;

}

}

}

}

總結

上述三種遍歷方式時間復雜度和空間復雜度分析:

1.遞歸遍歷和非遞歸遍歷 時間復雜度0(n) 空間復雜度O(n)

2.Morris遍歷 時間復雜度0(n) 空間復雜度O(1)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275473.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275473.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275473.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

動態庫的創建與使用

1、動態庫文件的創建 &#xff08;1&#xff09;編寫源文件 &#xff08;2&#xff09;編譯生成動態庫 g -fPIC -shared -o libfile_operation.so file_operation.cpp 此編譯過程分為兩步&#xff0c;等同于下面的兩個命令&#xff1a; g -c -fPIC file_operation.cpp …

ux設計中的各種地圖_UX寫作中的移情

ux設計中的各種地圖Demetri Martin is a master of comedic situations. If you’ve never seen Demetri Martin是喜劇情境的大師。 如果你從未見過 him before, he has a sort of dry brand of observational humor, relying more on anecdotes than full stories, and often…

字符串搜索。HOJ1530 Compound Words。

stl set實現字符串搜索。。效率一般。(附二分搜索。) Compound WordsTime limit:1sec.Submitted:233Memory limit:32MAccepted:81Source: Waterloo ACM Programming Contest Sep 28, 1996 You are to find all the two-word compound words in a dictionary. A two-word compo…

字節3-1前端面試官自學Vue的正確姿勢

大家好&#xff0c;我是若川。前不久和一個字節前端TL朋友聊天&#xff0c;說到大廠前端供需脫節的情況。特別是使用Vue框架的&#xff0c;因為簡單易學好上手&#xff0c;但是能夠深入理解的人并不多&#xff0c;大多都只停留在應用層面&#xff0c;缺乏更深層面的理解。尤其是…

android視圖工具,android studio的HierarchyViewer工具如何知道android屏幕的視圖屬性

讓我們首先看看adb是如何組織的.它有3個主要組件,如here所述 –> client – 在用于開發的機器上運行的客戶端.通過發出adb命令從shell調用客戶端.層次結構查看器還會創建adb客戶端.> server – 在開發計算機上作為后臺進程運行的服務器.它將從adb客戶端發出的命令傳遞給a…

云時代架構讀后感4--IT架構的本質

IT架構的本質 原文地址&#xff1a;http://mp.weixin.qq.com/s?__bizMzAwNTQ4MTQ4NQ&mid2453562304&idx1&snbe86a7bc682c4e76e06b87a10ad45188&chksm8cd136a2bba6bfb430103e50f94b670e799412d0a1cae4eded0eb901847b6d462359ae317635&mpshare1&scene23…

蘋果風格ui_蘋果如何使Soft-UI成為未來

蘋果風格ui重點 (Top highlight)Apple announced some pretty wild updates at WWDC 2020 today.蘋果今天在WWDC 2020上宣布了一些相當瘋狂的更新。 But technology aside, let’s focus on how their UI has changed. It went through the first bitmap representations, thr…

【數據結構】量子危機

問題 宇宙時間公元 5.55 億年&#xff0c;由于某種原因兩大聯盟展開了激戰&#xff08;maxingc 聯盟采用了微子技術&#xff09;&#xff1a; 邪惡的 maxingc 聯盟采集好了微子能&#xff0c;就要運輸。Maxingc 聯盟的領袖 xc 此時才發現&#xff0c;自己的軍事基地中由微子發射…

android 自定義menu背景,Android編程實現自定義系統菜單背景的方法

本文實例講述了Android編程實現自定義系統菜單背景的方法。分享給大家供大家參考&#xff0c;具體如下&#xff1a;不多說&#xff0c;上圖&#xff0c;見代碼。package lab.sodino.menutest;import android.content.Context;import android.app.Activity;import android.os.Bu…

面試官問 async、await 函數原理是在問什么?

大家好&#xff0c;我是若川。這是 源碼共讀活動《1個月&#xff0c;200人&#xff0c;一起讀了4周源碼》 第四期&#xff0c;紀年小姐姐的第四次投稿。紀年小姐姐通過本次學習提早接觸到generator&#xff0c;協程概念&#xff0c;了解了async/await函數的原理等。第四期是 學…

一步步優化JVM六:優化吞吐量[轉]

2019獨角獸企業重金招聘Python工程師標準>>> 原文&#xff1a;http://ganlv.iteye.com/blog/1571315 參考&#xff1a;http://www.myexception.cn/software-architecture-design/1455594.html 現代JVM是一個具有靈活適應各種應用能力的軟件&#xff0c;盡管很多應用…

element-ui 網格_UI備忘單:列表與網格

element-ui 網格重點 (Top highlight)Grids or lists? That is the question we will look at in this cheat sheet. While they can be used anywhere in your site, we are going to look primarily at search results, catalogs and newsfeeds. Making this choice will de…

asp.net mvc使用TagBuilder的應用程序集

在asp.net mvc編寫擴展方法中需要使用到TagBuilder類&#xff0c;根據msdn的說法應該應用System.Web.Mvc.dll 程序集。 TagBuilder 構造函數 .NET Framework 4 初始化 TagBuilder 類的新實例。命名空間&#xff1a; System.Web.Mvc程序集&#xff1a; System.Web.Mvc&#xf…

50行 koa-compose,面試常考的中間件原理原來這么簡單?

大家好&#xff0c;我是若川。源碼共讀《1個月&#xff0c;200人&#xff0c;一起讀了4周源碼》 活動第五期是學習 koa 源碼的整體架構&#xff0c;淺析koa洋蔥模型原理和co原理中的koa-compose源碼原理&#xff0c;閱讀不到50行的koa-compose源碼。這篇是izjing小哥哥的投稿。…

sqlite3源碼編譯到Android,實現SQLite跨全平臺使用

文&#xff0f;何曉杰Dev(高級Android架構師)著作權歸作者所有&#xff0c;轉載請聯系作者獲得授權。初看這個標題你可能會不解&#xff0c;SQLite 本身就是一個跨平臺的數據庫&#xff0c;在這里再說跨平臺有什么意義呢&#xff1f;其實不然&#xff0c;目前我就遇到了一個項目…

在Red Hat 4 AS U7上安裝oracle10gR2

軟件&#xff1a;Red Hat 4 AS U7, Oracle 10g R2 for linux32, VMWare 7, Windows 7詳細步驟清單&#xff1a;在Red Hat 4 AS U7上安裝oracle10gR2 1. 硬件需求&#xff1a; &#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1…

illustrator下載_平面設計:16個Illustrator快捷方式可加快工作流程

illustrator下載I know, I know — keyboard shortcuts sound so nerdy, and you’re a graphic designer, not an IT Director, why should you learn keyboard shortcuts?我知道&#xff0c;我知道—鍵盤快捷鍵聽起來很書呆&#xff0c;而且您是圖形設計師&#xff0c;而不是…

手把手教你五分鐘扒個源碼寫個無敵外掛

大家好&#xff0c;我是若川。源碼共讀《1個月&#xff0c;200人&#xff0c;一起讀了4周源碼》 活動進行到第五期了&#xff0c;歡迎點鏈接加我微信 ruochuan12 報名參加。前言前段時間群里分享了一個小游戲&#xff0c;多次懷疑自己的眼睛以后&#xff0c;嘗試去寫個外掛。中…

Kubernetes 1.14重磅來襲,多項關鍵特性生產可用

走過了突飛猛進的2018年&#xff0c;Kubernetes在2019年終于迎來了第一個大動作&#xff1a;Kubernetes 1.14版本的正式發布&#xff01;Kubernetes 本次發布的 1.14 版本&#xff0c;包含了 31 項增強&#xff0c;其中 10 項為 GA&#xff0c;12 項進入 beta 試用階段&#xf…

中英文

http://it.freesion.com/3220/4888028/13606306/#轉載于:https://www.cnblogs.com/yqskj/articles/2082326.html