??本文記錄了一些有關樹這種資料結構相對初階的知識,
??如果你是已經有一定的樹這種資料結構的基礎想知道為什么研究樹要重點研究二叉樹,可以去看目錄 13、樹、森林與二叉樹的轉化;如果你是和我一樣的小白想循序了解一些樹的有關知識,可以從頭看起,
文章目錄
- ?1.樹的定義
- ?2.結點的度 葉結點 樹的度
- 🍛1. 結點的度(Degree)
- 🍛2. 葉結點(Leaf)
- 🍛3. 樹的度
- ?3.結點間的關系
- ?4.樹的其他概念
- ?5.樹的存盤結構
- 🥘1. 雙親表示法
- 🥘2. 孩子表示法
- 🥘3. 孩子兄弟表示法
- ?6.二叉樹的存盤結構
- 🍲1.順序存盤
- 🍲2.鏈式存盤
- ?7.二叉樹的遍歷
- 🥫1.前序遍歷
- 🥫2.中序遍歷
- 🥫3.后序遍歷
- 🥫4.層次遍歷
- ?8. 二叉樹的遍歷演算法
- 🍜1.前序遍歷演算法
- 🍜2.中序遍歷演算法
- 🍜3.后序遍歷演算法
- ?9. 二叉樹遍歷結果的意義
- ?10. 二叉樹的建立
- ?11. 二叉樹的匯總
- ?12.線索二叉樹
- 🌳1.提出背景
- 🌳2.線索二叉樹的存盤結構
- 🌳3.中序序列線索二叉樹的建立
- 🎃3.1 建立二叉樹
- 🎃3.2 二叉樹線索化
- 🎃3.3 插入頭結點
- 🎃3.4 遍歷線索二叉樹
- 🌳4.前序序列線索二叉樹的建立
- 🌳5.后序序列線索二叉樹的建立(遍歷函式待研究)
- 🌳6. 線索二叉樹的匯總
- ?13.樹、森林與二叉樹的轉化
- 🌴1.樹轉化為二叉樹
- 🌴2.森林轉化為二叉樹
- 🌴3.二叉樹轉化為樹
- 🌴4.二叉樹轉化為森林
- 🌴5.樹與森林的遍歷
- 🔥5.1 樹的遍歷
- 🐱5.1.1 先根遍歷
- 🐱5.1.2 后根遍歷
- 🔥5.2 森林的遍歷
- 🐬5.2.1 前序遍歷
- 🐬5.2.2 后序遍歷
?1.樹的定義
??樹是n個節點的有限集,n=0時稱為空樹,在任意一顆非空樹中:
- 有且僅有一個特定的稱為根(Root)的結點;
- 當n>1時,其余結點可以分為m(m>0)個互不相交的有限集T1、T2、…、Tm,其中每個集合本身又是一顆樹,并且稱為根的子樹(SubTree),
??這個定義用了遞回的概念,在樹的知識中,這種定義方法非常常見,
?2.結點的度 葉結點 樹的度
🍛1. 結點的度(Degree)
??結點擁有的子樹數稱為結點的度,
🍛2. 葉結點(Leaf)
??度為0的結點.
🍛3. 樹的度
??樹的度是樹內部各結點的度的最大值,
?3.結點間的關系
??節點的子樹的根稱為節點的孩子(Child),相應的,該節點稱為孩子的雙親,
??同一個雙親的孩子之間互稱兄弟,
??結點的祖先是從根到該結點所經分支上的所有結點,
??以某結點為根的子樹中的任一節點都稱為該結點的子孫,
?4.樹的其他概念
結點的層次
如果將樹中結點的各子樹看成從左至右是有次序的,不能互換的,則稱該樹為有序樹,否則稱為無序樹,
森林是m課互不相交的樹的集合,
?5.樹的存盤結構
🥘1. 雙親表示法
//雙親表示法
#define MAXSIZE 100//最多的節點個數
typedef int TDatatype;//方便后續修改
//單個結點
typedef struct {
TDatatype data;//資料域
int parent;//雙親域
int firstchild;//長子
int rightsub;//右兄弟域
}Node;
//具體要怎么設計節點中的內容可以根據基于該存盤結構的運算是否合適是否方便來決定,
//整個樹的空間
typedef struct {
int root;//根的位置
int n;//節點數
Node nodes[MAXSIZE];
}PTree;
🥘2. 孩子表示法
#define MAXSIZE 100//本樹的最多的節點個數
//樹的孩子表示法
//首先用一個鏈表表示樹中一個結點的所有孩子節點
//這個鏈表的節點(非頭結點)
typedef struct NODE{
int child;
struct NODE* next;
}*childptr;
//這個鏈表的頭結點的內容是這個節點的資料和它的第一個孩子
typedef struct {
TDatatype data;
childptr firstchild;
}treeelement;
//把這個頭結點再組成一個陣列,
typedef struct {
treeelement nodes[MAXSIZE];
int root;
int n;//節點數
}CTree;
🥘3. 孩子兄弟表示法
/孩子兄弟表示法
//觀察發現,對于一個二叉樹來說,他的長子和右兄弟如果存在那么一定唯一 所以我們可以這樣設計
typedef struct CBNode {
TDatatype data;
CBNode* firstchild;
CBNode* rightbro;
CBNode* parent;//加這個方便搜索自己的雙親
}CBTnode,*CBTree;
//這樣的好處是我們把一個復雜的書變成了一棵二叉樹 就可以充分的利用二叉樹的性質來計算了
?6.二叉樹的存盤結構
🍲1.順序存盤
按照補全程對應完全二叉樹的號來存,不存在的位置則存空,由于太浪費空間,所以一般只用來存完全二叉樹,
🍲2.鏈式存盤
存自己的左孩子,右孩子,如果需要快速查雙親資訊則再加一個雙親域
//二叉樹的鏈式存盤
typedef struct BiTNode {
TDatatype data;
BiTNode* leftchild;
BiTNode* rightchild;
BiTNode* parent;
}BiTNode,*BiTree;
?7.二叉樹的遍歷
??二叉樹的遍歷是指從根節點出發,按照某種次序一次訪問二叉樹中的所有結點,使得每個節點被訪問一次且僅被訪問一次,
🥫1.前序遍歷
??規則是若二叉樹為空,則空操作并回傳;否則先訪問根節點,然后前序遍歷左子樹,再前序遍歷右子樹,

