資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

一隻少年a發表於2022-11-30

二叉樹鏈式結構

前一篇部落格介紹了二叉樹的順序結構,是通陣列來儲存的,這裡我們透過建立鏈式結構來儲存,在堆上申請空間,結構如下:

template <class DateType>
struct BinaryTreeNode
{
	DateType data;//資料域
	BinaryTreeNode* leftChild;//左樹指標
	BinaryTreeNode* rightChild;//右樹種子很
};

二叉樹的建立

透過前序遍歷的陣列"ABD##E#H##CF##G##",給定一串字串,#代表的是空樹,其他的都是節點。

這裡我們只需要前序遍歷這串字串來構建這棵樹即可。
同樣是用遞迴來解決這個問題:
CreatBinaryTree(root->leftChild);讓當前這個節點的左指向給遞迴構建左子樹
CreatBinaryTree(root->rightChild);讓當前這個節點的右指向給遞迴構建左子樹,如果當前節點為空就直接返回NULL。

//建構函式
BinaryTree()
{
	cout << "請輸入根節點: " << endl;
	cout << "輸入#代表結點指標指向空" << endl;
	CreatBinaryTree(root);
	if (root != NULL)
	{
		cout << "root = " << root->data << endl;
	}
	else
	{
		cout << "The BinaryTree is empty" << endl;
	}
}
//建立二叉樹
void CreatBinaryTree(BinaryTreeNode<DateType>* &root)
{
	DateType ch;
	cin >> ch;
	if (ch == '#')
	{
		root = NULL;
	}
	else
	{
		root = new BinaryTreeNode<DateType>;
		root->data = ch;
		cout << "呼叫左孩子" << endl;
		CreatBinaryTree(root->leftChild);
		cout << "呼叫右孩子" << endl;
		CreatBinaryTree(root->rightChild);
	}
}

二叉樹的遞迴遍歷

前序遍歷(遞迴實現)

前序遍歷指的是先遍歷根,再遍歷左子樹,再遍歷右子樹。

思想: 二叉樹本身就是一種遞迴結構,所以透過遞迴來遍歷這棵樹,如何遞迴遍歷呢?
是這樣的,先遍歷根,再遍歷左子樹,左子樹又可以分解為,根、左子樹和右子樹,直到把所以左子樹的部分遍歷完,然後就遍歷右子樹,右子樹又可以分解為,根、左子樹和右子樹。

其實在計算機中,入棧的不是結點,而是函式,當進入函式入口的時候,函式就會入棧,此時如果在函式內呼叫了其他函式,計算機會標記當前函式執行到哪一步,隨後將呼叫的函式入棧,轉去執行棧頂的函式,當呼叫函式執行完畢之後,計算機返回當前函式會檢視標記執行到哪一步,繼續執行,直到函式徹底執行完畢,該函式出棧

但為了更直觀的理解這個過程,下面會透過圖解結點入棧的方式,描述二叉樹前序、中序和後序遍歷的過程,下面的程式不是以函式是否結束為結點出棧的標準,而是以是否訪問到該結點的data值”cout<< ->data“元素為出棧標準,更直觀

圖解前序遍歷的遞迴演算法

(1)假定給定的二叉樹結構如下,如何對二叉樹進行先序遍歷

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(2)首先將根節點傳入,A!=NULL,遍歷了A,並且指向A的指標入棧(遞迴的實現利用了棧),遍歷A的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(3)A的左子樹不為空,遍歷B,將B入棧,遍歷B的左子樹,同樣,B的左子樹不為空,遍歷D,將D入棧,遍歷D的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(4)D的左子樹為空,不遍歷,然後D出棧開始遍歷D的右子樹,但是D的右子樹也為空,不遍歷,故D的左右子樹以及本身都遍歷完

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(5)然後B出棧,遍歷B的右子樹,B的右子樹不為空,遍歷E,E入棧,遍歷E的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(6)E的左子樹不為空,遍歷G,G入棧,遍歷G的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(7)G的左子樹為空,不遍歷,G出棧遍歷G的右子樹,G的右子樹也為空,故不遍歷

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(8)此時棧中存在E和A,E出棧,遍歷E的右子樹,但是E的右子樹為空,故不進行遍歷

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(9)棧中僅存A,A出棧,A的右子樹不為空,遍歷A的右子樹,遍歷C,C入棧,遍歷C的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(10)C的左子樹為空,不遍歷,C出棧,遍歷C的右子樹,F不為空,F入棧

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(11)遍歷F的左子樹,為空,F出棧,遍歷F的右子樹,F的右子樹為空,不遍歷,此時棧沒空,結束遍歷,二叉樹的全部結點有且僅有一次被訪問

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

