數據結構入門指南:二叉樹

目錄

文章目錄

前言

?1. 樹的概念及結構

? ?1.1 樹的概念

?1.2 樹的基礎概念

1.3 樹的表示

1.4 樹的應用

?2. 二叉樹

2.1 二叉樹的概念

?2.2 二叉樹的遍歷


前言

????????在計算機科學中,數據結構是解決問題的關鍵。而二叉樹作為最基本、最常用的數據結構之一,不僅在算法和數據處理中發揮著重要作用,也在日常生活中有著豐富的應用。無論是搜索引擎的索引算法、文件系統的組織方式,還是編譯器的語法分析,二叉樹都扮演著不可或缺的角色。為了便于大家更好的入門二叉樹,本期先向大家簡單介紹一下二叉樹的基本性質,以及代碼理解實現,給大家預預熱。


?1. 樹的概念及結構

? ?1.1 樹的概念

?????????樹是一種非線性的數據結構,它是由n(n>=0)個有限結點組成一個具有層次關系的集合。把它叫做樹是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。

  • 有一個特殊的結點,稱為根結點,根節點沒有前驅結點
  • 除根節點外,其余結點被分成M(M>0)個互不相交的集合T1、T2、……、Tm,其中每一個集合Ti(1<= i<= m)又是一棵結構與樹類似的子樹。每棵子樹的根結點有且只有一個前驅,可以有0個或多個后繼
  • 因此,樹是遞歸定義的。

?那這樣是樹還是非樹?

?

?答案是非樹,樹形結構中,子樹之間不能有交集,否則就不是樹形結構。

?1.2 樹的基礎概念

  • 節點的度:一個節點含有的子樹的個數稱為該節點的度; 如上圖:A的為6
  • 葉節點或終端節點:度為0的節點稱為葉節點; 如上圖:B、C、H、I...等節點為葉節點
  • *非終端節點或分支節點:度不為0的節點; 如上圖:D、E、F、G...等節點為分支節點
  • 雙親節點或父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點; 如上圖:A是B的父節點
  • 孩子節點或子節點:一個節點含有的子樹的根節點稱為該節點的子節點; 如上圖:B是A的孩子節點
  • 兄弟節點:具有相同父節點的節點互稱為兄弟節點; 如上圖:B、C是兄弟節點
  • 樹的度:一棵樹中,最大的節點的度稱為樹的度; 如上圖:樹的度為6
  • *節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
  • *樹的高度或深度:樹中節點的最大層次; 如上圖:樹的高度為4
  • *堂兄弟節點:雙親在同一層的節點互為堂兄弟;如上圖:H、I互為兄弟節點
  • *節點的祖先:從根到該節點所經分支上的所有節點;如上圖:A是所有節點的祖先
  • *子孫:以某節點為根的子樹中任一節點都稱為該節點的子孫。如上圖:所有節點都是A的子孫
  • *森林:由m(m>0)棵互不相交的樹的集合稱為森林;

帶星號的了解即可。

????????這里我們重點說一下樹的高度和節點層次,不同的數據結構書中一般有兩種方式表示樹的高度,一種是從0開始例如上述的樹,根節點A高度就是0,到P、Q高度就是3。另外一種是從1開始,根節點1的高度為1,那P、Q的高度就是4。個人更推薦使用從1開始的,如果使用從0開始的,那如果是空樹,在使用0表示就有點說不過去了,那空樹的高度就只能是-1了。如果使用從1開始的,那空樹就可以使用0來表示。

1.3 樹的表示

?????????樹結構相對線性表就比較復雜了,要存儲表示起來就比較麻煩了,既然保存值域,也要保存結點和結點之間的關系,實際中樹有很多種表示方式如:雙親表示法,孩子表示法、孩子雙親表示法以及孩子兄弟表示法等。

? ? ? ? ?首先在定義樹的節點時就很為難,一個節點到底要定義多少個指向子節點的指針:

struct TreeNode
{Datatype x;struct TreeNode* child1;struct TreeNode* child2;……};

?要想定義一個節點就要先知道一個樹的度,例如上述的樹:

?????????這棵樹的度為6,那我們定義時就要定義6個指針變量嗎?那大部分的節點的子節點并沒有達到6個,這樣就會很浪費,定義也很費勁。

在C++中,有這樣一種定義方法:

struct TreeNode
{Datatype x;vector<struct TreeNode*> childs;
};

?????????可以不規定個數,它使用數組的方式來存儲,如果不夠還可以進行增容,這種方式是使用順序表來存儲孩子節點的信息。

?????????還有一種非常巧的方式,叫孩子兄弟表示法,即左孩子右兄弟。

?????????拿這棵樹為例,這種方法在定義節點時就只定義兩個指針,一個指針叫左孩子指針,一個指針叫右兄弟指針。怎么指向呢?就是說無論一個節點有多少個孩子,它的孩子指針就只指向第一個孩子(最左邊的孩子節點),剩下的孩子用第一個孩子的兄弟指針指向第二個,第二個孩子的兄弟指針指向第三個。

表示出來就是這樣的結構:

?除此之外還有其他的表示法,這里就不再一一列舉。

1.4 樹的應用

?在現實生活中我們也經常使用到樹狀結構,例如:文件存儲

?2. 二叉樹

?? ? ? ? 了解完樹的基本概念后,我們接下來進入二叉樹的學習。

2.1 二叉樹的概念

?一棵二叉樹是結點的一個有限集合,該集合:

  1. 或者為空
  2. 由一個根節點加上兩棵別稱為左子樹和右子樹的二叉樹組成

?二叉樹的特點:

  1. 二叉樹不存在度大于2的結點
  2. 二叉樹的子樹有左右之分,次序不能顛倒,因此二叉樹是有序樹

?對于任何一顆二叉樹都是由一下幾種情況復合而成。

?????????當然關于二叉樹的基礎概念還有很多,今天就先簡單介紹,接下來給大家來點干貨,先讓大家切身體驗一下二叉樹。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

?2.2 二叉樹的遍歷

?當我們看到任何一顆二叉樹都應該把它分為三個部分:

  • 根節點
  • 左子樹
  • 右子樹

?我們以這棵樹為例進行分析:

?????????A為這棵樹的根節點,B及其以下的節點(D、E)被稱為左子樹,C及其以下節點被稱為右子樹。然后B子樹仍然可以分為D及其以下節點是左子樹,E及其以下節點是右子樹,然后再分,直到子節點為NULL,停止。

我們這里用的是分治算法。分治算法:

????????把大問題分成類似的子問題,子問題再分成子問題,……,直到子問題無法再分割為止。

遍歷可分為三種:

前序:也叫先根遍歷,遍歷順序為:根、左子樹、右子樹

中序:也叫中根遍歷,遍歷順序為:左子樹、根、右子樹

后序:也叫后根遍歷,遍歷順序為:左子樹、右子樹、根

?????????我們先來嘗試以下前序遍歷,理解了前序遍歷后兩個就簡單了。還是以這棵樹為例:

?????????前序遍歷,我們是先訪問根,然后是左子樹、右子樹。那應該先遍歷A,然后遍歷A的左子樹B(及其以下節點),然后就以B為根繼續遍歷它的左子樹D(及其以下節點),然后再次以D為根開始,遍歷左子樹(NULL),然后開始返回到D,D再遍歷右子樹(NULL),然后D(這個子樹)就遍歷完畢,返回到B,B開始遍歷右子樹E(及其以下節點),E左子樹為NULL返回到E,然后遍歷右子樹,右子樹也為空(E子樹遍歷完畢),然后返回E(B的右子樹遍歷完畢),接著返回B(B的所有子樹遍歷完畢),接著返回到A,A開始遍歷右子樹……這個規律很符合遞歸。

????????遍歷完的順序為:A、B、D、NULL、NULL、E、NULL、NULL、C、NULL、NULL。大部分學校講的都是A、B、D、E、C。大部分同學應該都知道這個規律,但不知道為什么這樣遍歷。

????????根據這個思路我們再來寫一下中序遍歷,中序遍歷先訪問左子樹,然后是跟,最后是右子樹。還是從A開始找,A的左子樹B,然后以B為根,找B的左子樹,接著以D為根,找D的左子樹,D的左子樹為NULL(D左子樹遍歷結束),返回到D(根),開始遍歷D的右子樹……

?????????最后遍歷的順序為:【NULL、D、NULL(B的左子樹)、B(根)、NULL、E、NULL? ? ? ? ? ? ? (B的右子樹)、】(A的左子樹)、A(根)、【NULL、C、NULL】(A的右子樹)。整理一下:

NULL、D、NULL、B、NULL、E、NULL、A、NULL、C、NULL(D、B、E、C、A)。

? ? ? ? ?我們接著寫一下后序遍歷:NULL、NULL、D、NULL、NULL、E、B、NULL、NULL、C、A(D、E、B、C、A)。

我們再來練一個:

?????????前序遍歷:A(根)||這部分為整體二叉樹的根、B(左子樹)、NULL(B的左子樹)、D(B的右子樹)、F(D的左子樹)、NULL(F的左子樹)、NULL(F的右子樹)、NULL(D的右子樹)||這部分屬于A的左子樹、C、E、NULL(E的左子樹)、NULL(E的右子樹)、NULL(C的右子樹)||這部分為A的右子樹。

?后續的中序遍歷和后續遍歷大家可以自己私下練一下。

????????好了我們已經了解了遍歷,接下來我們來說實現以下前序遍歷。給大家來點干貨,便于大家更好理解。

?我們依然以這個簡單的二叉樹為例進行實現。

?我們先定義一個二叉樹的節點:

typedef char Datatype;
typedef struct BinaryTreeNode
{Datatype data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;

?然后就是它的前序遍歷的實現:

void PrevOrder(BTNode* root)
{}

????????我們知道前序遍歷的順序是根,然后是左子樹,最后是右子樹。因此在開始前我們先判斷以下root是否為NULL,如果不為NULL我們就打印根節點的數據。?

void PrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);}

?????????那如何遍歷到左子樹、右子樹呢?其實很簡單,我們之前介紹的時候說:二叉樹的遍歷復合遞歸結構,這里我們就可以使用遞歸來完成遍歷,代碼如下:

void PrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}

?????????先遍歷左子樹,再遍歷右子樹,那調用的順序就先調用自己傳左孩子指針過去,以左孩子節點為根,再按照這個程序進行執行,再次傳左孩子指針過去……,知道左孩子為NULL,返回上一層,開始遍歷右子樹。

?我們可以簡單粗暴的測試以下,測試代碼如下:

#include<stdio.h>
#include<stdlib.h>typedef char Datatype;
typedef struct BinaryTreeNode
{Datatype data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;void PrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}
int main()
{BTNode* A = (BTNode*)malloc(sizeof(BTNode));A->data = 'A';A->left = NULL;A->right = NULL;BTNode* B = (BTNode*)malloc(sizeof(BTNode));B->data = 'B';B->left = NULL;B->right = NULL;BTNode* C = (BTNode*)malloc(sizeof(BTNode));C->data = 'C';C->left = NULL;C->right = NULL;BTNode* D = (BTNode*)malloc(sizeof(BTNode));D->data = 'D';D->left = NULL;D->right = NULL;BTNode* E = (BTNode*)malloc(sizeof(BTNode));E->data = 'E';E->left = NULL;E->right = NULL;A->left = B;A->right = C;B->left = D;B->right = E;PrevOrder(A);printf("\n");return 0;
}

執行結果:

?和上述分析的結果一致。

那剩下的中序遍歷和后序遍歷也很簡單,只需要改變一下遞歸調用函數的次序即可:

//中序遍歷
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}//后序遍歷
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);}

?我們也可以測試以下看看執行結果,測試代碼如下:

typedef struct BinaryTreeNode
{Datatype data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;void PrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);}
int main()
{BTNode* A = (BTNode*)malloc(sizeof(BTNode));A->data = 'A';A->left = NULL;A->right = NULL;BTNode* B = (BTNode*)malloc(sizeof(BTNode));B->data = 'B';B->left = NULL;B->right = NULL;BTNode* C = (BTNode*)malloc(sizeof(BTNode));C->data = 'C';C->left = NULL;C->right = NULL;BTNode* D = (BTNode*)malloc(sizeof(BTNode));D->data = 'D';D->left = NULL;D->right = NULL;BTNode* E = (BTNode*)malloc(sizeof(BTNode));E->data = 'E';E->left = NULL;E->right = NULL;A->left = B;A->right = C;B->left = D;B->right = E;printf("前序遍歷:");PrevOrder(A);printf("\n");printf("中序遍歷:");InOrder(A);printf("\n");printf("后序遍歷:");PostOrder(A);printf("\n");return 0;
}

