1. 程式人生 > >算法學習筆記(六) 二叉樹和圖遍歷—深搜 DFS 與廣搜 BFS

算法學習筆記(六) 二叉樹和圖遍歷—深搜 DFS 與廣搜 BFS

創建 mark preorder 第一個 高度 變量初始化 term link 文章

圖的深搜與廣搜

復習下二叉樹、圖的深搜與廣搜。

從圖的遍歷說起。圖的遍歷方法有兩種:深度優先遍歷(Depth First Search), 廣度優先遍歷(Breadth First Search),其經典應用走迷宮、N皇後、二叉樹遍歷等。遍歷即按某種順序訪問“圖”中全部的節點,順序分為:

  • 深度優先(優先往深處走),用的數據結構是棧, 主要是遞歸實現。
  • 廣度優先(優先走近期的)。用的數據結構是隊列。主要是叠代實現。

對於深搜。因為遞歸往往能夠方便的利用系統棧,不須要自己維護棧。所以通常實現比較簡單。

而對於廣搜。則須要自己維護一個隊列,且因為隊列大小未知,底層存儲的物理結構採用鏈式存儲。

二叉樹概念和性質

二叉樹是每個節點最多有兩個子樹的樹結構,常被用於實現二叉查找樹和二叉堆。

在圖論中,二叉樹定義是一個連通的無環圖。而且每個頂點的度不大於2。二叉樹和樹有非常多類似之處,但並非樹的特殊情形,主要有下面三點主要區別:

  1. 樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為2。
  2. 樹的結點無左、右之分,而二叉樹的結點有左、右之分;
  3. 樹的結點個數至少為1,而二叉樹的結點個數能夠為0
樹的一些相關概念:
  • 結點的度:結點所擁有的子樹的個數稱為該結點的度。二叉樹就僅僅有 0,1。2 三種情況;
  • 葉結點:度為 0 的結點稱為葉結點,或者稱為終端結點;
  • 分枝結點:度不為 0 的結點稱為分支結點,一棵樹的結點除葉結點外,其余的都是分支結點。
  • 結點的層數: 規定樹的根結點的層數為 1,其余結點的層數等於它的雙親結點的層數加 1;
  • 樹的深度: 樹中全部結點的最大層數稱為樹的深度;
  • 路徑、路徑長度。假設一棵樹的一串結點 n(1), n(2), ?…, n(k) ?有例如以下關系:結點 n(i) 是 n(i+1) 的父結點 (1≤i<k), 就把 n(1),n(2),…,n(k) 稱為一條由 n(1) 至 n(k) 的路徑。

    這條路徑的長度是 k-1;

  • 滿二叉樹: 在一棵二叉樹中,假設全部分支結點都存在左子樹和右子樹,而且全部葉子結點都在同一層上。
  • 全然二叉樹: 深度為 K 的。有 N 個結點的二叉樹,當且僅當其每個結點都與深度為 K 的滿二叉樹中按層序編號從 1 至 N 的結點一一相應;
  • 堆是一種全然二叉樹,經常使用的排序算法、Dijkstra 算法、Prim 算法等都經常使用堆優化;
  • AVL樹:它是最先發明的自平衡二叉查找樹, 名字來源於其發明者,AVL 樹中不論什麽節點的兩個子樹的高度最大區別為 1, 查找、插入和刪除在平均和最壞情況下都是 O(logn);
二叉樹的重要的性質:
  • 性質1:在二叉樹的第 i 層上至多有 2i-1 個結點 (i≥1);
  • 性質2:深度為 k 的二叉樹上至多含 2k-1 個結點 (k≥1);
  • 性質3:對不論什麽一棵二叉樹,若它含有 n0 個葉子結點、n2 個度為 2 的結點,則:n0 = n2+1;
  • 性質4:具有 n 個結點的全然二叉樹的深度為 log2n+1;
  • 性質5:若對含 n 個結點的全然二叉樹從上到下且從左至右(即按層序)進行 1 至 n 的編號。則對全然二叉樹中隨意一個編號為 i 的結點:
    (1) 若 i=1,則該結點是二叉樹的根,無雙親,否則。編號為 i/2 ?的結點為其雙親結點;
    (2)?若 2i>n。則該結點無左孩子。否則,編號為 2i 的結點為其左孩子結點。
    (3)?若 2i+1>n,則該結點無右孩子結點。否則,編號為 2i+1 的結點為其右孩子結點;