程式碼實現:

//遞迴前序遍歷
void BinaryTreePrevOrder(BinaryTreeNode<DateType>* root)
{
    // 遍歷到NULL就返回
	if (root == NULL)
	{
		return;
	}
	else
	{
		// 先遍歷根
		cout << root->data <<" ";
		// 左子樹交給BinaryTreePrevOrder這個函式去遍歷
		BinaryTreePrevOrder(root->leftChild);
		// 右子樹交給BinaryTreePrevOrder這個函式去遍歷
		BinaryTreePrevOrder(root->rightChild);
	}
}

中序遍歷(遞迴實現)

中序遍歷指的是先遍歷左子樹,再遍歷根,再遍歷右子樹。
思想: 二叉樹本身就是一種遞迴結構,所以透過遞迴來遍歷這棵樹,如何遞迴遍歷呢?
是這樣的,先遍歷左子樹,左子樹又可以分解為,左子樹、根和右子樹,直到把所以左子樹的部分遍歷完,然後就遍歷根,再遍歷右子樹,右子樹又可以分解為,左子樹、根和右子樹。

圖解前序遍歷的遞迴演算法

(1)將A入棧,遍歷A的左子樹,但是不遍歷A,因為訪問A的語句是在遍歷A的左子樹之後

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(2)A的左子樹不為空,B入棧,遍歷B的左子樹,同樣不遍歷B,B的左子樹不為空,D入棧,遍歷D的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(3)D的左子樹為空,不進行遍歷,D出棧並訪問D,接著遍歷D的右子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(4)D的右子樹為空,不遍歷,此時B出棧並且訪問B,然後遍歷B的右子樹,B的右子樹不為空,E入棧,遍歷E的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(5)E的左子樹不為空,G入棧,遍歷G的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(6)G的左子樹為空,不遍歷,G出棧並且訪問,接著遍歷G的右子樹,G的右子樹為空,不遍歷,隨後E出棧並且訪問,遍歷E的右子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(7)E的右子樹為空,不遍歷,隨後A出棧並且訪問,遍歷A的右子樹,A的右子樹不為空,C入棧

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(8)遍歷C的左子樹,C的左子樹為空,不遍歷,C出棧並訪問,遍歷C的右子樹,將F入棧,F是最後一個元素,出棧並訪問即可

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

程式碼實現:

//遞迴中序遍歷
void BinaryTreeInOrder(BinaryTreeNode<DateType>* root)
{
	// 遍歷到NULL就返回
	if (root == NULL)
	{
		return;
	}
	else
	{
		// 左子樹交給BinaryTreeInOrder這個函式去遍歷
		BinaryTreeInOrder(root->leftChild);
		// 遍歷根
		cout << root->data<<" ";
		// 右子樹交給BinaryTreeInvOrder這個函式去遍歷
		BinaryTreeInOrder(root->rightChild);
	}
}

後序遍歷(遞迴實現)

後序遍歷指的是先遍歷左子樹,再遍歷右子樹,最後遍歷根。
思想: 二叉樹本身就是一種遞迴結構,所以透過遞迴來遍歷這棵樹,如何遞迴遍歷呢?
是這樣的,先遍歷左子樹,左子樹又可以分解為,左子樹、右子樹和根,直到把所以左子樹的部分遍歷完,然後遍歷右子樹,右子樹又可以分解為,左子樹、右子樹和根,最後遍歷根。

圖解前序遍歷的遞迴演算法

