国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解

這篇具有很好參考價值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、前置說明

在學習二叉樹的基本操作前,需先要創(chuàng)建一棵二叉樹,然后才能學習其相關(guān)的基本操作。為了降低大家學習成本,此處手動快速創(chuàng)建一棵簡單的二叉樹,快速進入二叉樹操作學習。
typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

// 動態(tài)申請一個新節(jié)點
BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	assert(newnode);

	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;

	return newnode;
}

BTNode* CreatBinaryTree()
{
    BTNode* node1 = BuyNode(1);
    BTNode* node2 = BuyNode(2);
    BTNode* node3 = BuyNode(3);
    BTNode* node4 = BuyNode(4);
    BTNode* node5 = BuyNode(5);
    BTNode* node6 = BuyNode(6);
 
    node1->left = node2;
    node1->right = node4;
    node2->left = node3;
    node4->left = node5;
    node4->right = node6;
    return node1;
}

注意:上述代碼并不是創(chuàng)建二叉樹的方式。


二、構(gòu)建二叉樹

// 通過前序遍歷的數(shù)組"ABD##E#H##CF##G##"構(gòu)建二叉樹
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if (*pi >= n)
	{
		return NULL;
	}

	char ch = a[*pi];
	(*pi)++;

	if (ch == '#')
	{
		return NULL;
	}

	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	newNode->data = ch;
	newNode->left = BinaryTreeCreate(a, n, pi);
	newNode->right = BinaryTreeCreate(a, n, pi);

	return newNode;
}

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習


三、二叉樹的遍歷

學習二叉樹結(jié)構(gòu),最簡單的方式就是遍歷。所謂二叉樹遍歷 (Traversal) 是按照某種特定的規(guī)則,依次對二叉 樹中的節(jié)點進行相應(yīng)的操作,并且每個節(jié)點只操作一次。訪問結(jié)點所做的操作依賴于具體的應(yīng)用問題。 遍歷是二叉樹上最重要的運算之一,也是二叉樹上進行其它運算的基礎(chǔ)。
【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習

二叉樹的遍歷方式主要有四種,先介紹三種,最后再介紹第四種。(利用了分治的思想)

  1. 序遍歷?(Preorder Traversal 亦稱先序遍歷),方式為先遍歷根結(jié)點,左子樹,右子樹。
  2. 序遍歷?(Inorder Traversal),方式為先遍歷左子樹,根結(jié)點,右子樹。
  3. 序遍歷?(Postorder Traversal),方式為先遍歷左子樹,右子樹,根結(jié)點。
// 二叉樹前序遍歷
void PreOrder(BTNode* root);
// 二叉樹中序遍歷
void InOrder(BTNode* root);
// 二叉樹后序遍歷
void PostOrder(BTNode* root);

其中這三種遍歷方式一般都用遞歸進行實現(xiàn)。

由于被訪問的結(jié)點必是某子樹的根,所以 N(Node)、L(Left subtree)和 R(Right subtree)又可解釋為 根、根的左子樹和根的右子樹。NLR、LNR 和 LRN分別又稱為先根遍歷、中根遍歷和后根遍歷。
注意
  1. 深度優(yōu)先遍厲前序遍厲、中序遍厲、后序遍厲,注意有些說法只認同前序遍厲。
  2. 廣度優(yōu)先遍厲層序遍厲。

1、前序遍歷

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習

按照前序遍歷的方式,我們應(yīng)該先遍歷根結(jié)點 A,然后再去遍歷左子樹。當進入左子樹后,我們需要再執(zhí)行前序遍歷方式,即遍歷 A 的左子樹中的根結(jié)點 B,然后再遍歷 B 的左子樹。當我們再進入左子樹,又是先遍歷根結(jié)點D,然后又遍歷左子樹,按照順序遍歷到 R,此時終于完成根結(jié)點,左子樹,接下來遍歷右子樹。進入右子樹后,又遍歷根結(jié)點T... ...,所以這種遍歷方式屬于遞歸性質(zhì)的。(遍歷順序為:A–>B–>D–>R–>T–>E–>Y–>C–>Q–>U–>W

// 二叉樹前序遍歷 
void BinaryTreePrevOrder(BTNode* root) // 根->左子樹->右子樹
{
	if (root == NULL)
	{
		printf("# "); // 用#代表NULL
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
【遞歸圖解】
【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習

2、中序遍歷

中序遍歷方式為左子樹,根結(jié)點,右子樹。仍以上面的圖為例,遍歷順序為:

R–>D–>T–>B–>E–>Y–>A–>Q–>U–>C–>W

// 二叉樹中序遍歷
void BinaryTreeInOrder(BTNode* root)// 左子樹->根->右子樹
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}

3、后序遍歷

后序遍歷方式為?左子樹,右子樹,根結(jié)點。仍以上面的圖為例,遍歷順序應(yīng)該為:

R–>T–>D–>Y–>E–>B–>U–>Q–>W–>C–>A

// 二叉樹后序遍歷
void BinaryTreePostOrder(BTNode* root) // 左子樹->右子樹->根
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data);
}

