1. 程式人生 > >【資料結構】二叉樹的遍歷及應用

【資料結構】二叉樹的遍歷及應用

前言

      在二叉樹的應用中,常常要求在樹中查詢某些結點,或者對樹中的結點統一進行某種處理。因此,就提到了二叉樹的遍歷問題,對於線性結構來說,遍歷是一個很容易解決的問題,而二叉樹偏偏是一種非線性的結構,因此需要尋找一種規律。

       二叉樹由三個基本單元組成,分別是根結點、左子樹及右子樹。依次遍歷這三個部分就能遍歷整個二叉樹,以V、L、R表示訪問根結點、遍歷左子樹及遍歷右子樹,則有VLR、VRL、RLV、RVL、LVR、LRV六種遍歷二叉樹的方案。若規定左子樹一定先於右子樹被遍歷,就只剩下三種情況。再根據根結點被訪問的次序,可以分為可以分別命名為先(根)序遍歷,中(根)序遍歷,後(根)序遍歷。

        為了方便理解,定義一個二叉樹的結點型別和二叉樹型別;

template<class T> class BinaryTree;

template<class T>
class BinaryTreeNode
{
	friend class BinaryTree<T>;
public:
	BinaryTreeNode();
	BinaryTreeNode(T D, BinaryTreeNode <T> *L=NULL, BinaryTreeNode <T> *R=NULL)
	{
		data = D;
		Lchild = L;
		Rchild = R;
	}

private:
	T data;
	BinaryTreeNode <T> *Lchild ;
	BinaryTreeNode <T> *Rchild ;

};

template<class T>
class BinaryTree
{
public:
	BinaryTree();
	BinaryTree(T D, BinaryTreeNode <T> *L=NULL, BinaryTreeNode <T> *R=NULL)
	{
		root = new BinaryTreeNode<T>(D, L, R);
	}
	BinaryTree(BinaryTreeNode <T> *Node)
	{
		root = Node;
	}
	void PreOrder();
	void PreOrder(BinaryTreeNode <T> *current);

	void InOrder();
	void InOrder(BinaryTreeNode <T> *current);

	void PastOrder();
	void PastOrder(BinaryTreeNode <T> *current);
private:
	BinaryTreeNode <T> *root=NULL;
};

       構建一個如下圖的二叉樹;

二叉樹的遍歷

先序遍歷

先序遍歷的規則如下:若當前二叉樹為空,則返回空,否則

1.  訪問根結點;

2.  先序遍歷左子樹;

3.  先序遍歷右子樹;

上圖中的二叉樹的先序遍歷為:ABDEHIJKCFG

根據上面的關係,可以寫出二叉樹類的先序遍歷的函式;

template<class T>
void BinaryTree<T>::PreOrder()
{
	cout << "先序遍歷:";
	PreOrder(root);        //先序遍歷
}

template<class T>
void BinaryTree<T>::PreOrder(BinaryTreeNode <T> *current)
{
	if (current != NULL)                   //當current為空指標,說明已經到達葉結點
	{
		cout << current->data<<" ";        //首先輸出當前結點的值
		PreOrder(current->Lchild);         //遞迴呼叫左子樹
		PreOrder(current->Rchild);         //遞迴呼叫右子樹
	}
}

中序遍歷

中序遍歷的規則如下:若當前二叉樹為空,則返回空,否則

1.  中序列根結點的左子樹;

2.  訪問根結點;

3.  中序遍歷根結點的右子樹;

上圖中的二叉樹的中序遍歷為:DBHEJIKAFCG

根據上面的關係,可以寫出二叉樹類的中序遍歷的函式;

template<class T>
void BinaryTree<T>::InOrder()
{
	InOrder(root);        //先序遍歷
}

template<class T>
void BinaryTree<T>::InOrder(BinaryTreeNode <T> *current)
{
	if (current != NULL)                   //當current為空指標,說明已經到達葉結點
	{
		
		InOrder(current->Lchild);         //遞迴呼叫左子樹
		cout << current->data << " ";        //首先輸出當前結點的值
		InOrder(current->Rchild);         //遞迴呼叫右子樹
	}
}

後序遍歷

後序遍歷的規則如下:若當前二叉樹為空,則返回空,否則

1.  後序列根結點的左子樹;

2.  後序遍歷根結點的右子樹;

3.  訪問根結點;

上圖中的二叉樹的後序遍歷為:DHJKIEBFGCA

根據上面的關係,可以寫出二叉樹類的後序遍歷的函式;