?執行結果如下:

?可以和上邊的分析對比一下,沒有問題,


總結

????????二叉樹遍歷是學習和理解二叉樹的重要部分。通過遍歷,我們可以按照特定的順序訪問二叉樹的節點,從而深入了解它們的結構和關系。在這篇博客中,我們介紹了三種常見的二叉樹遍歷方式:前序遍歷、中序遍歷和后序遍歷,并對它們的原理、特點和應用進行了詳細討論。本期內容為預熱階段,先讓大家熟悉一下二叉樹,以便于后續二叉樹的學習,好的本期內容到此結束,感謝閱讀!

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

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

相關文章

java對大文件分片上傳

這里記錄一下&#xff0c;Java對大文件的切分&#xff0c;和后端接口分片上傳的實現邏輯 正常&#xff0c;前后端分離的項目其實是前端去切分文件&#xff0c;后端接口接收到切分后的分片文件去合并&#xff0c;這里都用java來記錄一下。特別說明&#xff1a;我這里用的是zip包…

vue+java實現在線播放mp4視頻

java: 讀取本地視頻文件的流然后給response的輸出流 File file new File("/Users/zhangqingtian/Documents/水庫/Floodforecast/static/" videoName);BufferedInputStream inputStream new BufferedInputStream(new FileInputStream(file));response.setContentT…

ReactDOM模塊react-dom/client沒有默認導出報錯解決辦法

import ReactDOM 模塊“"E:/Dpandata/Shbank/rt-pro/node_modules/.pnpm/registry.npmmirror.comtypesreact-dom18.2.7/node_modules/types/react-dom/client"”沒有默認導出。 解決辦法 只需要在tsconfig.json里面添加配置 "esModuleInterop": true 即…

【C++】queue容器

1.queue容器基本概念 2.queue常用接口 #include <iostream> using namespace std;//隊列queue #include<queue>//創建Person類 class Person { public:Person(string name, int age){this->m_Name name;this->m_Age age;}string m_Name; //姓名int m_Age; …

mysql創建新用戶并授權

目錄 前言登錄到mysql創建用戶用戶授權更改用戶密碼參考 前言 略 登錄到mysql shell> mysql -h127.0.0.1 -P3306 -uroot -p******創建用戶 mysql> CREATE USER abc% IDENTIFIED BY 123456;用戶授權 mysql> GRANT all privileges ON ruoyi.* TO abc%;用戶ruoyi擁有…

優維低代碼實踐:自定義模板

優維低代碼技術專欄&#xff0c;是一個全新的、技術為主的專欄&#xff0c;由優維技術委員會成員執筆&#xff0c;基于優維7年低代碼技術研發及運維成果&#xff0c;主要介紹低代碼相關的技術原理及架構邏輯&#xff0c;目的是給廣大運維人提供一個技術交流與學習的平臺。 優維…

禾賽科技Q2營收交付雙新高,國產激光雷達從量變到質變

隨著2022年激光雷達元年、2023年城市智能輔助駕駛&#xff08;NOA&#xff09;元年相繼到來&#xff0c;激光雷達產業迎來爆發期。 今年以來&#xff0c;自動駕駛公司、汽車制造商以及移動出行公司等各路人馬積極推動城市級別的智能輔助駕駛全面落地&#xff0c;北京、上海、深…

通過css設置filter 屬性,使整個頁面呈現灰度效果,讓整個網頁變灰

通過css設置filter 屬性設置頁面整體置灰 效果圖: 通過設置 filter 屬性為 grayscale(100%)&#xff0c;頁面中的所有元素都會被應用灰色濾鏡效果&#xff0c;使整個頁面呈現灰度效果。 <style type"text/css"> html { filter: grayscale(100%); -webkit-f…

git pull 某一個文件或文件夾

QQ: 2967732156 背景&#xff1a; 在使用Oracle VM VirtualBox&#xff0c;進行Linux開發時&#xff0c;隨著使用內存越來越少&#xff0c;空間已不足拉取整個代碼庫。 Ubuntu1604內存夠&#xff0c;Ubuntu18.04內存不夠。 思路&#xff1a; 第一步&#xff1a;從問題本身…

TB/TM-商品詳情原數據(APP)