【總結(jié)】?

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習
  • 前序遍歷結(jié)果:1->2->3->4->5->6
  • 中序遍歷結(jié)果:3->2->1->5->4->6
  • 后序遍歷結(jié)果:3->2->5->6->4->1

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習


4、層序遍歷

層序遍歷:除了先序遍歷、中序遍歷、后序遍歷外,還可以對二叉樹進行層序遍歷。
設(shè)二叉樹的根節(jié)點所在層數(shù)為 1,層序遍歷就是從所在二叉樹的根節(jié)點出發(fā),首先訪問第一層的樹根節(jié)點,然后從左到右訪問第 2 層上的節(jié)點,接著是第三層的節(jié)點,以此類推,自上而下,自左至右逐層訪問樹的結(jié)點的過程就是層序遍歷。
【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習
// 層序遍歷
void LevelOrder(BTNode* root);

注意:層序遍歷一般需要使用隊列。 (隊列內(nèi)容前面已經(jīng)詳細介紹過了)

【思路】先讓根入隊列,然后再讓根出隊列,當左子樹不為 NULL 時讓左子樹入隊列,當右子樹不為NULL時讓右子樹入隊列,然后不斷地迭代下去,直至隊列為空。記得出隊列前要保存當前值來訪問到該元素,Pop 到隊列當中的值是地址,通過該地址來訪問其中的 data

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習

層序遍歷結(jié)果為:?3->4->3->8->6->6->7

如何利用隊列實現(xiàn)呢?
  1. 判斷當前隊列是否為空。
  2. 隊列為空:結(jié)束;隊列非空:取出隊列第一個元素入隊列。
  3. 上一層出來后,再入下一層(即它的左右孩子節(jié)點)。

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習

由于前面已經(jīng)對隊列的各種操作進行了詳解,這里就不展開講了。(直接運用之前寫的 Queue.c 和 Queue.h)

// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root) // 樹的根節(jié)點root不為空 將根節(jié)點入隊列
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q); // 獲取隊列頭部元素
		printf("%c ", front->data); // 打印節(jié)點值
		QueuePop(&q); // 出隊列

		// 如果當前樹根的左右孩子不為空 則分別入隊列
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習?


四、二叉樹其它接口的實現(xiàn)

1、二叉樹的節(jié)點個數(shù)

按照遞歸思想,計算二叉樹的節(jié)點數(shù)量,我們可以認為 二叉樹的節(jié)點個數(shù)?= 左子樹數(shù)量 + 右子樹數(shù)量 + 1,其中 1 是當前根節(jié)點數(shù)量(前提條件是存在根節(jié)點)。

【思想 1】

迭代,使用棧來模擬遞歸的過程,用全局變量 / 靜態(tài)局部變量來記錄節(jié)點個數(shù),遍歷二叉樹的所有節(jié)點,并累加節(jié)點的個數(shù)。


【思想 2】

遞歸,利用分治的思想,函數(shù)使用帶返回值的方式,其內(nèi)部的遞歸本質(zhì)上是一個后序遍厲(左子樹->右子樹->根節(jié)點)。

// 二叉樹節(jié)點個數(shù)
int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習?


2、二叉樹葉子節(jié)點個數(shù)

按照遞歸的思想,計算二叉樹的葉子節(jié)點數(shù)量,我們可以認為 葉子節(jié)點個數(shù) = 左子樹葉子節(jié)點個數(shù) + 右子樹葉子節(jié)點個數(shù) + 0,0 是因為當前根結(jié)點有子樹,說明根結(jié)點不是葉子結(jié)點。

【思想】

以 left 和 right 為標志,如果都為 NULL,則說明該節(jié)點是葉子節(jié)點。