(1)首先A入棧進行遍歷左子樹,A的左子樹不為空,B入棧,遍歷B的左子樹,B的左子樹不為空,D入棧,遍歷D的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(2)D的左子樹為空,不遍歷,D出棧遍歷D的右子樹,但由於在函式最後還需遍歷自身,故出棧後緊接著入棧

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(3)但由於D的右子樹為空,不遍歷,故D出棧並且訪問,至此D訪問完畢,然後B出棧訪問右子樹,緊接著入棧,準備執行最後的出棧訪問

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(4)B的右子樹不為空,E入棧,並遍歷E的左子樹,E的左子樹不為空,G入棧,並遍歷G的左子樹

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(5)G的左子樹為空,不遍歷,G出棧遍歷右子樹,緊接著入棧(此時還沒有訪問G本身),G的右子樹為空,不遍歷,此時G的左右子樹均遍歷完,G出棧訪問

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

(6)緊接著E出棧遍歷右子樹,緊接著入棧,為後續的出棧訪問自身做準備,E的右子樹為空,不遍歷,E的左右子樹遍歷完,E出棧訪問,此時棧中剩下B和A

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

.........................後續不再展示(懶了),最後的結果是

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

程式碼實現:

//遞迴後序遍歷
void BinaryTreePostOrder(BinaryTreeNode<DateType>* root)
{
	if (root == NULL)
	{
		return;
	}
	else
	{
		// 左子樹交給BinaryTreePostOrder這個函式去遍歷
		BinaryTreePostOrder(root->leftChild);
		// 右子樹交給BinaryTreePostOrder這個函式去遍歷
		BinaryTreePostOrder(root->rightChild);
		//遍歷根
		cout << root->data<<" ";
    }
}

層序遍歷

層序遍歷:設二叉樹的根節點所在層數為1,層序遍歷就是從所在二叉樹的根節點出發,首先訪問第一層的樹根節點,然後從左到右訪問第2層上的節點,接著是第三層的節點,以此類推,自上而下,自左至右逐層訪問樹的結點的過程就是層序遍歷。

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

層序遍歷用到的是佇列來解決,先將根入隊,然後把根取出,取出的同時分別再把不為空的左節點右節點入隊,直到佇列為空時就說明二叉樹已經遍歷完了。為了方便大家理解我在這裡做了個動圖演示一下:

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

程式碼實現:

//層序遍歷
void BinaryTreeLevelOrder(BinaryTreeNode<DateType>* root)
{
	if (root == NULL)
	{
		return;
	}
	queue<BinaryTreeNode<DateType>*> q;
	q.push(root);
	while (!q.empty())
	{
		BinaryTreeNode<DateType>* front = q.front();
		q.pop();
		cout << front->data << " ";
		if (front->leftChild)
			q.push(front->leftChild);
		if (front->rightChild)
			q.push(front->rightChild);
	}
}

二叉樹的非遞迴遍歷

中序遍歷(非遞迴實現)

中序遍歷的遞迴定義:先左子樹,後根節點,再右子樹。

假設,你面前有一棵二叉樹,現要求你寫出它的中序遍歷序列。如果你對中序遍歷理解透徹的話,你肯定先找到左子樹的最下邊的節點。那麼下面的程式碼就是理所當然的:

stack<BinaryTreeNode<DateType>*> Stack;
BinaryTreeNode<DateType>* Pointer = root;
//一直遍歷到左子樹最下邊,邊遍歷邊儲存根節點到棧中
while (Pointer)
{	
    Stack.push(Pointer);
	Pointer = Pointer->leftChild;
}

儲存一路走過的根節點的理由是:中序遍歷的需要,遍歷完左子樹後,需要藉助根節點進入右子樹,程式碼走到這裡,指標p為空,此時無非兩種情況:

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

二叉樹的左子樹,最下邊是不是上圖兩種情況?不管怎樣,此時都要出棧,並訪問該節點。這個節點就是中序序列的第一個節點。根據我們的思維,程式碼應該是這樣:

Pointer = Stack.top();
Stack.pop();
cout << Pointer->data << " ";

兩圖情形不同得區別對待:

