樹和森林

Lkkkkkkkcy發表於2020-11-18


前言

在學了二叉樹,這種特殊的樹後,我們再回到一般的樹,討論他的儲存和遍歷


一、樹的儲存

實際在寫程式碼的時候,人們用到很多種辦法來儲存樹,以下我來介紹最常見的三種方法

1.雙親表示法

因為我們知道樹中的每一個節點都有唯一的雙親,我們正是利用這個特點來通過一個順序表儲存樹中的節點,每個節點中多加一個元素來儲存其雙親的節點,節點的結構如下:

dataparent

儲存結構定義為下:

typedef struct TNode{
	DataType data;
	int parent;
}TNode;
typedef struct
{
	TNode tree[MAX];
	int root;
	int num;
}PTree;

在這裡插入圖片描述
順序表的結構為:

DataParent
0A-1
1B0
2C0
3D0
4E1
5F1
6G3
7H3
8I3
9J6

    因為每一個節點都包含著他的雙親的資訊,所以用這個結構在反覆求節點的雙親的時候很方便,但是求一個節點的孩子節點的時候,就比較麻煩,必須遍歷整個表來找到節點的雙親值是當前的節點。

2.孩子表示法

    孩子表示法,就是把每個節點的孩子存到一個單連結串列中去,這個連結串列稱為“孩子連結串列”,每一個節點都對應一個孩子連結串列,沒有孩子的節點,對應的孩子連結串列為空,節點的資料和孩子的頭指標,我們還是用一個順序表來儲存。
儲存結構定義為下:

typedef struct ChildNode{
	//孩子連結串列節點結構的定義 
	int Child;
	struct 	ChildNode *next;
}ChildNode; 
typedef struct {
	DataType data;
	ChildNode * FirstChild;//用來儲存孩子連結串列的頭結點	
}DataNode;
typedef struct{
	//樹的定義 
	DataNode nodes[MAX]	;
	int root;//樹中根結點在順序表中的位置 
	int num;//樹中結點個數 
}CTree;

順序表的結構為:
在這裡插入圖片描述
    孩子表示法和上述的雙親表示法,我們聽名字就可以知道,這種儲存結構在找節點的孩子的時候十分方便,但是在找他的雙親的時候又有些麻煩,於是我們可以在這個順序表的每個結點加上一個雙親域形成帶雙親的孩子表示法。

順序表的結構為:
在這裡插入圖片描述

3.孩子兄弟表示法

    通過我們學習二叉樹,我們可以知道,一個結點可以包含指向的孩子的指標,這裡,我們就利用二叉樹的這個特點,把一個孩子的指標改為,指向該節點的右兄弟結點。
    如圖所示:
在這裡插入圖片描述

儲存結構定義為下:

typedef struct CSNode {
	DataType data;
	struct CSNode *FirstChild;//第一個孩子指標
	struct CSNode *NextSibling;//右兄弟指標
} CSNode,*CSTree;

    孩子兄弟結點是所講的三種儲存方式中最好操作的,他的本質就是二叉樹,只不過,他的右指標的所代表的含義從右孩子變成了他的兄弟,其他的與二叉樹相同,如果想找到某個結點的第n個孩子,可以先通過他的指標找到第一個孩子,然後通過第一個孩子的兄弟結點遍歷n-1次,此時得到的結點就是他的第n個孩子。
    接下來所講的樹,森林與二叉樹之間的轉化,就是基於這種結構。
話不多說,直接進入轉化。

二、樹,森林與二叉樹之間的轉化

    前面對樹的儲存進行了概述,接下來我們來看樹,森林與二叉樹之間的轉化
    首先要告訴大家的是,此時的二叉樹與樹是一 一對應的關係,給定一棵樹有其對應的唯一的二叉樹,同理,給定一個二叉樹,也有唯一對應的樹(或森林)與之對應。
對應的關係圖如下:
在這裡插入圖片描述

    二叉樹與樹轉化的實質就是,拿右指標為其兄弟,左指標為其孩子的二叉樹解釋成為一棵樹,本質是與二叉樹差不多的,只是他的右指標不再是他的右孩子,而是他的兄弟了,所以我們在解釋的時候一定要注意他的指向含義。
    根據我們對二叉樹的定義,我們知道,任何一棵樹對應的二叉連結串列的根節點的是沒有兄弟的,那麼如果我們遇到了,根節點有兄弟的我們應該如何理解呢?其實,這個時候,我們可以將森林中的各個樹的根節點,視為兄弟,這樣子,這個樹我們就可以解釋了,其實他是一個森林對應的二叉樹。他的根節點和他的第一個孩子視為是這個森林中的第一個樹,右節點則是森林中的其他的樹,這樣子我們對根節點有兄弟的二叉樹也可以做解釋。
在這裡插入圖片描述

1.樹轉化成二叉樹

轉化過程如下:
在這裡插入圖片描述

2.森林轉換為二叉樹

轉化過程如下:
在這裡插入圖片描述

3.二叉樹轉化成森林

轉化過程如下:
    相當於2的逆過程,對於每一個右節點已經不再是右孩子,而是該結點對應的兄弟,除此之外其餘正常

三、樹和森林的遍歷

1.樹的遍歷

    本質與二叉樹的遍歷相似,對根分為先後的遍歷,但是對於樹來說沒有中根遍歷。

(1)樹的遍歷

    ①先根遍歷:

    首先訪問根節點;
    然後從左到右依次根結點的每一棵子樹

    ②後根遍歷:

    首先從左到右依次根結點的每一棵子樹
    然後訪問根節點;

(2)樹的遍歷演算法

    樹的先根遍歷演算法①:

void RootFirst(CSTree root)
{
	if(root != NULL)
	{
		Visit(root->data)//訪問根結點
		p = root->FirstChild;
		while(p != NULL) //依次遍歷每一棵子樹 
		{
			RootFirst(p);//遞迴的呼叫進行遍歷 
			p = p->NextSibling;//指向下一個他的兄弟 
			
		}
	}
}

    樹的先根遍歷演算法②:

void RootFirst(CSTree root){
	if(root != NULL)
	{
		Visit(root->data)//訪問根結點
		RootFirst(root->FirstChild;);//先根遍歷第一個子樹 
		RootFirst(root->NextSibling);//先根遍歷他的兄弟樹 
	}
} 

    第二個遞迴的演算法與二叉樹的先序演算法更加的像,他的後根演算法只需在先根的演算法基礎上進行改動一下就可得到。

2.森林的遍歷

(1)先序遍歷:

    ①訪問森林中的第一棵樹的根結點
    ②先序訪問第一個樹的子樹森林
    ③先序訪問剩下第一棵樹的兄弟構成的森林

(1)中序遍歷:

    ①中序訪問第一個樹的子樹森林
    ②訪問森林中的第一棵樹的根結點
    ③中序訪問剩下第一棵樹的兄弟構成的森林


總結

    通過學習樹和森林,我們會發現其實樹和森林都是可以用二叉樹來表示的,並且遍歷的演算法也很相似,所以二叉樹是我們能夠理解樹和森林的關鍵。

相關文章