// 二叉樹葉子節(jié)點個數(shù)
int BinaryTreeLeafSize(BTNode* root)
{
    // 先判斷當前訪問的節(jié)點是否為空
	if (root == NULL) 
	{
		return 0;
	}

    // 當前節(jié)點不為空,它的左右孩子都為空,說明該節(jié)點是葉子節(jié)點
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

    // 當前節(jié)點不為空,左右孩子不都為空,則繼續(xù)往下遍歷
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習?


3、二叉樹第k層節(jié)點個數(shù)

【思想】

求當前樹的第 k 層節(jié)點個數(shù) = 左子樹的第 k-1 層節(jié)點個數(shù) + 右子樹的第 k-1 層節(jié)點個數(shù) (當 k=1 時,說明此層就是目標層)

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習?

// 二叉樹第k層節(jié)點個數(shù)
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k >= 1);
	if (root == NULL) // 先判斷當前訪問的節(jié)點是否為空
	{
		return 0;
	}

	if (k == 1) // 當前節(jié)點不為空,而k已經(jīng)減到1了,說明遍歷到了第k層,說明該節(jié)點是第k層的
	{
		return 1;
	}

    // 還沒有遍歷到第k層,我們就繼續(xù)往下遍歷
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
如何知道這個節(jié)點是不是第 k 層的?

求二叉樹第 k 層的節(jié)點個數(shù),我們從根節(jié)點開始往下遍歷(按根->左->右的順序),每遍歷一次 k 就減 1一次,當 k==1 時,說明我們遍歷到了第 k 層,此時訪問該層的節(jié)點。如果它不為空,則二叉樹第 k 層的節(jié)點個數(shù)就要 +1。


4、二叉樹查找值為x的節(jié)點

【思想】

按照遞歸思想,先判斷當前結(jié)點是否是目標節(jié)點,然后查找左子樹,再查找右子樹。

如果左右子樹都沒有找到,就返回NULL。

// 二叉樹查找值為x的節(jié)點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL) // 先判斷當前訪問的節(jié)點是否為空
	{
		return NULL;
	}

	if (root->data == x) // 判斷要找的x值節(jié)點是不是當前節(jié)點
	{
		return root;
	}

    // 不是當前節(jié)點,則繼續(xù)去該節(jié)點的左子樹中找
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
	{
		return ret1;
	}

    // 還沒找到,再繼續(xù)去該節(jié)點的右子樹中找
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL; // 當前節(jié)點及其左右子樹中都沒找到,返回NULL
}

5、銷毀二叉樹

// 二叉樹銷毀
void BinaryTreeDestory(BTNode** root)
{
	// 如果使用前中序遍歷銷毀,節(jié)點會先被銷毀,變成隨機值,就不知道它的左右子樹位置了 所以采用后序遍歷銷毀
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL; // 將根節(jié)點設(shè)置為NULL 防止野指針
}

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習

注意:如果這里使用前序遍歷或中序遍歷進行銷毀,節(jié)點會先被銷毀,變成隨機值,就不知道它的左右子樹位置了,所以應(yīng)該采用后序遍歷來銷毀二叉樹。

如果這里傳進來的是一級指針,由于要在函數(shù)內(nèi)改變形參的值,無法改變外部實參的值,所以我們需要在函數(shù)外置頭節(jié)點指針為NULL。


6、判斷二叉樹是否是完全二叉樹

【思想】

層序遍歷時,把空節(jié)點也入隊列。

  • 完全二叉樹中,非空節(jié)點是連續(xù)的,則空節(jié)點是連續(xù)的。
  • 非完全二叉樹中,非空節(jié)點不是連續(xù)的,則空節(jié)點不是連續(xù)的。

所以在出隊時,判斷一下,出到第一個空節(jié)點時,跳出循環(huán);

在下面重新寫一個循環(huán)繼續(xù)出隊,并檢查出隊元素

  • 如果第一個空節(jié)點后面的全是空節(jié)點,說明是完全二叉樹。
  • 如果第一個空節(jié)點后面的有非空節(jié)點,說明是非完全二叉樹

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習?

// 判斷二叉樹是否是完全二叉樹(利用層序遍歷的思想來判斷)
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root) // 樹的根節(jié)點root不為空 將根節(jié)點入隊列
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q); // 獲取隊列頭部元素
		QueuePop(&q); //出隊列

		if (front)
		{
			// 不管當前樹根的左右孩子是否為空,都分別入隊列
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			break; //遇到空后,跳出層序遍歷
		}
	}

	// 如果后面全是空,則是完全二叉樹,否則不是完全二叉樹
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true; // 出隊的節(jié)點中,如果沒有出現(xiàn)非空節(jié)點,說明是完全二叉樹出隊的節(jié)點中,如果沒有出現(xiàn)非空節(jié)點,說明是完全二叉樹
}

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習