1.情景一中訪問的是一個左孩子,按中序遍歷順序,接下來應訪問它的根節點。也就是圖中的另一個節點,高興的是它已被儲存在棧中。我們只需這樣的程式碼和上一步一樣的程式碼:

Pointer = Stack.top();
Stack.pop();
cout << Pointer->data << " ";

左孩子和根都訪問完了,接著就是右孩子了,接下來只需一句程式碼:Pointer = Pointer->rightChild;在右子樹中,又會新一輪的程式碼段直到棧空且p空

2.再看情景2,由於沒有左孩子,根節點就是中序序列中第一個,然後直接是進入右子樹:Pointer = Pointer->rightChild;在右子樹中,又會新一輪的程式碼段直到棧空且p空

Pointer = Stack.top();
Stack.pop();
cout << Pointer->data << " ";
Pointer = Pointer->rightChild;

看到這裡我們思考一個問題,這兩種情景的程式碼可以合成一個嗎?

資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

答案是可以的,我們回顧一下二叉樹,是不是每個結點都可以看成是根節點,由於是葉子節點,Pointer =Pointer->rightChild;Pointer之後肯定為空。為空,還需要執行while迴圈,遍歷它的左子樹嗎?顯然不需要。換句話說,就算情景一的程式碼變成下面這樣,加上Pointer = Pointer->rightChild,由於葉子結點右孩子一定是空,依舊會連續出棧兩次,那麼這兩種情況的程式碼不就可以進行統一嗎?

p = s.top();
s.pop();
cout << p->data;
p = p->rchild;
p = s.top();
s.pop();
cout << p->data;
p = p->rchild;

我們可以對這兩種情況的程式碼進行統一

Pointer = Stack.top();
Stack.pop();
cout << Pointer->data << " ";
Pointer = Pointer->rightChild;

中序遍歷非遞迴形式的完整程式碼是這樣的:

//非遞迴的中序遍歷
void InOrderWithoutRecusion()
{
	stack<BinaryTreeNode<DateType>*> Stack;
	BinaryTreeNode<DateType>* Pointer = root;
	while (!Stack.empty() || Pointer)
	{
		if (Pointer)
		{
			Stack.push(Pointer);
			Pointer = Pointer->leftChild;
		}
		else
		{
			Pointer = Stack.top();
			Stack.pop();
			cout << Pointer->data << " ";
			Pointer = Pointer->rightChild;
		}
	}
	cout << endl;
}

前序遍歷(非遞迴實現)

前序遍歷的遞迴定義:先根節點,後左子樹,再右子樹。

首先,我們遍歷左子樹,邊遍歷邊列印,並把根節點存入棧中,以後需藉助這些節點進入右子樹開啟新一輪的迴圈。

while(pointer)
{
	//訪問當前結點併入棧
	cout << pointer->data << " ";
	Stack.push(pointer);
	pointer = pointer->leftChild;//左孩子不空,一直向左
}

接下來就是:出棧,根據棧頂節點進入右子樹

while (!Stack.empty())
{
	//出棧,並轉向出棧結點的右子樹
	pointer = Stack.top();
	Stack.pop();
	pointer = pointer->rightChild;
}

不難寫出完整的前序遍歷的非遞迴寫法

//非遞迴的前序遍歷
void PreOrderWithoutRecusion()
{
	//初始化一個棧
	stack<BinaryTreeNode<DateType>*> Stack;
	BinaryTreeNode<DateType>* pointer = root;//pointer為遍歷指標
	//棧不為空或遍歷指標不為空時迴圈
	while (pointer || !Stack.empty())
	{
		//一路向左
		if (pointer)
		{
			//訪問當前結點併入棧
			cout << pointer->data << " ";
			Stack.push(pointer);
			pointer = pointer->leftChild;//左孩子不空,一直向左
		}
		else
		{
			//出棧,並轉向出棧結點的右子樹
			pointer = Stack.top();
			Stack.pop();
			pointer = pointer->rightChild;
		}
	}
	cout << endl;
}

後序遍歷(非遞迴實現)