🥫2.中序遍歷
??規則是若樹為空,則空操作回傳;否則從根節點開始,中序遍歷根節點的左子樹,然后訪問根節點,然后中序遍歷右子樹,

🥫3.后序遍歷
??規則是若樹是空的,那么就空操作且回傳,否則從左到右先葉子后節點的方式遍歷左右子樹,最后是訪問根節點,

🥫4.層次遍歷
??從上到下,從左到右依次遍歷,

?8. 二叉樹的遍歷演算法
🍜1.前序遍歷演算法
void PreOrderTraverse(BiTree T)
{
if (T == NULL)
{
return;
}
printf("%d ", T->data);
PreOrderTraverse(T->leftchild);
PreOrderTraverse(T->rightchild);
}
🍜2.中序遍歷演算法
void InOrderTraverse(BiTree T)
{
if (T == NULL)
{
return;
}
InOrderTraverse(T->leftchild);
printf("%d ", T->data);
InOrderTraverse(T->rightchild);
}
🍜3.后序遍歷演算法
void PostOrderTraverse(BiTree T)
{
if (T == NULL)
{
return;
}
PostOrderTraverse(T->leftchild);
PostOrderTraverse(T->rightchild);
printf("%c ", T->data);
}
??這個所謂的序,就是實際“訪問”節點的順序在訪問結點對應左子樹 訪問結點對應的右子樹的前面、中間、后面,
?9. 二叉樹遍歷結果的意義
- 已知前序遍歷序列和中序遍歷序列,可以唯一確定一顆二叉樹,
- 已知中序遍歷序列和后續遍歷序列,可以唯一確定一顆二叉樹,
??前序和后序在本質上都是將父節點與子結點進行分離,但并沒有指明左子樹和右子樹的能力,(沒有說明到哪里左子樹遍歷結束,到哪里右子樹遍歷結束)因此得到這兩個序列只能明確父子關系,而不能確定一個二叉樹,
??已知前序排列或者已知后續排列某種程度上都是會知道根是誰,前序會知道誰是自己的左孩子,后序會知道誰是自己的右孩子,但它們都沒有嚴格指明左子樹遍歷到哪里結束,右子樹遍歷到哪里結束,而再加上中序排列會知道左子樹在哪里遍歷結束(因為會列印自己的節點),會知道右子樹在哪里開始遍歷(因為知道根),會知道右子樹在哪里結束(因為中序列印完右子樹會列印自己雙親節點,而這某種程度上是一種根,通過前序和后續都可以知道),因此我們知道了根在那和左右子樹在那開始在哪結束,然后每個子樹可以這樣遞回下去,因此就唯一確定了一顆子樹,
??已知前序遍歷序列和后序遍歷序列,無法唯一確定一顆二叉樹,
??如前序遍歷結果ABC,后序遍歷序列CBA