五、代碼整合

1、Queue.h

// Queue.h
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

struct BinaryTreeNode;

typedef struct BinaryTreeNode* QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);

2、Queue.c

// Queue.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	int n = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		++n;
		cur = cur->next;
	}
	return n;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

3、test.c

// test.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//動態(tài)申請一個新節(jié)點
BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	assert(newnode);

	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;

	return newnode;
}

// 通過前序遍歷的數(shù)組"ABD##E#H##CF##G##"構(gòu)建二叉樹
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if (*pi >= n)
	{
		return NULL;
	}

	char ch = a[*pi];
	(*pi)++;

	if (ch == '#')
	{
		return NULL;
	}

	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	newNode->data = ch;
	newNode->left = BinaryTreeCreate(a, n, pi);
	newNode->right = BinaryTreeCreate(a, n, pi);

	return newNode;
}

// 二叉樹節(jié)點個數(shù)
int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

// 二叉樹葉子節(jié)點個數(shù)
int BinaryTreeLeafSize(BTNode* root)
{
    // 先判斷當前訪問的節(jié)點是否為空
	if (root == NULL) 
	{
		return 0;
	}

    // 當前節(jié)點不為空,它的左右孩子都為空,說明該節(jié)點是葉子節(jié)點
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

    // 當前節(jié)點不為空,左右孩子不都為空,則繼續(xù)往下遍歷
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// 二叉樹第k層節(jié)點個數(shù)
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k >= 1);
	if (root == NULL) // 先判斷當前訪問的節(jié)點是否為空
	{
		return 0;
	}

	if (k == 1) // 當前節(jié)點不為空,而k已經(jīng)減到1了,說明遍歷到了第k層,說明該節(jié)點是第k層的
	{
		return 1;
	}

    // 還沒有遍歷到第k層,我們就繼續(xù)往下遍歷
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

// 二叉樹查找值為x的節(jié)點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL) // 先判斷當前訪問的節(jié)點是否為空
	{
		return NULL;
	}

	if (root->data == x) // 判斷要找的x值節(jié)點是不是當前節(jié)點
	{
		return root;
	}

    // 不是當前節(jié)點,則繼續(xù)去該節(jié)點的左子樹中找
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
	{
		return ret1;
	}

    // 還沒找到,再繼續(xù)去該節(jié)點的右子樹中找
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL; // 當前節(jié)點及其左右子樹中都沒找到,返回NULL
}

// 二叉樹銷毀
void BinaryTreeDestory(BTNode** root)
{
	// 如果使用前中序遍歷銷毀,節(jié)點會先被銷毀,變成隨機值,就不知道它的左右子樹位置了 所以采用后序遍歷銷毀
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL; // 將根節(jié)點設(shè)置為NULL 防止野指針
}

// 二叉樹前序遍歷 
void BinaryTreePrevOrder(BTNode* root) // 根->左子樹->右子樹
{
	if (root == NULL)
	{
		printf("# "); // 用#代表NULL
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

// 二叉樹中序遍歷
void BinaryTreeInOrder(BTNode* root)// 左子樹->根->右子樹
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}

// 二叉樹后序遍歷
void BinaryTreePostOrder(BTNode* root) // 左子樹->右子樹->根
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data);
}

// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root) // 樹的根節(jié)點root不為空 將根節(jié)點入隊列
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q); // 獲取隊列頭部元素
		printf("%c ", front->data); // 打印節(jié)點值
		QueuePop(&q); // 出隊列

		// 如果當前樹根的左右孩子不為空 則分別入隊列
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

// 判斷二叉樹是否是完全二叉樹(利用層序遍歷的思想來判斷)
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root) // 樹的根節(jié)點root不為空 將根節(jié)點入隊列
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q); // 獲取隊列頭部元素
		QueuePop(&q); //出隊列

		if (front)
		{
			// 不管當前樹根的左右孩子是否為空,都分別入隊列
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			break; //遇到空后,跳出層序遍歷
		}
	}

	// 如果后面全是空,則是完全二叉樹,否則不是完全二叉樹
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true; // 出隊的節(jié)點中,如果沒有出現(xiàn)非空節(jié)點,說明是完全二叉樹出隊的節(jié)點中,如果沒有出現(xiàn)非空節(jié)點,說明是完全二叉樹
}