後序遍歷遞迴定義:先左子樹,後右子樹,再根節點。

後序遍歷的難點在於:需要判斷上次訪問的節點是位於左子樹,還是右子樹。若是位於左子樹,則需跳過根節點,先進入右子樹,再回頭訪問根節點;若是位於右子樹,則直接訪問根節點。

故這裡我在程式碼中附加了一個標記(Left和Right)。如果該節點的左子樹已被訪問過則置標記為left;若右子樹被訪問過,則置標記為right。顯然,只有當節點的標記位是right時,才可訪問該節點;否則,必須先進入它的右子樹。

小夥伴們可以結合圖解以及註釋好好理解一下後序遍歷的思想和過程
資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)

//--------------- 非遞迴後續遍歷的標誌位-------------------
enum Tags
{
	Left, Right //Left為左標誌位,Right為右標誌位
};
//-----------------非遞迴後續遍歷的棧類--------------------
//自定義新的型別,將二叉樹結點和標記封裝在一起
template <class DateType>
class StackElement
{
public:
	BinaryTreeNode<DateType>* pointer;
	Tags tag;
};
//非遞迴的後續遍歷
void PostOrderWithoutRecusion()
{
	StackElement<DateType> element;
	//生成一個棧
	stack<StackElement<DateType>> Stack;
	BinaryTreeNode<DateType>* pointer;
	pointer = root;
	while (!Stack.empty() || pointer)
	{
		//這個while迴圈就是一路遍歷左子樹,
		while (pointer != NULL)
		{
			element.pointer = pointer;
			//該節點的左子樹被訪問
			element.tag = Left;
			Stack.push(element);
			pointer = pointer->leftChild;
		}
		element = Stack.top();
		Stack.pop();
		//左子樹被訪問過,則還需要進入右子樹
		pointer = element.pointer;
		if (element.tag == Left)
		{
			//更改標記
			element.tag = Right;
			//再次入棧
			Stack.push(element);
			//進入右子樹
			pointer = pointer->rightChild;
		}
		else//右子樹已經被訪問過,則可以訪問當前結點
		{
			cout << pointer->data << " ";
			//置空,再次出棧
			pointer = NULL;
		}
	}
	cout << endl;
}

二叉樹的節點個數和高度

二叉樹的節點個數

此問題可以分解為求左子樹節點個數+右子樹節點個數+1,然後左子樹節點個數又可以繼續分,所以這裡可以用遞迴來求解這個問題,下面我們就來實現一下這個介面

//二叉樹結點個數
int BinaryTreeSize(BinaryTreeNode<DateType>* root)
{
	return root == NULL ? 0 : BinaryTreeSize(root->leftChild) + BinaryTreeSize(root->rightChild) + 1;
}

二叉樹的葉子節點個數

問題可以分解為求左子樹葉子節點個數+右子樹葉子節點個數,也是一個遞迴的問題,這裡就直接實現了。

//二叉樹的葉子結點個數
int BinaryTreeLeafSize(BinaryTreeNode<DateType>* root)
{
	if (root == NULL)
		return 0;
	if (root->leftChild == NULL && root->rightChild == NULL)
		return 1;
	return BinaryTreeLeafSize(root->leftChild) + BinaryTreeLeafSize(root->rightChild);
}

二叉樹第k層節點個數

求解第k層節點個數其實就是求解左子樹的第k-1層節點個數+右子樹的第k-1層節點個數,當k==1時,就可以直接返回1,節點為空就返回0。

//二叉樹第K層結點個數
int BinaryTreeLevelKSize(BinaryTreeNode<DateType>* root, int k)
{
	if (k < 0)
		return -1;
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->leftChild, k - 1) + BinaryTreeLevelKSize(root->rightChild, k - 1);
}

二叉樹查詢值為x的節點

//二叉樹查詢值為x的結點
BinaryTreeNode<DateType>* BinaryTreeFind(BinaryTreeNode<DateType>* root, DateType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BinaryTreeNode<DateType>* leftRet = BinaryTreeFind(root->leftChild, x);
	if (leftRet)
		return leftRet;
	BinaryTreeNode<DateType>* rightRet = BinaryTreeFind(root->rightChild, x);
	if (rightRet)
		return rightRet;
}

