樹型結構

来源:https://www.cnblogs.com/audrey-hall/archive/2022/03/24/16043442.html
-Advertisement-
Play Games

樹 定義 樹是遞歸定義的。 一棵樹是由n(n>0)個元素組成的有限集合,其中每個元素稱為結點(node),有一個特定的結點,稱為樹根(root),除根結點外,其餘結點能分成m(m>=0)個互不相交的有限集合T0,T1,T2,……Tm-1,其中的每個子集又都是一棵樹,這些集合稱為這棵樹的子樹。 如圖是 ...


定義

樹是遞歸定義的。

一棵樹是由n(n>0)個元素組成的有限集合,其中每個元素稱為結點(node),有一個特定的結點,稱為樹根(root),除根結點外,其餘結點能分成m(m>=0)個互不相交的有限集合T0,T1,T2,……Tm-1,其中的每個子集又都是一棵樹,這些集合稱為這棵樹的子樹。

如圖是一棵樹:

一棵樹中至少有1個結點,即根結點。

一個結點的子樹個數,稱為這個結點的度(如結點1的度為3,結點3的度為0)。

度為0的結點稱為葉結點(leaf)(如結點3、5、6、8、9)。

樹中各結點的度的最大值稱為這棵樹的度(此樹的度為3)。

上端結點為下端結點的父結點,稱同一個父結點的多個子結點為兄弟結點(如結點1是結點2、3、4的父結點,結點 2、3、4是結點1的子結點,它們又是兄弟結點)。

遍歷

樹結構解決問題時,按照某種次序獲得樹中全部結點的信息,這種操作叫作樹的遍歷。

先序(根)遍歷

先訪問根結點,再從左到右按照先序思想遍歷各棵子樹(如,上圖先序遍歷的結果為125634789)。

後序(根)遍歷

先從左到右遍歷各棵子樹,再訪問根結點(如,上圖後序遍歷的結果為562389741)。

層次遍歷

按層次從小到大逐個訪問,同一層次按照從左到右的次序(如,上圖層次遍歷的結果為123456789)。

葉結點遍歷

即從左到右遍歷所有葉節點(如,上圖葉節點遍歷的結果為56389)。

 

二叉樹

二叉樹是一種特殊的樹型結構,它是度數為2的樹,即二叉樹的每個結點最多有兩個子結點。

每個結點的子結點分別稱為左兒子、右兒子。

五種基本形態

性質

性質一

二叉樹的第i層最多有2i-1個結點(i>=1)(可用二進位性質解釋。)。

性質二

深度為k的二叉樹至多有2k–1個結點(k>=1)。

性質三

任意一棵二叉樹,如果其葉結點數為n0,度為2的結點數為n2,則一定滿足:n0=n2+1。

性質四

有n個結點的完全二叉樹的深度為floor(log2n)+1。

性質五

一棵n個結點的完全二叉樹,對任一個結點(編號為i),有:如果i=1,則結點i為根,無父結點;如果i>1,則其父結點編號為floor(i/2),如果i為父節點編號,那麼2*i是左孩子,2*i+1是右孩子。

圖A-滿二叉樹


圖B-完全二叉樹

編號示意圖

遍歷

二叉樹的遍歷是指按一定的規律和次序訪問樹中的各個結點。

遍歷一般按照從左到右的順序,共有3種遍歷方法,先(根)序遍歷,中(根)序遍歷,後(根)序遍歷。

先序遍歷

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

訪問根結點、先序遍歷左子樹、先序遍歷右子樹

void preorder(tree bt)//先序遞歸演算法
{
    if(bt)
    {  
        cout << bt->data;
        preorder(bt->lchild);
        preorder(bt->rchild);
    }
}

先序遍歷此圖結果為:124753689

中序遍歷

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

中序遍歷左子樹、訪問根結點、中序遍歷右子樹

void inorder(tree bt)//中序遍歷遞歸演算法
{
    if(bt)
    {  
        inorder(bt->lchild);
        cout << bt->data;
        inorder(bt->rchild);
    }
}

中序遍歷上圖結果為:742513869