一、接口參數說明&#xff1a; item_get_app-獲得TB/TMapp商品詳情原數據&#xff0c;點擊更多API調試&#xff0c;請移步注冊API賬號點擊獲取測試key和secret 公共參數 請求地址: https://api-gw.onebound.cn/taobao/item_get_app 名稱類型必須描述keyString是調用key&…

考研 408 | 【計算機網絡】 應用層

導圖 網絡應用模型 客戶/服務器&#xff08;c/s&#xff09;模型 P2P模型 DNS 域名 域名服務器 域名解析過程 文件傳輸協議FTP FTP服務器和用戶端 FTP工作原理 電子郵件 電子郵件的信息格式 組成結構 郵件服務器的功能&#xff1a; 1.發送&接收郵件 2.給發件人報告郵…

使用windows Api簡單驗證ISO9660文件格式,以及裝載和卸載鏡像文件

使用IIsoImageManager接口簡單驗證ISO鏡像文件正確性,使用AttachVirtualDisk裝載ISO鏡像文件,和使用DetachVirtualDisk卸載,(只支持windows 8及以上系統) 導讀 IIsoImageManager 驗證ISO文件正確性AttachVirtualDisk 裝載鏡像文件DetachVirtualDisk 卸載鏡像文件其他相關函…

《游戲編程模式》學習筆記(四) 觀察者模式 Observer Pattern

定義 觀察者模式定義了對象間的一種一對多的依賴關系&#xff0c;當一個對象的狀態發生改變時&#xff0c;所有依賴于它的對象都得到通知并被自動更新。 這是定義&#xff0c;看不懂就看不懂吧&#xff0c;我接下來舉個例子慢慢說 為什么我們需要觀察者模式 我們看一個很簡…

PAT (Advanced Level) 甲級 1004 Counting Leaves

點此查看所有題目集 A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child. Input Specification: Each input file contains one test case. Each case starts with a line containing 0<N<100, …

如何在iPhone手機上修改手機定位和模擬導航?

如何在iPhone手機上修改手機定位和模擬導航&#xff1f; English Location Simulator&#xff08;定位模擬工具&#xff09; 是一款功能強大的 macOS 應用&#xff0c;專為 iPhone 用戶設計&#xff0c;旨在修改手機定位并提供逼真的模擬導航體驗。無論是為了保護隱私、測試位…

Angular 獨立組件入門

Angular 獨立組件入門 如果你正在學習 Angular&#xff0c;那么你可能已經聽說過獨立組件&#xff08;Component&#xff09;。顧名思義&#xff0c;獨立組件就是可以獨立使用和管理的組件&#xff0c;它們能夠被包含在其他組件中或被其他組件引用。 在本文中&#xff0c;我們…

【Unity腳本開源】記錄鼠標按下的位置和移動的距離來進行物體的旋轉,并在鼠標釋放后將物體恢復到初始旋轉位置

??作者&#xff1a;白日參商 &#x1f935;?♂?個人主頁&#xff1a;白日參商主頁 ??堅持分析平時學習到的項目以及學習到的軟件開發知識&#xff0c;和大家一起努力呀&#xff01;&#xff01;&#xff01; &#x1f388;&#x1f388;加油&#xff01; 加油&#xff01…

go-安裝部署

一、安裝go 詳細安裝方式可以查看官網 # 下載 wget https://golang.google.cn/dl/go1.21.0.linux-amd64.tar.gz # 解壓縮 tar -xzf go1.21.0.linux-amd64.tar.gz # 遷移目錄 mv go /usr/local # 配置環境變量 export PATH$PATH:/usr/local/go/bin # 檢查go的版本 go version有…

Python中的字符串與字符編碼

Hello&#xff0c;這里是Token_w的博客&#xff0c;歡迎您的到來 今天文章講解的是Python中的字符串與字符編碼&#xff0c;其中有基礎的理論知識講解&#xff0c;也有實戰中的應用講解&#xff0c;希望對你有所幫助 整理不易&#xff0c;如對你有所幫助&#xff0c;希望能得到…

PDM/PLM系統建設

僅供學習使用&#xff0c;會隨時更新 工程機械跨生命周期數據管理系統 來源&#xff1a;清華大學 淺論企業PDM/PLM系統建設成功經驗 來源&#xff1a;e-works 作者&#xff1a;陳凡 https://articles.e-works.net.cn/pdm/article149572.htm 隨著“中國制造2025”強基工程戰略的…