二叉樹的銷燬

二叉樹的銷燬我們可以透過層序遍歷來把節點逐個釋放掉,由於與上面的層序遍歷很相似,這裡就不過多介紹了,直接上程式碼

//二叉樹的銷燬
void BinaryTreeDestory(BinaryTreeNode<DateType>* root)
{
	if (root == NULL)
	{
		return;
	}
	queue<BinaryTreeNode<DateType>*> q;
	q.push(root);
	while (!q.empty())
	{
		BinaryTreeNode<DateType>* front = q.front();
		q.pop();
		if (front->leftChild)
			q.push(front->leftChild);
		if (front->rightChild)
			q.push(front->rightChild);
		delete front;
		front = NULL;
	}
}

完整程式碼以及測試

#define _CRT_SECURE_NO_WARNINGS
#include<iostream> //引入標頭檔案
#include<string>//C++中的字串
#include<queue>
#include<stack>
using namespace std; //標準名稱空間
//--------------- 非遞迴後續遍歷的標誌位-------------------
enum Tags
{
	Left, Right //Left為左標誌位,Right為右標誌位
};
//-------------------二叉樹結點結構體----------------------
template <class DateType>
struct BinaryTreeNode
{
	DateType data;//資料域
	BinaryTreeNode* leftChild;//左樹指標
	BinaryTreeNode* rightChild;//右樹種子很
};
//-----------------非遞迴後續遍歷的棧類--------------------
template <class DateType>
class StackElement
{
public:
	BinaryTreeNode<DateType>* pointer;
	Tags tag;
};
//----------------------二叉樹類---------------------------
template <class DateType>
class BinaryTree
{
public:
	//建構函式
	BinaryTree()
	{
		cout << "請輸入根節點: " << endl;
		cout << "輸入#代表結點指標指向空" << endl;
		CreatBinaryTree(root);
		if (root != NULL)
		{
			cout << "root = " << root->data << endl;
		}
		else
		{
			cout << "The BinaryTree is empty" << endl;
		}
	}
	//建立二叉樹
	void CreatBinaryTree(BinaryTreeNode<DateType>* &root)
	{
		DateType ch;
		cin >> ch;
		if (ch == '#')
		{
			root = NULL;
		}
		else
		{
			root = new BinaryTreeNode<DateType>;
			root->data = ch;
			cout << "呼叫左孩子" << endl;
			CreatBinaryTree(root->leftChild);
			cout << "呼叫右孩子" << endl;
			CreatBinaryTree(root->rightChild);
		}
	}
	//遞迴前序遍歷
	void BinaryTreePrevOrder(BinaryTreeNode<DateType>* root)
	{
		// 遍歷到NULL就返回
		if (root == NULL)
		{
			return;
		}
		else
		{
			// 先遍歷根
			cout << root->data <<" ";
			// 左子樹交給BinaryTreePrevOrder這個函式去遍歷
			BinaryTreePrevOrder(root->leftChild);
			// 右子樹交給BinaryTreePrevOrder這個函式去遍歷
			BinaryTreePrevOrder(root->rightChild);
		}
	}
	//遞迴中序遍歷
	void BinaryTreeInOrder(BinaryTreeNode<DateType>* root)
	{
		// 遍歷到NULL就返回
		if (root == NULL)
		{
			return;
		}
		else
		{
			// 左子樹交給BinaryTreeInOrder這個函式去遍歷
			BinaryTreeInOrder(root->leftChild);
			// 遍歷根
			cout << root->data<<" ";
			// 右子樹交給BinaryTreeInvOrder這個函式去遍歷
			BinaryTreeInOrder(root->rightChild);
		}
	}
	//遞迴後序遍歷
	void BinaryTreePostOrder(BinaryTreeNode<DateType>* root)
	{
		if (root == NULL)
		{
			return;
		}
		else
		{
			// 左子樹交給BinaryTreePostOrder這個函式去遍歷
			BinaryTreePostOrder(root->leftChild);
			// 右子樹交給BinaryTreePostOrder這個函式去遍歷
			BinaryTreePostOrder(root->rightChild);
			//遍歷根
			cout << root->data<<" ";
	    }
	}
	//層序遍歷
	void BinaryTreeLevelOrder(BinaryTreeNode<DateType>* root)
	{
		if (root == NULL)
		{
			return;
		}
		queue<BinaryTreeNode<DateType>*> q;
		q.push(root);
		while (!q.empty())
		{
			BinaryTreeNode<DateType>* front = q.front();
			q.pop();
			cout << front->data << " ";
			if (front->leftChild)
				q.push(front->leftChild);
			if (front->rightChild)
				q.push(front->rightChild);
		}
		cout << endl;
		cout << ">>============================================================================<<" << endl;
		cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
	}
	//非遞迴的前序遍歷
	void PreOrderWithoutRecusion()
	{
		//初始化一個棧
		stack<BinaryTreeNode<DateType>*> Stack;
		BinaryTreeNode<DateType>* pointer = root;//pointer為遍歷指標
		//棧不為空或遍歷指標不為空時迴圈
		while (pointer || !Stack.empty())
		{
			//一路向左
			if (pointer)
			{
				//訪問當前結點併入棧
				cout << pointer->data << " ";
				Stack.push(pointer);
				pointer = pointer->leftChild;//左孩子不空,一直向左
			}
			else
			{
				//出棧,並轉向出棧結點的右子樹
				pointer = Stack.top();
				Stack.pop();
				pointer = pointer->rightChild;
			}
		}
		cout << endl;
		cout << ">>============================================================================<<" << endl;
		cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
	}
	//非遞迴的中序遍歷
	void InOrderWithoutRecusion()
	{
		stack<BinaryTreeNode<DateType>*> Stack;
		BinaryTreeNode<DateType>* Pointer = root;
		while (!Stack.empty() || Pointer)
		{
			if (Pointer)
			{
				Stack.push(Pointer);
				Pointer = Pointer->leftChild;
			}
			else
			{
				Pointer = Stack.top();
				Stack.pop();
				cout << Pointer->data << " ";
				Pointer = Pointer->rightChild;
			}
		}
		cout << endl;
		cout << ">>============================================================================<<" << endl;
		cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
	}
	//非遞迴的後續遍歷
	void PostOrderWithoutRecusion()
	{
		StackElement<DateType> element;
		stack<StackElement<DateType>> Stack;
		BinaryTreeNode<DateType>* pointer;
		pointer = root;
		while (!Stack.empty() || pointer)
		{
			while (pointer != NULL)
			{
				element.pointer = pointer;
				element.tag = Left;
				Stack.push(element);
				pointer = pointer->leftChild;
			}
			element = Stack.top();
			Stack.pop();
			pointer = element.pointer;
			if (element.tag == Left)
			{
				element.tag = Right;
				Stack.push(element);
				pointer = pointer->rightChild;
			}
			else
			{
				cout << pointer->data << " ";
				pointer = NULL;
			}
		}
		cout << endl;
		cout << ">>============================================================================<<" << endl;
		cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
	}
	//二叉樹結點個數
	int BinaryTreeSize(BinaryTreeNode<DateType>* root)
	{
		return root == NULL ? 0 : BinaryTreeSize(root->leftChild) + BinaryTreeSize(root->rightChild) + 1;
	}
	//二叉樹的葉子結點個數
	int BinaryTreeLeafSize(BinaryTreeNode<DateType>* root)
	{
		if (root == NULL)
			return 0;
		if (root->leftChild == NULL && root->rightChild == NULL)
			return 1;
		return BinaryTreeLeafSize(root->leftChild) + BinaryTreeLeafSize(root->rightChild);
	}
	//二叉樹第K層結點個數
	int BinaryTreeLevelKSize(BinaryTreeNode<DateType>* root, int k)
	{
		if (k < 0)
			return -1;
		if (root == NULL)
			return 0;
		if (k == 1)
			return 1;
		return BinaryTreeLevelKSize(root->leftChild, k - 1) + BinaryTreeLevelKSize(root->rightChild, k - 1);
	}
	//二叉樹查詢值為x的結點
	BinaryTreeNode<DateType>* BinaryTreeFind(BinaryTreeNode<DateType>* root, DateType x)
	{
		if (root == NULL)
			return NULL;
		if (root->data == x)
			return root;
		BinaryTreeNode<DateType>* leftRet = BinaryTreeFind(root->leftChild, x);
		if (leftRet)
			return leftRet;
		BinaryTreeNode<DateType>* rightRet = BinaryTreeFind(root->rightChild, x);
		if (rightRet)
			return rightRet;
	}
	//二叉樹的銷燬
	void BinaryTreeDestory(BinaryTreeNode<DateType>* root)
	{
		if (root == NULL)
		{
			return;
		}
		queue<BinaryTreeNode<DateType>*> q;
		q.push(root);
		while (!q.empty())
		{
			BinaryTreeNode<DateType>* front = q.front();
			q.pop();
			if (front->leftChild)
				q.push(front->leftChild);
			if (front->rightChild)
				q.push(front->rightChild);
			delete front;
			front = NULL;
		}
	}
	void print()
	{
		cout << ">>============================================================================<<" << endl;
		cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
		while (1)
		{
			cout << "請選擇二叉樹遍歷方式" << endl;
			cout << "1:遞迴前序遍歷" << endl;
			cout << "2:遞迴中序遍歷" << endl;
			cout << "3:遞迴後續遍歷" << endl;
			cout << "4:層序遍歷" << endl;
			cout << "5:非遞迴前續遍歷" << endl;
			cout << "6:非遞迴中續遍歷" << endl;
			cout << "7:非遞迴後續遍歷" << endl;
			cout << "8:二叉樹結點個數" << endl;
			cout << "9:二叉樹葉子結點個數" << endl;
			cout << "10:二叉樹第K層結點個數" << endl;
			cout << "11:二叉樹的銷燬" << endl;
			int n;
			cin >> n;
			switch (n)
			{
			case 1:
				cout << "遞迴前序遍歷:";
				BinaryTreePrevOrder(root);
				cout << endl;
				cout << ">>============================================================================<<"<<endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			case 2:
				cout << "遞迴中序遍歷:";
				BinaryTreeInOrder(root);
				cout << endl;
				cout << ">>============================================================================<<" << endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			case 3:
				cout << "遞迴後序遍歷:";
				BinaryTreePostOrder(root);
				cout << endl;
				cout << ">>============================================================================<<" << endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			case 4:
				cout << "層序遍歷:";
				BinaryTreeLevelOrder(root);
				break;
			case 5:
				cout << "非遞迴前序遍歷:";
				PreOrderWithoutRecusion();
				break;
			case 6:
				cout << "非遞迴中續遍歷:";
				InOrderWithoutRecusion();
				break;
			case 7:
				cout << "非遞迴後續遍歷:";
				PostOrderWithoutRecusion();
				break;
			case 8:
				cout << "二叉樹結點個數:" ;
				cout << BinaryTreeSize(root) << endl;
				cout << ">>============================================================================<<" << endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			case 9:
				cout << "二叉樹葉子結點個數:";
				cout << BinaryTreeLeafSize(root) << endl;
				cout << ">>============================================================================<<" << endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			case 10:
			{
				int b = 0;
				cout << "請輸入你要查詢的層數k:";
				cin >> b;
				cout << "第k層的節點數為:" << BinaryTreeLevelKSize(root, b) << endl;
				cout << ">>============================================================================<<" << endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			}
			case 11:
				BinaryTreeDestory(root);
				cout << "銷燬成功" << endl;
				cout << ">>============================================================================<<" << endl;
				cout << "/*------------------------請輸入你要選擇的選項------------------------- */" << endl;
				break;
			default:
				break;
			}
		}
	}
private:
	BinaryTreeNode<DateType>* root;
};
int main()
{
	BinaryTree<char> tree;
	tree.print();
	system("pause");
	return EXIT_SUCCESS;
}

相關文章