int main()
{
	BTDataType a[] = { 'A', 'B', 'D', '#', '#', 'E', '#', 'H', '#', '#', 'C', 'F', '#', '#', 'G', '#', '#' };
	int n = sizeof(a) / sizeof(a[0]) - 1; // 減去末尾的'\0'
	int pos = 0;
	BTNode* root = BinaryTreeCreate(a, n, &pos); // 構(gòu)建二叉樹

	printf("TreeSize:%d\n", BinaryTreeSize(root)); // 二叉樹節(jié)點個數(shù)
	printf("TreeLeafSize:%d\n", BinaryTreeLeafSize(root)); // 二叉樹葉子節(jié)點個數(shù)
	printf("Tree2LevelSize:%d\n", BinaryTreeLevelKSize(root, 2)); // 二叉樹第k層節(jié)點個數(shù)
	printf("TreeFindB:%p\n", BinaryTreeFind(root, 'B')); // 二叉樹查找值為x的節(jié)點
 
	// 前序遍歷
	BinaryTreePrevOrder(root);
	printf("\n");
	// 中序遍歷
	BinaryTreeInOrder(root);
	printf("\n");
	// 后序遍歷
	BinaryTreePostOrder(root);
	printf("\n");
	BinaryTreeLevelOrder(root); // 層序遍歷

	printf("TreeComplete:%d\n", BinaryTreeComplete(root)); // 判斷二叉樹是否是完全二叉樹
	BinaryTreeDestory(&root);// 二叉樹銷毀
	printf("二叉樹已銷毀\n");
	return 0;
}

六、程序運行整體效果

【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解,數(shù)據(jù)結(jié)構(gòu),C語言,初學者,c語言,數(shù)據(jù)結(jié)構(gòu),學習文章來源地址http://www.zghlxwxcb.cn/news/detail-663639.html