小實驗(C 實現)

這裏首先用一個數組生成一個全然二叉樹(鏈式存儲), 然後深搜用前序遍歷,廣搜借助自己實現的一個隊列(鏈式存儲)來進行。圖例如以下所看到的:

技術分享圖片

代碼為:
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
	int data;
	struct Node * left;
	struct Node * right;
} BitNode, *BiTree;

typedef BiTree QElemType;  // 定義隊列元素類型

typedef struct QNode {
	QElemType data;   // 存放樹節點指針
	struct QNode *next;
} QNode, *QueuePtr;  // 隊列節點和節點指針

typedef struct LinkQueue {
	QueuePtr front, rear;
} LinkQueue; // 隊列前後指針: 隊首。隊尾

/* 初始化隊列  */
int InitQueue(LinkQueue *Q) {
	QueuePtr s = (QueuePtr) malloc(sizeof(QNode));
	if (!s)	return 0;
	Q->front = s;
	Q->rear = s;
	return 1;
}

/* 入隊 */
int EnQueue(LinkQueue *Q, QElemType e) {
	if (e == NULL)	return 0;
	QueuePtr s = (QueuePtr) malloc(sizeof(QNode));
	if (!s) return 0;
	s->data = e;
	s->next = NULL;
	Q->rear->next = s;
	Q->rear = s;
	return 1;
}

/* 出隊 */
int DeQueue(LinkQueue *Q, QElemType *e) {
	if (Q->front == Q->rear) {
		return 0;
	} // 首位指針相等。隊列空,出隊失敗
	QueuePtr p;
	p = Q->front->next; // 頭結點不放數據,其後繼結點作為隊首,出隊
	*e = p->data;
	Q->front->next = p->next; // 隊首指針後移一個節點
	if (Q->rear == p) {
		Q->rear = Q->front;
	} // 尾指針假設指向的是頭結點後第一個節點,則令其指向隊首指針。 即隊列僅僅有一個數據節點的情況
	free(p);
	return 1;
}

/* 利用數組 a 創建二叉樹。遞歸的編寫技巧在於設計好函數接口 */
void CreateTree(BiTree *bt, int a[], int len, int index) {
	if (index > len - 1)
		return;
	(*bt) = (BiTree) malloc(sizeof(BitNode));   // 指針變量初始化堆區的內存,c 用 malloc 函數
	(*bt)->data = a[index];
	(*bt)->left = NULL;   //  不能能省略,當其為葉節點時,指針域為 NULL,而且常作為程序推斷條件
	(*bt)->right = NULL;
	CreateTree(&((*bt)->left), a, len, 2 * index + 1);
	CreateTree(&((*bt)->right), a, len, 2 * index + 2);
}

/* 前序遍歷二叉樹 , 屬於深度優先遍歷 DFS */
void PreOrderTraverse(BiTree bt) {
	if (bt == NULL)
		return;
	printf("%d ", bt->data); // 操作節點數據
	PreOrderTraverse(bt->left);
	PreOrderTraverse(bt->right);
}

/* 按層遍歷,即廣度優先遍歷 BFS */
void BFSTraverse(BiTree bt) {
	LinkQueue *Q = (LinkQueue *) malloc(sizeof(LinkQueue));
	BiTree e;
	InitQueue(Q);
	EnQueue(Q, bt);
	while (Q->front != Q->rear) {
		DeQueue(Q, &e);
		printf("%d ", e->data);
		EnQueue(Q, e->left);
		EnQueue(Q, e->right);
	}
}

int main() {
	int arr[] = { 0, 1, 2, 3, 4, 5, 6};
	BiTree root;
	CreateTree(&root, arr, sizeof(arr) / sizeof(int), 0);
	printf("PreOrderTraverse: ");
	PreOrderTraverse(root);
	printf("\nBFSOrderTravesre: ");
	BFSTraverse(root);
	return 0;
}

/*
程序執行例如以下:
PreOrderTraverse: 0 1 3 4 2 5 6
BFSOrderTravesre: 0 1 2 3 4 5 6
 */
【文章地址為:http://blog.csdn.net/thisinnocence】

算法學習筆記(六) 二叉樹和圖遍歷—深搜 DFS 與廣搜 BFS