後序遍歷

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

後序遍歷左子樹、後序遍歷右子樹、訪問根結點

void postorder(tree bt)//後序遞歸演算法
{
    if(bt)
    {  
        postorder(bt->lchild);
        postorder(bt->rchild);
        cout << bt->data;
    }
}

後序遍歷上圖結果為:745289631

已知先序序列和中序序列可唯一確定一棵二叉樹;

已知中序序列和後序序列可唯一確定一棵二叉樹;

已知先序序列和後序序列不可唯一確定一棵二叉樹;

二叉樹操作(建樹、刪除、輸出

普通樹轉二叉樹

由於二叉樹是有序的,而且操作和應用更廣泛,所以在實際使用時,我們經常把普通樹轉換成二叉樹進行操作。

通用法則:“左孩子,右兄弟”

建樹

刪除樹

插入一個結點到排序二叉樹中

在排序二叉樹中查找一個數

相關題目

擴展二叉樹

由於先序、中序和後序序列中的任一個都不能唯一確定一棵二叉樹,所以對二叉樹做如下處理,將二叉樹的空結點用“.”補齊,稱為原二叉樹的擴展二叉樹,擴展二叉樹的先序和後序序列能唯一確定其二叉樹。

現給出擴展二叉樹的先序序列,要求輸出其中序和後序序列。

輸入樣例:

ABD..EF..G..C..

輸出樣例:

DBFEGAC

DFGEBCA

 

 

二叉樹的建立和輸出

以二叉鏈表作存儲結構,建立一棵二叉樹,並輸出該二叉樹的先序、中序、後序遍歷序列、高度和結點總數。

輸入樣例:

12##3##  

//#為空

輸出樣例:

123 

//先序排列

213 

//中序排列

231 

//後序排列

2  

//高度

3  

//結點總數

 

 

因為本蒟蒻不太會用指針,所以自己寫了一個不帶指針的,代碼很醜,見諒QwQ

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

int top,maxh;
char s;

struct t{
	int data,father,lson=0,rson=0,h=0;
}tree[100005];

void build(int father,bool right){
	cin>>s;
	if(s=='\n')
	return;
	if(s!='#'){
		++top;
		int t=top;
		tree[t].father=father;
		tree[t].data=s-'0';
		tree[t].h=tree[father].h+1;
		maxh=max(tree[t].h,maxh);
		
		if(right==1)
		tree[father].rson=t;
		else
		tree[father].lson=t;
		
		build(t,0);
		build(t,1);
	}
	else return;
}

void xian(int now){
	cout<<tree[now].data;
	if(tree[now].lson!=0)
	xian(tree[now].lson);
	if(tree[now].rson!=0)
	xian(tree[now].rson);
}

void zhong(int now){
	if(tree[now].lson!=0)
	zhong(tree[now].lson);
	cout<<tree[now].data;
	if(tree[now].rson!=0)
	zhong(tree[now].rson);
}

void hou(int now){
	if(tree[now].lson!=0)
	hou(tree[now].lson);
	if(tree[now].rson!=0)
	hou(tree[now].rson);
	cout<<tree[now].data;
}

int main(){
	build(0,0);
//	for(int i=1;i<=top;i++){
//		cout<<tree[i].data<<' '<<tree[i].father<<' ';
//		cout<<tree[i].lson<<' '<<tree[i].rson<<' ';
//		cout<<tree[i].h<<endl;;
//	}
	xian(1);
	cout<<'\n';
	zhong(1);
	cout<<'\n';
	hou(1);
	cout<<'\n';
	cout<<maxh<<'\n'<<top<<'\n';
	return 0;
}

P1030 求先序排列

給出一棵二叉樹的中序與後序排列。求出它的先序排列。(約定樹結點用不同的大寫字母表示,長度<=8)。

輸入:

2行,均為大寫字母組成的字元串,表示一棵二叉樹的中序與 後序排列。

輸出:

1行,表示一棵二叉樹的先序。

輸入樣例:

BADC

BDCA

輸出樣例:

ABCD

分析
中序為BADC,後序為BDCA,所以A為根結點,B、DC分別為左右子樹的中序序列,B、DC分別為左右子樹的後序序列。然後再遞歸處理中序為B,後序為B的子樹和中序為DC,後序為DC的子樹。

自己用char數組寫的代碼QwQ

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
char mid[10],post[10];
//mid數組記錄中序排列,post數組記錄後序排列
//除了打暴力最好不要用string
int z[10],m[10],p[10];
//z數組做中轉使用,m數組記錄mid數組的內容,p數組記錄post數組的每一位在mid數組中的位置
void find(int start,int end,int kai,int jie){
//start和end記錄我們正在找的mid數組的範圍
//kai(開頭)和jie(結尾)記錄我們正在找的post數組的範圍
    if(start>end||kai>jie)return;
    //如果開頭大於結尾,就返回
    if(start==end||kai==jie){
        printf("%c",mid[p[jie]]);
        return;
    }
    //如果開頭等於結尾,那此節點一定沒有兒子,輸出當前節點並返回
    printf("%c",mid[p[jie]]); 
    //前面說過後序排列的最後一位就是當前樹的根節點,所以p[jie]就是根節點在mid數組中的位置
    //開頭小於結尾,那就輸出當前節點然後再去尋找此節點的左兒子和右兒子
    find(start,p[jie]-1,kai,kai+p[jie]-start-1);
    //求左子樹的範圍,然後遞歸尋找左兒子
    find(p[jie]+1,end,kai+p[jie]-start,jie-1);
    //求右子樹的範圍,然後遞歸尋找右兒子
}
int main(){
    scanf("%s%s",mid+1,post+1);
    //輸入時下標從1開始(主要是因為我比較毛病)
    int len=strlen(mid+1);
    //輸入時下標從1開始那麼計算字元串長度時也要加1
    for(int i=1;i<=len;i++){
        m[i]=mid[i]-'A'+1;
        //將每一位轉成數字以方便處理(是的,我很毛病)
        z[m[i]]=i;
        //z數組記錄m數組每一位的位置(這一步是為了方便後面記錄post數字)
    }
    for(int i=1;i<=len;i++){
        p[i]=z[post[i]-'A'+1];
        //記錄post數組的每一位在mid數組中的位置
        //z:我滴任務完成啦!
    }
    find(1,len,1,len);
    //開始遞歸
    return 0;
}

求後序排列

輸入:

二叉樹的前序序列與中序序列

輸出:

二叉樹的後序序列

樣例輸入:

abcdefg

cbdafeg

樣例輸出:

cdbfgea

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
char qian[100005],zhong[100005];
int q[100005],z[100005],a[100005],cnt=0;
void find(int start,int end){
	if(start>end){
		return;
	}
	cnt++;
	if(start==end){
		cout<<char(z[q[cnt]]+'a'-1);
		return;
	}
	int t=cnt;
	find(start,q[t]-1);
	find(q[t]+1,end);
	cout<<char(z[q[t]]+'a'-1);
}
int main(){
	cin>>qian>>zhong;
	int len=strlen(qian);
	for(int i=0;i<len;i++){
		a[zhong[i]-'a']=i;
	}
	for(int i=0;i<len;i++){
		z[i+1]=zhong[i]-'a'+1;
		q[i+1]=a[qian[i]-'a']+1;
	}
	find(1,strlen(qian));
	return 0;
}

因為有小可愛說我的代碼在輸入時的處理不清楚,所以又寫了一個版本QwQ

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
char qian[100005],zhong[100005];
int q[100005],z[100005],a[100005],cnt=0;
void find(int start,int end){
//	cout<<endl<<'*'<<start<<' '<<end<<'*'<<endl;
	if(start>end){
		return;
	}
	cnt++;
	if(start==end){
		cout<<char(z[q[cnt]]+'a'-1);
		return;
	}
	int t=cnt;
	find(start,q[t]-1);
	find(q[t]+1,end);
	cout<<char(z[q[t]]+'a'-1);
}
int main(){
//	cin>>qian+1>>zhong+1;
	scanf("%s%s",qian+1,zhong+1);//這裡的輸入下標從1開始
	int len=strlen(qian+1);
	for(int i=1;i<=len;i++){
		a[zhong[i]-'a']=i;
	}
	for(int i=1;i<=len;i++){
		z[i]=zhong[i]-'a'+1;
		q[i]=a[qian[i]-'a'];
	}
	find(1,len);
	return 0;
}

補充

表達式樹

關於表達式樹,我們可以分別用先序、中序、後序的遍歷方法得出完全不同的遍歷結果,如,對於下圖的遍歷結果如下,它們對應著表達式的3種表示方法。

-+a*b-cd/ef  (首碼表示、波蘭式)

a+b*(c-d)-e/f  (中綴表示)

abcd-*+ef/-  (尾碼表示、逆波蘭式)

哈夫曼樹

QwQ,不是很會,那就推薦一篇博客吧。

並非原創,僅是整理,請見諒

Lo問我為什麼看星星。我覺得銀河和代碼是同一種東西,這也是一種回答。————Co
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 從業軟體項目這麼多年,在企業應用開發項目中,項目能否成功,是否能按照項目計劃有效的推進,是有很強的一套項目管理理論。最早的時候,接觸的項目管理的方法論就是微軟的MSF(Microsoft Solutions Framework)。它是一個可以更快地成功交付技術解決方案的方法論,強調實施此項目方法需要 ...
  • 1 可靠性Reliability 容忍硬體故障、軟體失效、人為錯誤即使發生了錯誤,系統仍可用。針對可消除影響的故障類型。 硬體故障:硬體增加冗餘;軟體容錯容忍多機失效; 軟體失效: 特點:長期引而不發,特定條件觸發。 方案:全面考慮,充分測試,反覆評估,及時響應。 人為錯誤:簡化人為操作;分離易錯場 ...
  • 使用 sort() 或內建函數 sorted() 對列表進行排序。它們之間的區別有兩點: sort() 方法是對原列表進行操作,而 sorted() 方法會返回一個新列表,不是在原來的基礎上進行操作。 sort() 是應用在列表上的方法,而 sorted() 可以對所有可迭代的對象進行排序操作。 # ...
  • (JVM | 第2部分:虛擬機執行子系統) 前言 參考資料: 《深入理解 Java 虛擬機 - JVM 高級特性與最佳實踐》 第1部分主題為自動記憶體管理,以此延伸出 Java 記憶體區域與記憶體溢出、垃圾收集器與記憶體分配策略、參數配置與性能調優等相關內容; 第2部分主題為虛擬機執行子系統,以此延伸出 c ...
  • 需求:請輸出一個4行5列的星星(*)圖案。 結果: ***** ***** ***** ***** public class XunHuanQianTao { public static void main(String[] args) { //外層控制行數 內層控制列數 for(int x=0;x ...
  • 我還記得我讀高中的時候,班裡邊很多同學都在玩天天酷跑,那時候對天天酷跑特別沉迷。早上六點早讀,我可以晚上玩酷跑到半夜,現在不得不感嘆年輕真好。為了懷念過去的青春,今天來一篇酷跑教程。 寫出來的效果圖就是這樣了: 下麵就更新一下全部的代碼吧還是老樣子先定義 import pygame,sys impo ...
  • 上一版:重磅!JDK 17 發佈,Oracle 宣佈從 JDK 17 開始正式免費。。 JDK 18 正式發佈 JDK 17 剛發佈半年,JDK 18 又如期而至,JDK 版本號這算是成年了? JDK 18 發佈了,棧長繼續為大家解讀! JDK 18 延續了 JDK 17 開創的免費策略,但,JDK ...
  • (JVM | 第1部分:自動記憶體管理與性能調優) 前言 參考資料: 《深入理解 Java 虛擬機 - JVM 高級特性與最佳實踐》 第1部分主題為自動記憶體管理,以此延伸出 Java 記憶體區域與記憶體溢出、垃圾收集器與記憶體分配策略、參數配置與性能調優等相關內容; 第2部分主題為虛擬機執行子系統,以此延伸 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...