1. 程式人生 > 其它 >C++資料結構之樹

C++資料結構之樹

技術標籤:C++二叉樹資料結構

樹在資料結構是一個極其重要的存在,例如二叉樹,排序二叉樹,平衡二叉樹,紅黑樹等等在許多專案中運用比較廣,而且也是平時考察的重點,所以今天就來系統地談一談樹

定義:n個結點的有限集合,當n等於0時,稱為空樹,n個結點的樹只有n-1條邊,有如下性質。

  1. 有且僅有一個特定的稱為根的結點

  2. 當n>1時,其餘結點可分為m個互不相交的有限集合,其中每一個集合本身又是一顆樹,稱為根節點的子樹(n個結點的樹中只有n-1條邊)

  3. 樹中的一個結點的子結點的個數稱為該結點的度,樹中最大度數稱為樹的度

  4. 度大於0的結點稱為分支節點,度為0的結點稱為葉子結點

  5. 結點的層次,結點的高度(是樹中結點的最大層數),結點的深度

  6. 有序樹和無序數

  7. 樹中兩個結點之間的路徑是由這兩個結點之間所經過的結點序列構成的

  8. 路徑長度:路徑上所經歷邊的個數

  9. 森林:m棵互不相交的樹的集合

  10. 樹中的結點數等於所有結點的度數加1

  11. 度為m的樹中第i層上至多有m**(i-1)個結點

  12. 高度為h的m叉結點至多有(m**h -1)/(m-1)個結點

  13. 具有n個結點的m叉樹的最小高度為[logm (n(m-1)+1)]

二叉樹
二叉樹是n(n=0)個結點的有限集合

  • n=0,二叉樹為空
  • n>0,由根節點和兩個互不相交的被稱為根的左子樹和右子樹組成,左子樹和右子樹葉分別是一顆二叉樹

基本形態

  • 空,根節點,左子樹,右子樹,二叉樹

滿二叉樹:一棵高度為h,且含有2**h-1的結點的二叉樹為滿二叉樹(對於編號為i的結點,若存在,其雙親的編號為i/2,左孩子為2i,右孩子為2i+1)

完全二叉樹:設一個高度為h,有n個結點的二叉樹,當且僅當每個結點都與高度為h的滿二叉樹中編號1~n的結點一一對應時,稱為完全二叉樹(相當於滿二叉樹的子集)

二叉樹的儲存結構

順序儲存:用陣列類似的一片連續的記憶體空間來儲存,若存在左孩子,則為2i,右孩子為2i+1
在這裡插入圖片描述
在這裡插入圖片描述
順序儲存最壞情況下會非常浪費記憶體空間,因此比較適合完全二叉樹。

鏈式儲存:用連結串列來存放一顆二叉樹,二叉樹中每個結點用連結串列的一個鏈結點來儲存.

typedef struct BiTNode {
	// 資料域
	ElemType data;
	// lchild指向左孩子,rchild指向右孩子
	struct BiTNode *
lchild, *rchild; }BiTNode, *BiTree;

含有n個結點的連結串列中,有n-1個空鏈域

二叉樹的遍歷

先序遍歷

  1. 根節點
  2. 左子樹
  3. 右子樹
void PreOrder(BiTree T) {
	if (T != nullptr) {
		visit(T);
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}

中序遍歷

  1. 左子樹
  2. 根節點
  3. 右子樹
void InOrder(BiTree T) {
	if (T != nullptr) {
		InOrder(T->lchild);
		visit(T);
		InOrder(T->rchild);
	}
}

後序遍歷

  1. 左子樹
  2. 右子樹
  3. 根節點
void PostOrder(BiTree T) {
	if (T != nullptr) {
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		visit(T);
	}
}

中序遍歷非遞迴演算法

  • 初始時一次掃描根節點的所有左側結點並將它們一一進棧
  • 出棧一個結點,訪問他
  • 掃描該結點的右孩子結點並將其進棧
  • 一次掃描右孩子結點的所有左側結點並一一進棧
  • 反覆該過程直到棧空為止
#include <stack>
void InOrder(BiTree T) {
	// 初始化一個棧
	stack<BiTree> s;
	// 構造一個二叉樹
	BiTree p = T;
	while (p != nullptr || s.empty() == 1) {
		if (p != nullptr) {
			// 進棧
			s.push(p);
			p = p->lchild;
		} else {
			// 返回棧頂元素
			p = s.top();
			s.pop();
			p = p->rchild();
	}
	}
}

層次遍歷

  • 初始將跟入隊並訪問根節點,然後出隊
  • 若有左子樹,則將左子樹的根入隊
  • 若有右子樹,則將右子樹的根入隊
  • 然後出隊,訪問該結點
  • 反覆該過程直到佇列為空為止
#include <queue>
void LevelOrder(BiTree T) {
	queue<BiTree> q;
	BiTree b;
	q.push(b);
	while(q.empty() == 1) {
		q.pop(b);
		if (b->lchild != nullptr) {
			q.push(b->lchild);
		}
		if (b->rchild != nullptr) {
			q.push(p->rchild);
		}
	}
}

線索二叉樹
對於二叉樹中的空指標域,為了不浪費記憶體,故使用線索化的線索二叉樹,基本原理:
若無左子樹,則將左指標指向前驅結點;
若無右子樹,則將右指標指向後驅結點;
在這裡插入圖片描述

typedef struct ThreadNode {
	ElemType  data;
	struct ThreadNode *lchild, *rchild;
	int ltag, rtag;
}ThreadNode, *ThreadTree;

待寫