template<class T>
void BinaryTree<T>::PastOrder()
{
	PastOrder(root);        //先序遍歷
}

template<class T>
void BinaryTree<T>::PastOrder(BinaryTreeNode <T> *current)
{
	if (current != NULL)                   //當current為空指標,說明已經到達葉結點
	{

		PastOrder(current->Lchild);         //遞迴呼叫左子樹	
		PastOrder(current->Rchild);         //遞迴呼叫右子樹
		cout << current->data << " ";        //首先輸出當前結點的值
	}
}

二叉樹遍歷的應用

計算節點個數

      計算二叉樹的節點的格式可以利用二叉樹的遍歷,常用的是後遍歷,先遍歷根結點的左子樹和右子樹,分別計算出左右子樹的結點個數,然後加上根結點個數就是整個二叉樹節點個數。

template<class T>
int BinaryTree<T>::size(BinaryTreeNode <T> *current)
{
	if (current == NULL){ return 0; }
	else{ return 1 + size(current->Lchild) + size(current->Rchild); }
}

計算二叉樹的高度

       與計算二叉樹節點高度類似,計算二叉樹高度時如果高度為0,返回-1;否則按照後序遍歷規則,先遞迴計算根結點的左子樹和右子樹的高度,再求兩者中的較大者,並加1,最終得到整個二叉樹的高度;

template<class T>
int BinaryTree<T>::depth(BinaryTreeNode <T> *current)
{
	if (current == NULL){ return -1; }
	else{ return 1 + Max(depth(current->Lchild), depth(current->Rchild)); }
}

知道先序(後序)和中序求二叉樹後序(先序)

     有一些題目喜歡提這樣的問題,以知道先序和中序求後序為例,例如已知先序是ABDEHIJKCFG,已知中序是DBHEJIKAFCG,求二叉樹的後序排列。(知道先序和後序是無法求出中序的

       其實瞭解二叉樹的遍歷後,這個題目很簡單。由於先序是先遍歷根結點,先序排列的第一個點必定根結點,也就是說A是根結點;再看中序遍歷,先遍歷左子樹,左子樹遍歷玩才會遍歷根結點,因此,排在A前方的全是左子樹上的點,排在A後方的全是,如果A在中序排列中也是排在第一個,說明它沒有左子樹。因此有了如下結構;

       再看左子樹,此時左子樹的先序為BDEHIJK,中序為DBHEJIK。同樣的道理,B為A的左子樹的根結點,中序排列中在B前面的為左子樹,排在B後側的為右子樹;如此反覆進行就能得出二叉樹的結構,再進行後序遍歷就能得出後序排列。

       當知道後序和中序排列求先序排列時,也是同樣的道理,二叉樹的根結點是最後被遍歷到的點。

        根據上面的關係,可以的寫出重建二叉樹的函式;

template<class T>
BinaryTreeNode<T>* BinaryTreeNode<T>::reConstructBinaryTree(vector<T> pre, vector<T> in)
{
	BinaryTreeNode<T> *BiTree=NULL;

	int size = pre.size();
	if (size != 0)
	{
		BiTree->data = pre[0];     //根結點賦值
		//構建左右子樹的序列;
		vector<T> leftPre;
		vector<T> leftIn;
		vector<T> rightPre;
		vector<T> rightIn;

		//在中序排列中找到根結點的位置
		int i = 0;
		for (; i<size; i++)
		{
			if (in[i] == pre[0])
			{
				break;
			}
		}
		for (int j = 0; j < size; j++)
		{
			if (j<i)        //中序序列:排在根結點之前的放入左子樹
			{
				if (j != i)
				{
					leftIn.push_back(in[j]);
				}

			}
			if (j>i)         //中序序列:排在根結點之後的放入右子樹
			{

				rightIn.push_back(in[j]);
			}
		}
		for (int j = 1; j < size; j++)
		{
			if (j <= i)      //先序序列:排在根結點之前的放入左子樹
			{
				leftPre.push_back(pre[j]);
			}
			if (j>i)         //先序序列:排在根結點之後的放入右子樹
			{
				rightPre.push_back(pre[j]);
			}
		}
		if (leftIn.size() != 1){ BiTree->Lchild = reConstructBinaryTree(leftPre, leftIn); }
		if (rightIn.size() != 1){ BiTree->Rchild = reConstructBinaryTree(rightPre, rightIn); }
	}

	return BiTree;
}

最後

        二叉樹的應用非常多,例如堆排序,二叉排序樹,霍夫曼樹等等,需要更多地去了解。

已完。