到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)的實現(xiàn) -- 詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式實現(xiàn)及遍歷

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式實現(xiàn)及遍歷

    后文所有代碼中的二叉樹結(jié)點: 前,中,后序遍歷都可以采用分治遞歸的思想解決,將根節(jié)點和它的孩子結(jié)點分別處理。 此處僅利用遞歸展開圖分析前序遍歷,中序和后序也是相同的思想: 層序遍歷需要利用隊列來進行,如果二叉樹跟結(jié)點不為空,則讓 指向它的一個指針入

    2024年02月07日
    瀏覽(24)
  • 數(shù)據(jù)結(jié)構(gòu)——二叉樹的鏈式存儲的實現(xiàn)

    ???????????????????????? InitBiTree(BiTree T)——初始化二叉樹。 ????????????????????????CreateBiTree(BiTree T)——先序遍歷的順序建立二叉鏈表。 ????????????????????????PreOrderTraverse(BiTree T)——先序遍歷。 ????????????????????????

    2024年02月04日
    瀏覽(25)
  • 數(shù)據(jù)結(jié)構(gòu)第六課 -----鏈式二叉樹的實現(xiàn)

    數(shù)據(jù)結(jié)構(gòu)第六課 -----鏈式二叉樹的實現(xiàn)

    ?? ?????????????????????? ??? 作者介紹: ???? ?? ?????????????? ?? ??作者id:老秦包你會, ?? 簡單介紹:?????????????????????????????? 喜歡學習C語言和python等編程語言,是一位愛分享的博主,有興趣的小可愛可以來互討 ????

    2024年02月03日
    瀏覽(24)
  • 數(shù)據(jù)結(jié)構(gòu)(C語言實現(xiàn))——二叉樹的概念及二叉樹順序結(jié)構(gòu)和鏈式結(jié)構(gòu)的實現(xiàn)(堆排序+TOP-K問題+鏈式二叉樹相關(guān)操作)

    數(shù)據(jù)結(jié)構(gòu)(C語言實現(xiàn))——二叉樹的概念及二叉樹順序結(jié)構(gòu)和鏈式結(jié)構(gòu)的實現(xiàn)(堆排序+TOP-K問題+鏈式二叉樹相關(guān)操作)

    前面學習了數(shù)據(jù)結(jié)構(gòu)中線性結(jié)構(gòu)的幾種結(jié)構(gòu),順序表,鏈表,棧和隊列等,今天我們來學習一種非線性的數(shù)據(jù)結(jié)構(gòu)——樹。由于二叉樹是數(shù)據(jù)結(jié)構(gòu)中的一個重點和難點,所以本文著重介紹二叉樹的相關(guān)概念和性質(zhì),以及二叉樹的應(yīng)用。 樹是一種非線性的數(shù)據(jù)結(jié)構(gòu),它是由n(

    2023年04月21日
    瀏覽(23)
  • 【數(shù)據(jù)結(jié)構(gòu)初階】八、非線性表里的二叉樹(二叉樹的實現(xiàn) -- C語言鏈式結(jié)構(gòu))

    【數(shù)據(jù)結(jié)構(gòu)初階】八、非線性表里的二叉樹(二叉樹的實現(xiàn) -- C語言鏈式結(jié)構(gòu))

    ========================================================================= 相關(guān)代碼gitee自取 : C語言學習日記: 加油努力 (gitee.com) ?========================================================================= 接上期 : 【數(shù)據(jù)結(jié)構(gòu)初階】七、非線性表里的二叉樹(堆的實現(xiàn) -- C語言順序結(jié)構(gòu))-CSDN博客 ?==========

    2024年02月08日
    瀏覽(31)
  • 【鏈式二叉樹】數(shù)據(jù)結(jié)構(gòu)鏈式二叉樹的(萬字詳解)

    【鏈式二叉樹】數(shù)據(jù)結(jié)構(gòu)鏈式二叉樹的(萬字詳解)

    前言: 在上一篇博客中,我們已經(jīng)詳解學習了堆的基本知識,今天帶大家進入的是二叉樹的另外一種存儲方式----“鏈式二叉樹”的學習,主要用到的就是“遞歸思想”??! 前情回顧: 再看二叉樹基本操作前,再回顧下二叉樹的概念,二叉樹是: 空樹 非空:根節(jié)點,根節(jié)點

    2024年01月20日
    瀏覽(41)
  • 數(shù)據(jù)結(jié)構(gòu)——二叉樹的鏈式結(jié)構(gòu)

    數(shù)據(jù)結(jié)構(gòu)——二叉樹的鏈式結(jié)構(gòu)

    ? 個人主頁 : 日刷百題 系列專欄 : 〖C語言小游戲〗〖Linux〗〖數(shù)據(jù)結(jié)構(gòu)〗 ?〖C語言〗 ?? 歡迎各位 → 點贊 ??+ 收藏 ??+ 留言 ??? ? 這里我們使用先序遍歷的思想來創(chuàng)建二叉樹,這里的內(nèi)容對于剛接觸二叉樹的朋友可能有些難理解,不妨先看完下面的二叉樹各種遍歷

    2024年02月05日
    瀏覽(31)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式結(jié)構(gòu)

    學習鏈式二叉樹要知道三種遍歷方式,便于對二叉樹的節(jié)點以及左子樹和右子樹進行操作。 前序遍歷:根、左子樹、右子樹 中序遍歷:左子樹、根、右子樹 后序遍歷:左子樹、右子樹、根 以下圖為例: 得到的結(jié)果: 前序遍歷:1 2 3 4 5 6 中序遍歷:3 2 1 5 4 6 后序遍歷:3 2

    2024年02月08日
    瀏覽(29)
  • 數(shù)據(jù)結(jié)構(gòu):二叉樹的鏈式結(jié)構(gòu)

    數(shù)據(jù)結(jié)構(gòu):二叉樹的鏈式結(jié)構(gòu)

    朋友們、伙計們,我們又見面了,本期來給大家解讀一下鏈式二叉樹的相關(guān)知識點,如果看完之后對你有一定的啟發(fā),那么請留下你的三連,祝大家心想事成! 數(shù)據(jù)結(jié)構(gòu)與算法專欄 :數(shù)據(jù)結(jié)構(gòu)與算法 個? 人? 主? 頁 :stackY、 C 語 言 專 欄 :C語言:從入門到精通 目錄 前言

    2024年02月07日
    瀏覽(22)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式存儲結(jié)構(gòu)

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹的鏈式存儲結(jié)構(gòu)

    前序遍歷,又叫先根遍歷。 遍歷順序:根 - 左子樹 - 右子樹 代碼: 中序遍歷,又叫中根遍歷。 遍歷順序:左子樹 - 根 - 右子樹 代碼 : 后序遍歷,又叫后根遍歷。 遍歷順序:左子樹 - 右子樹 - 根 代碼 : 除了先序遍歷、中序遍歷、后序遍歷外,還可以對二叉樹進行層序遍

    2024年02月09日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包