?10. 二叉樹的建立
??我們利用了一個原理,拓展二叉樹可以用先序或后序序列唯一確定一顆二叉樹,而中序不可以
Proof:
??所謂拓展二叉樹,就是保證除’#‘結點外,每個節點都有左孩子和右孩子,如果缺少左孩子或右孩子,就用’#'補上,
??對于先序排列,我根據先序排列會知道誰是根,誰是我的左孩子,加上拓展二叉樹后,我會知道左子樹在哪里結束(因為結尾肯定是##),也就知道了右子樹在哪開始(即右孩子的位置),然后也會知道右子樹的結束位置(##),以此遞回,我就會唯一確定整顆子樹,
??對于中序排列,其實我的拓展二叉樹已經交待了區分左右子樹的方法,但是你不能告訴我根,所以我們確定不了
??對于后序排列,我根據后序排列知道誰是根,誰是我的右孩子,加上了拓展二叉樹后,我會知道右子樹在哪里結束(結尾肯定是從右往左數經過兩個##),也就知道了左子樹在哪里開始(即左孩子的位置),也會知道左子樹結束的位置(##),以此遞回,我就會唯一確定整顆二叉樹,
所以我們考慮用拓展二叉樹的先序排列創建一棵二叉樹,
int index = 0;//用一個全域變數來表示遍歷到那個序列的哪個位置了
void CreateBiTree(BiTree* T,char* arr)
{
char ch = arr[index++];
if (ch == '#')
{
*T = NULL;
}
else
{
BiTree tmp = (BiTree)malloc(sizeof(BiTNode));
if (tmp == NULL)
{
printf("malloc fault\n");
exit(-1);
}
*T = tmp;
(*T)->data = ch;
CreateBiTree(&((*T)->leftchild),arr);
CreateBiTree(&((*T)->rightchild),arr);
}
}
?11. 二叉樹的匯總
//BiTree.h
//二叉樹的鏈式存盤
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 1000
typedef char TDatatype;
typedef struct BiTNode {
TDatatype data;
struct BiTNode* leftchild;
struct BiTNode* rightchild;
struct BiTNode* parent;
}BiTNode,*BiTree;
//前序遍歷演算法
void PreOrderTraverse(BiTree T);
//中序遍歷演算法
void InOrderTraverse(BiTree T);
//后續遍歷演算法
void PostOrderTraverse(BiTree T);
//利用拓展二叉樹(#法)的先序排列確定一棵二叉樹
void CreateBiTree(BiTree* T,char* arr);
//int index = 0;//用來表示這個先序序列走到哪一位了
//BiTree.c
#include "BiTree.h"
void PreOrderTraverse(BiTree T)
{
if (T == NULL)
{
return;
}
printf("%c ", T->data);
PreOrderTraverse(T->leftchild);
PreOrderTraverse(T->rightchild);
}
void InOrderTraverse(BiTree T)
{
if (T == NULL)
{
return;
}
InOrderTraverse(T->leftchild);
printf("%c ", T->data);
InOrderTraverse(T->rightchild);
}
void PostOrderTraverse(BiTree T)
{
if (T == NULL)
{
return;
}
PostOrderTraverse(T->leftchild);
PostOrderTraverse(T->rightchild);
printf("%c ", T->data);
}
int index = 0;//用一個全域變數來表示遍歷到那個序列的哪個位置了
void CreateBiTree(BiTree* T,char* arr)
{
char ch = arr[index++];
if (ch == '#')
{
*T = NULL;
}
else
{
BiTree tmp = (BiTree)malloc(sizeof(BiTNode));
if (tmp == NULL)
{
printf("malloc fault\n");
exit(-1);
}
*T = tmp;
(*T)->data = ch;
CreateBiTree(&((*T)->leftchild),arr);
CreateBiTree(&((*T)->rightchild),arr);
}
}
/
#include "BiTree.h"
void test1()
{
BiTree testree;
printf("請輸入這棵二叉樹的拓展二叉樹(#法)的先序排列\n");
char arr[2 * MAXSIZE + 2] = { 0 };
scanf("%s", arr);
CreateBiTree(&testree,arr);
PreOrderTraverse(testree);
printf("\n");
InOrderTraverse(testree);
printf("\n");
PostOrderTraverse(testree);
}
int main()
{
test1();
return 0;
}
?12.線索二叉樹
🌳1.提出背景
typedef char TDatatype;
typedef struct BiTNode {
TDatatype data;
struct BiTNode* leftchild;
struct BiTNode* rightchild;
struct BiTNode* parent;
}BiTNode,*BiTree;
??觀察我們的二叉樹的節點結構,其實有很多空間都被我們浪費了,因為有的節點沒有左孩子,有的結點沒有右孩子,葉結點沒有左右孩子,我們是否可以利用一下空閑的空間呢,比如我們讓空閑的左孩子指標指向創建當前二叉樹所用遍歷序列的前繼,讓空閑的右孩子指標指向創建當前二叉樹的遍歷序列的后繼,
??但是,我們還是有一些問題,我們怎么確定這個右(左)孩子指標指的是自己的右(左)孩子還是后繼(前繼)呢?
??我們可以再創建兩個布爾量,0表示此指標是正常指向自己的孩子的,1表示此指標是指向自己的后繼或前繼,為了方便,干碎用一個列舉變數,Link(鏈)表示此指標是指向自己孩子的鏈,Thread表示此指標是指向自己前繼或后繼的線,
🌳2.線索二叉樹的存盤結構
typedef enum {
Link,
Thread
}PointerTag;
typedef struct BiThrNode {
TDatatype data;
struct BiThNode* leftchild;
struct BiThNode* rightchild;
PointerTag LTag;
PointerTag RTag;
}BiThrNode,*BiThrTree;
🌳3.中序序列線索二叉樹的建立
🎃3.1 建立二叉樹
int index1 = 0;
void createBiThrTree(BiThrTree* T, char* arr)
{
char ch = arr[index1++];
if (ch == '#')
{
*T = NULL;
}
else
{
BiThrTree tmp = (BiThrTree)malloc(sizeof(BiThrNode));
if (tmp == NULL)
{
printf("malloc fault\n");
exit(-1);
}
*T = tmp;
(*T)->data = ch;
(*T)->LTag = Link;
(*T)->RTag = Link;
createBiThrTree(&((*T)->leftchild), arr);
createBiThrTree(&((*T)->rightchild), arr);
}
}
🎃3.2 二叉樹線索化
extern BiThrTree pre;
//把二叉樹按中序遍歷序列線索化
void InThreading(BiThrTree p)
{
if (p)
{
InThreading(p->leftchild);
if (!p->leftchild)
{
p->LTag = Thread;
p->leftchild = pre;
}
if (pre!=NULL)
{
if (!pre->rightchild)
{
pre->RTag = Thread;
pre->rightchild = p;
}
}
pre = p;
InThreading(p->rightchild);
}
}
🎃3.3 插入頭結點
//為二叉樹插入頭結點
void addhead_IN(BiThrTree* T)
{
BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
head->LTag = Link;
head->RTag = Thread;
head->leftchild = (*T);
BiThrTree p = *T;
//先把中序遍歷的第一個結點的前繼指標指向頭結點
while (p->LTag == Link)
{
p = p->leftchild;
}
p->leftchild = head;
//下面我們讓p指向中序遍歷序列的最后一個結點
while (p->rightchild != NULL)
{
while (p->LTag == Link)
p = p->leftchild;
while (p->RTag == Thread && p->rightchild != NULL)
{
p = p->rightchild;
}
p = p->rightchild;//進入它的右子樹
}
p->rightchild = head;
p->RTag = Thread;
head->rightchild = p;
*T = head;
}
🎃3.4 遍歷線索二叉樹
//中序遍歷線索二叉樹
void InOrderTraverse_Thr(BiThrTree T)
{
BiThrTree p;
p = T->leftchild;
if (p == NULL)
{
printf("線索二叉樹為空\n");
return;
}
while (p != T)
{
while (p->LTag == Link)
p = p->leftchild;
printf("%c ", p->data);
while (p->RTag == Thread && p != T)
{
p = p->rightchild;
if (p == T)
{
break;
}
printf("%c ", p->data);
}
//到這里說明左子樹遍歷完了 該進入右子樹了
if (p != T)
{
p = p->rightchild;
}
}
}
🌳4.前序序列線索二叉樹的建立
extern BiThrTree pre1;
//按照前序遍歷序列線索化二叉樹
void PreThreading(BiThrTree p)
{
if (p != NULL)
{
if (p->leftchild == NULL)
{
p->LTag = Thread;
p->leftchild = pre1;
}
if (pre1 != NULL)
{
if (pre1->rightchild == NULL)
{
pre1->RTag = Thread;
pre1->rightchild = p;
}
}
pre1 = p;
if (p->LTag == Link)
{
PreThreading(p->leftchild);
}
if (p->RTag == Link)
{
PreThreading(p->rightchild);
}
}
}
//插頭結點
void addhead_Pre(BiThrTree* T)
{
BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
if (head == NULL)
{
printf("malloc fault\n");
return;
}
head->LTag = Link;
head->leftchild = *T;
head->RTag = Thread;
BiThrTree p = *T;
while (p->rightchild != NULL)
{
while (p->LTag==Link)
p = p->leftchild;
while (p->RTag == Thread && p->rightchild!=NULL)
p = p->rightchild;
}
p->RTag = Thread;
head->rightchild = p;
p->rightchild = head;
*T = head;
}
//前序遍歷線索二叉樹
void PreOrderTraverse_Thr(BiThrTree T)
{
BiThrTree p = T->leftchild;
printf("%c ", p->data);
while (p != T)
{
//printf("%c ", p->data);
while (p->LTag == Link)
{
p = p->leftchild;
printf("%c ", p->data);
}
while (p->RTag == Thread && p!=T)
{
p = p->rightchild;
if (p != T)
{
printf("%c ", p->data);
}
}
}
}
🌳5.后序序列線索二叉樹的建立(遍歷函式待研究)
extern BiThrTree pre2;
void PostThreading(BiThrTree p)
{
if (p)
{
PostThreading(p->leftchild);
PostThreading(p->rightchild);
if (p->leftchild == NULL)
{
p->LTag = Thread;
p->leftchild = pre2;
}
if (pre2 != NULL)
{
if (pre2->rightchild == NULL)
{
pre2->RTag = Thread;
pre2->rightchild = p;
}
}
pre2 = p;
}
}
void addhead_Post(BiThrTree* T)
{
BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
if (head == NULL)
{
printf("malloc fault\n");
return;
}
head->LTag = Link;
head->leftchild = *T;
head->RTag = Thread;
BiThrTree p = *T;
while (p->LTag == Link)
p = p->leftchild;
p->LTag = Thread;
p->leftchild = head;
head->rightchild = *T;
if ((*T)->rightchild == NULL)
{
(*T)->rightchild = head;
}
*T = head;
}
void PostOrderTraverse_Thr(BiThrTree T)
//開發失敗 寄了
{
BiThrTree p;
p = T->leftchild;
if (p == NULL)
{
printf("線索二叉樹為空\n");
return;
}
while (p != T)
{
while (p->LTag == Link)
p = p->leftchild;
printf("%c ", p->data);
while (p->RTag == Thread && p != T)
{
p = p->rightchild;
if (p == T)
{
break;
}
printf("%c ", p->data);
}
//到這里說明左子樹遍歷完了 該進入右子樹了
if (p != T)
{
p = p->rightchild;
}
}
}
🌳6. 線索二叉樹的匯總
//BiThrTree.h
#pragma once
//線索二叉樹
typedef enum {
Link,
Thread
}PointerTag;
typedef struct BiThrNode {
TDatatype data;
struct BiThNode* leftchild;
struct BiThNode* rightchild;
PointerTag LTag;
PointerTag RTag;
}BiThrNode,*BiThrTree;
void createBiThrTree(BiThrTree* T,char* arr);
void print(BiThrTree T);
//按照中序遍歷序列給把二叉樹線索化
BiThrTree pre;
void InThreading(BiThrTree p);
void addhead_IN(BiThrTree* T);
void InOrderTraverse_Thr(BiThrTree T);
//按照前序遍歷序列把二叉樹線索化
BiThrTree pre1;
void PreThreading(BiThrTree p);
void addhead_Pre(BiThrTree* T);
//中序遍歷線索二叉樹
void PreOrderTraverse_Thr(BiThrTree T);
//按照后序遍歷序列把二叉樹線索化
BiThrTree pre2;
//按照拓展二叉樹的后續遍歷序列創建二叉樹
void createBiThrTree_Post(BiThrTree* T, char* str);//待研究
void PostThreading(BiThrTree p);
void addhead_Post(BiThrTree* T);
void PostOrderTraverse_Thr(BiThrTree T);//待研究
//BiThrTree.c
#include "BiThrTree.h"
int index1 = 0;
void createBiThrTree(BiThrTree* T, char* arr)
{
char ch = arr[index1++];
if (ch == '#')
{
*T = NULL;
}
else
{
BiThrTree tmp = (BiThrTree)malloc(sizeof(BiThrNode));
if (tmp == NULL)
{
printf("malloc fault\n");
exit(-1);
}
*T = tmp;
(*T)->data = ch;
(*T)->LTag = Link;
(*T)->RTag = Link;
createBiThrTree(&((*T)->leftchild), arr);
createBiThrTree(&((*T)->rightchild), arr);
}
}
void print(BiThrTree T)
{
if (T == NULL)
{
return;
}
printf("%c ", T->data);
print(T->leftchild);
print(T->rightchild);
}
extern BiThrTree pre;
//把二叉樹按中序遍歷序列線索化
void InThreading(BiThrTree p)
{
if (p)
{
InThreading(p->leftchild);
if (!p->leftchild)
{
p->LTag = Thread;
p->leftchild = pre;
}
if (pre!=NULL)
{
if (!pre->rightchild)
{
pre->RTag = Thread;
pre->rightchild = p;
}
}
pre = p;
InThreading(p->rightchild);
}
}
//為二叉樹插入頭結點
void addhead_IN(BiThrTree* T)
{
BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
head->LTag = Link;
head->RTag = Thread;
head->leftchild = (*T);
BiThrTree p = *T;
//先把中序遍歷的第一個結點的前繼指標指向頭結點
while (p->LTag == Link)
{
p = p->leftchild;
}
p->leftchild = head;
//下面我們讓p指向中序遍歷序列的最后一個結點
while (p->rightchild != NULL)
{
while (p->LTag == Link)
p = p->leftchild;
while (p->RTag == Thread && p->rightchild != NULL)
{
p = p->rightchild;
}
p = p->rightchild;//進入它的右子樹
}
p->rightchild = head;
p->RTag = Thread;
head->rightchild = p;
*T = head;
}
//中序遍歷線索二叉樹
void InOrderTraverse_Thr(BiThrTree T)
{
BiThrTree p;
p = T->leftchild;
if (p == NULL)
{
printf("線索二叉樹為空\n");
return;
}
while (p != T)
{
while (p->LTag == Link)
p = p->leftchild;
printf("%c ", p->data);
while (p->RTag == Thread && p != T)
{
p = p->rightchild;
if (p == T)
{
break;
}
printf("%c ", p->data);
}
//到這里說明左子樹遍歷完了 該進入右子樹了
if (p != T)
{
p = p->rightchild;
}
}
}
extern BiThrTree pre1;
//按照前序遍歷序列線索化二叉樹
void PreThreading(BiThrTree p)
{
if (p != NULL)
{
if (p->leftchild == NULL)
{
p->LTag = Thread;
p->leftchild = pre1;
}
if (pre1 != NULL)
{
if (pre1->rightchild == NULL)
{
pre1->RTag = Thread;
pre1->rightchild = p;
}
}
pre1 = p;
if (p->LTag == Link)
{
PreThreading(p->leftchild);
}
if (p->RTag == Link)
{
PreThreading(p->rightchild);
}
}
}
//插頭結點
void addhead_Pre(BiThrTree* T)
{
BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
if (head == NULL)
{
printf("malloc fault\n");
return;
}
head->LTag = Link;
head->leftchild = *T;
head->RTag = Thread;
BiThrTree p = *T;
while (p->rightchild != NULL)
{
while (p->LTag==Link)
p = p->leftchild;
while (p->RTag == Thread && p->rightchild!=NULL)
p = p->rightchild;
}
p->RTag = Thread;
head->rightchild = p;
p->rightchild = head;
*T = head;
}
//前序遍歷線索二叉樹
void PreOrderTraverse_Thr(BiThrTree T)
{
BiThrTree p = T->leftchild;
printf("%c ", p->data);
while (p != T)
{
//printf("%c ", p->data);
while (p->LTag == Link)
{
p = p->leftchild;
printf("%c ", p->data);
}
while (p->RTag == Thread && p!=T)
{
p = p->rightchild;
if (p != T)
{
printf("%c ", p->data);
}
}
}
}
//int index2;
//void createBiThrTree_Post(BiThrTree* T, char* str)
//{
// char ch = str[index2++];
// if (ch == '#')
// {
// *T = NULL;
// }
// else
// {
// createBiThrTree_Post(&((*T)->leftchild), str);
// createBiThrTree_Post(&((*T)->rightchild), str);
// BiThrTree tmp = (BiThrTree)malloc(sizeof(BiThrNode));
// if (tmp == NULL)
// {
// printf("malloc fault\n");
// return;
// }
// tmp->data = ch;
// tmp->LTag = Link;
// tmp->RTag = Link;
// *T = tmp;
// }
//
//}
extern BiThrTree pre2;
void PostThreading(BiThrTree p)
{
if (p)
{
PostThreading(p->leftchild);
PostThreading(p->rightchild);
if (p->leftchild == NULL)
{
p->LTag = Thread;
p->leftchild = pre2;
}
if (pre2 != NULL)
{
if (pre2->rightchild == NULL)
{
pre2->RTag = Thread;
pre2->rightchild = p;
}
}
pre2 = p;
}
}
void addhead_Post(BiThrTree* T)
{
BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
if (head == NULL)
{
printf("malloc fault\n");
return;
}
head->LTag = Link;
head->leftchild = *T;
head->RTag = Thread;
BiThrTree p = *T;
while (p->LTag == Link)
p = p->leftchild;
p->LTag = Thread;
p->leftchild = head;
head->rightchild = *T;
if ((*T)->rightchild == NULL)
{
(*T)->rightchild = head;
}
*T = head;
}
//void PostOrderTraverse_Thr(BiThrTree T)
開發失敗 寄了
//{
// BiThrTree p;
// p = T->leftchild;
// if (p == NULL)
// {
// printf("線索二叉樹為空\n");
// return;
// }
// while (p != T)
// {
// while (p->LTag == Link)
// p = p->leftchild;
// printf("%c ", p->data);
// while (p->RTag == Thread && p != T)
// {
// p = p->rightchild;
// if (p == T)
// {
// break;
// }
// printf("%c ", p->data);
// }
// //到這里說明左子樹遍歷完了 該進入右子樹了
// if (p != T)
// {
// p = p->rightchild;
// }
// }
//}
//test.c
#include "BiThrTree.h"
void test2()
{
BiThrTree testtree;
printf("請輸入這棵二叉樹的拓展二叉樹(#法)的前序排列\n");
char arr[2 * MAXSIZE + 2] = { 0 };
scanf("%s", arr);
createBiThrTree(&testtree, arr);
print(testtree);
InThreading(testtree);
addhead_IN(&testtree);
printf("\n");
InOrderTraverse_Thr(testtree);
}
void test3()
{
BiThrTree testtree;
printf("請輸入這棵二叉樹的拓展二叉樹(#法)的前序排列\n");
char arr[2 * MAXSIZE + 2] = { 0 };
scanf("%s", arr);
createBiThrTree(&testtree, arr);
print(testtree);
PreThreading(testtree);
addhead_Pre(&testtree);
printf("\n");
PreOrderTraverse_Thr(testtree);
}
void test4()
{
BiThrTree testtree;
printf("請輸入這棵二叉樹的拓展二叉樹(#法)的前序排列\n");
char arr[2 * MAXSIZE + 2] = { 0 };
scanf("%s", arr);
createBiThrTree(&testtree, arr);
print(testtree);
PostThreading(testtree);
addhead_Post(&testtree);
}
int main()
{
test2();
test3();
test4();
return 0;
}
?13.樹、森林與二叉樹的轉化
??我們費了那么大力氣研究二叉樹不僅僅是因為二叉樹是一種很基礎的樹,更是因為樹和森林都可以在某種程度上轉化為二叉樹,并且樹、森林的前序遍歷、后序遍歷結果與我們轉化出來的二叉樹的前序遍歷、中序遍歷是一樣的,下面我們來介紹這塊的內容,
🌴1.樹轉化為二叉樹
步驟:
- 加線:在所有兄弟節點之間加一條連線,
- 去線:對于樹的每個節點,只保留它和第一個孩子結點的連線,洗掉它與其他孩子結點之間的連線,
- 層次調整:以樹的根節點為軸心,將整棵樹順時針旋轉一定的角度,使之結構層次分明,注意第一個孩子是二叉樹結點的左孩子,兄弟轉過來的孩子是結點的右孩子,
如圖:




🌴2.森林轉化為二叉樹
- 把每個樹轉化為二叉樹
- 第一棵樹不動,從第二棵二叉樹開始,依次把后一刻二叉樹的根節點作為前一棵二叉樹根節點的右孩子,用線連起來,
觀察到這種轉化轉化出來的二叉樹的根節點只有左孩子,
如圖:



🌴3.二叉樹轉化為樹
- 加線:若某結點的左孩子存在,在把這個左孩子的右孩子,左孩子的右孩子的右孩子,,,,左孩子的n層右孩子都作為此節點的孩子,并且連線連起來,
- 去線:洗掉原二叉樹中所有結點與其右孩子的連線,
- 次序調整,



🌴4.二叉樹轉化為森林
??首先要判斷一棵二叉樹是否能轉化為一棵森林,很簡單,只要看這棵二叉樹的根節點有沒有右孩子,如果有,那就是森林,如果沒有,就是一棵樹,
- 從根結點開始,如果右孩子存在,就把與右孩子結點的連線洗掉,
- 看去除后的二叉樹,重復第一步,直到沒有右孩子為止,
- 把每棵二叉樹轉化回普通的樹,



🌴5.樹與森林的遍歷
🔥5.1 樹的遍歷
🐱5.1.1 先根遍歷
??先訪問樹的根結點,然后依次先根遍歷根的每棵子樹,
🐱5.1.2 后根遍歷
??先依次后根遍歷每棵子樹,然后再訪問根結點,
🔥5.2 森林的遍歷
🐬5.2.1 前序遍歷
??先訪問森林中第一棵樹的根結點,然后再依次先根遍歷根的每棵子樹,然后再依次遍歷森林中的其他的樹,
🐬5.2.2 后序遍歷
??依次后根遍歷森林中的每棵子樹,
我們用個例子來說明樹的先根遍歷等價于轉化出來的二叉樹的前序遍歷、樹的后根遍歷等價于轉化出來的二叉樹的中序遍歷,


??再用一個例子說明森林的前序遍歷序列等價于轉化出的二叉樹的前序遍歷序列,森林的后序遍歷序列等價于轉化出的二叉樹的中序遍歷序列,


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/317921.html
標籤:其他
