欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

數(shù)據(jù)結構之鏈式二叉樹詳解

 更新時間:2023年04月17日 10:05:49   作者:蛋超飯不要加蛋  
所謂二叉樹遍歷 (Traversal) 是按照某種特定的規(guī)則,依次對二叉樹中的節(jié)點進行相應的操作,并且每個節(jié)點只操作一次。本文通過代碼示例詳細介紹了C語言中的鏈式二叉樹,需要的朋友可以參考一下

??1.二叉樹的遍歷??

所謂二叉樹遍歷 (Traversal) 是按照某種特定的規(guī)則,依次對二叉樹中的節(jié)點進行相應的操作,并且每個節(jié)點只操作一次。 訪問結點所做的操作依賴于具體的應用問題。遍歷 是二叉樹上最重要的運算之一,也是二叉樹上進行其它運算的基礎。
按照規(guī)則,二叉樹的遍歷分為: 前序遍歷、中序遍歷、后序遍歷的遞歸遍歷,層序遍歷的非遞歸遍歷下面將以下面的二叉樹為例講解四種遍歷

1.1前序遍歷

二叉樹的前序遍歷也叫先序遍歷,遍歷的順序為:根、左子樹、右子樹,即遇到一棵樹,先訪問根節(jié)點,再訪問左子樹和右子樹,訪問左子樹和右子樹的過程又分為先訪問根節(jié)點,再訪問左子樹和右子樹,這是一個遞歸訪問的過程,因此前序遍歷屬于遞歸遍歷

遍歷的過程將以文字和圖片兩種方式展現(xiàn)

遍歷過程:

先遇到根節(jié)點1,訪問根節(jié)點1,再訪問1的左子樹

1的左子樹:遇到根節(jié)點2,訪問根節(jié)點2,再訪問2的左子樹,2的左子樹只有一個根節(jié)點3,3的左右子樹為空因此不需要訪問3的左右子樹,訪問根節(jié)點3便結束,2的左子樹遍歷結束訪問2的右子樹,2的右子樹為空即不用訪問,此時2的整顆樹遍歷結束,即1的左子樹遍歷結束,然后接著訪問1的右子樹

1的右子樹:遇到根節(jié)點4,訪問根節(jié)點4,再訪問4的左子樹,4的左子樹只有一個根節(jié)點5,5的左右子樹為空因此不需要訪問5的左右子樹,訪問根節(jié)點5便結束,4的左子樹遍歷結束訪問4的右子樹,4的右子樹只有一個根節(jié)點6,6的左右子樹為空因此不需要訪問6的左右子樹,訪問根節(jié)點6便結束,此時4的整顆樹遍歷結束,即1的右子樹遍歷結束

整棵樹遍歷完成,遍歷序列為:1 2 3 4 5 6

遍歷圖示:

1.2中序遍歷

二叉樹的中序遍歷也叫中根遍歷,遍歷的順序為:左子樹、根、右節(jié)點,即遇到一棵樹,先訪問它的左子樹,再訪問根,最后訪問右子樹,訪問左子樹和右子樹的過程又分為先訪問左子樹,再訪問根和右子樹,這是一個遞歸訪問的過程,因此中序遍歷也屬于遞歸遍歷

遍歷的過程將以文字和圖片兩種方式展現(xiàn)

遍歷過程:

遇到根節(jié)點1,先不訪問根節(jié)點,先訪問1的左子樹

1的左子樹:遇到根節(jié)點2,先不訪問根節(jié)點2,先訪問2的左子樹:2的左子樹只有一個根節(jié)點3,3的左右子樹為空因此不需要訪問3的左右子樹,訪問根節(jié)點3便結束,此時2的左子樹結束,訪問根節(jié)點2,然后訪問2的右子樹,2的右子樹為空則不訪問,此時2的整顆樹遍歷結束,即1的左子樹遍歷結束

訪問根節(jié)點1,接著訪問1的右子樹

1的右子樹:遇到根節(jié)點4,先不訪問根節(jié)點4,先訪問4的左子樹:遇到根節(jié)點5,5的左右子樹為空因此不需要訪問5的左右子樹,訪問根節(jié)點5便結束,此時4的左子樹訪問結束,訪問根節(jié)點4,接著訪問4的右子樹,4的右子樹只有一個根節(jié)點6,6的左右子樹為空因此不需要訪問6的左右子樹,訪問根節(jié)點6便結束,此時4的整棵樹訪問結束,即1的右子樹訪問結束

整棵樹遍歷完成,遍歷序列:3 2 1 5 4 6

 遍歷圖示:

1.3后序遍歷

二叉樹的后序遍歷也叫后根遍歷,遍歷的順序為:左子樹、右子樹、根,即遇到一棵樹,先訪問它的左子樹,再訪問右子樹,最后訪問根,訪問左子樹和右子樹的過程又分為先訪問左子樹,再訪問右子樹和根,這是一個遞歸訪問的過程,因此后序遍歷也屬于遞歸遍歷

遍歷的過程將以文字和圖片兩種方式展現(xiàn)

遍歷過程:

遇到根節(jié)點1,先不訪問根節(jié)點1,先訪問左子樹

1的左子樹:遇到根節(jié)點2,先不訪問根節(jié)點2,先訪問2的左子樹:2的左子樹只有根節(jié)點3,3的左右子樹為空因此不需要訪問3的左右子樹,訪問根節(jié)點3便結束,此時2的左子樹訪問結束,接著訪問2的右子樹,2的右子樹為空因此不需要訪問,此時2的左右子樹訪問結束,最后訪問根節(jié)點2,此時2的整顆樹訪問結束,即1的左子樹訪問結束,接著訪問1的右子樹

1的右子樹:遇到根節(jié)點4,先不訪問根節(jié)點4,先訪問4的左子樹:5的左右子樹為空因此不需要訪問5的左右子樹,訪問根節(jié)點5便結束,此時4的左子樹訪問結束,接著訪問4的右子樹,6的左右子樹為空因此不需要訪問6的左右子樹,訪問根節(jié)點6便結束,此時4的左右子樹遍歷結束,最后訪問根節(jié)點4,4的整顆樹訪問結束,即1的右子樹訪問結束

最后訪問根節(jié)點1,整棵樹遍歷結束,遍歷序列:3 2 5 6 4 1

 遍歷圖示:

1.4層次遍歷

除了先序遍歷、中序遍歷、后序遍歷外,還可以對二叉樹進行層序遍歷。設二叉樹的根節(jié)點所在層數(shù)為1, 層序遍歷就是從所在二叉樹的根節(jié)點出發(fā),首先訪問第一層的樹根節(jié)點,然后從左到右訪問第 2 層上的節(jié)點,接著是第三層的節(jié)點,以此類推, 自上而下,自左至右逐層訪問樹的結點的過程就是層序遍歷。

遍歷圖示:

由上圖可以看出,層序遍歷為非遞歸遍歷

 ??2.鏈式二叉樹的實現(xiàn)??

鏈式二叉樹的實現(xiàn)包括二叉樹的創(chuàng)建、遍歷、銷毀、求節(jié)點個數(shù)、求葉子節(jié)點個數(shù)、求二叉樹的深度、求第k層節(jié)點個數(shù)、查找、判斷是否是完全二叉樹等

下面我們一一來實現(xiàn)這些接口

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

2.1二叉樹的創(chuàng)建

給定一個前序遍歷字符串,按照此字符串以指針方式構建一顆二叉樹

給定字符串ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空樹

代碼設計思路:

函數(shù)參數(shù)為字符串指針和下標指針,利用遞歸的思想,首先判斷是否遇到空格,遇到空格則跳過該空格即下標加1并返回,接著構建一個節(jié)點,當前字符指針指向的字符賦值給節(jié)點的值域然后下標加1,將字符指針和下標傳參調用遞歸函數(shù)然后用節(jié)點的左指針接收。再將字符指針和下標傳參調用遞歸函數(shù)然后用節(jié)點的右指針接收,最后返回節(jié)點指針

BTNode *TreeBuild(char *arr,int* i)
{
    if(arr[*i]=='#')//遇到空格則跳過該空格
    {
        (*i)++;
        return NULL;
    }
    // 建立節(jié)點
    BTNode *root=(BTNode *)malloc(sizeof(BTNode));
    //節(jié)點的值為當前下標指向的字符
    rot->data=arr[(*i)++];
    //調用遞歸,并用節(jié)點的左指針接收
    root->left=TreeBuild(arr,i);
    //調用遞歸,并用節(jié)點的左指針接收
    root->right=TreeBuild(arr,i);
    return root;
}

2.2前序遍歷

前序遍歷的過程在前面已經(jīng)介紹,我們按照過程設計相應的函數(shù)

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則直接返回,先打印當前節(jié)點的值,再將自己的左子樹作為參數(shù)調用遞歸遍歷,最后將自己的右子樹作為參數(shù)調用遞歸遍歷

代碼:

void PreOrder(BTNode* root)
{
	if (root == NULL)//當前節(jié)點為空則直接返回
		return NULL;
	printf("%d ", root->data);//打印當前節(jié)點的值
	PreOrder(root->left);//遞歸遍歷左子樹
	PreOrder(root->right);//遞歸調用右子樹
}

2.3中序遍歷

中序遍歷的過程在前面已經(jīng)介紹,我們按照過程設計相應的函數(shù)

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則直接返回,先將自己的左子樹作為參數(shù)調用遞歸遍歷,再打印當前節(jié)點的值,最后將自己的右子樹作為參數(shù)調用遞歸遍歷

代碼:

void InOrder(BTNode* root)
{
	if (root == NULL)//當前節(jié)點為空則直接返回
		return NULL;
	InOrder(root->left);//遞歸遍歷左子樹
	printf("%d ", root->data);//打印當前節(jié)點的值
	InOrder(root->right);//遞歸調用右子樹
}

2.4后序遍歷

后序遍歷的過程在前面已經(jīng)介紹,我們按照過程設計相應的函數(shù)

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則直接返回,先將自己的左子樹作為參數(shù)調用遞歸遍歷,再將自己的右子樹作為參數(shù)調用遞歸遍歷,最后打印當前節(jié)點的值

void PostOrder(BTNode* root)
{
	if (root == NULL)//當前節(jié)點為空則直接返回
		return NULL;
	PostOrder(root->left);//遞歸遍歷左子樹
	PostOrder(root->right);//遞歸調用右子樹
	printf("%d ", root->data);//打印當前節(jié)點的值
}

2.5層序遍歷

層序遍歷的過程在前面已經(jīng)介紹,我們按照過程設計相應的函數(shù)

代碼設計思路:

利用隊列,首先判斷當前節(jié)點是否為空,為空則直接返回。把第一個節(jié)點的指針入隊,然后進去循環(huán)(循環(huán)條件是隊列不為空),定義一個指針拿到隊列的第一個節(jié)點,然后出隊并打印節(jié)點的值,出隊之后將節(jié)點的左右孩子入隊,如果節(jié)點的左孩子存在,則將左孩子指針入隊,如果節(jié)點的右孩子存在,則將右孩子指針入隊,然后又繼續(xù)取隊頭元素打印,直到隊列為空

代碼:

void LevelOrder(BTNode *root)
{
	Queue q;//定義一個隊列
	QueueInit(&q);//隊列初始化
	if (root == NULL)//當前節(jié)點為空則返回
		return;
	QueuePush(&q, root);//將第一個節(jié)點指針入隊
	while (!(QueueEmpty(&q)))//循環(huán)條件是隊列不為空,隊列為空則結束
	{
		BTNode* front = QueueFront(&q);//取隊頭節(jié)點
		printf("%d ", front->data);//打印節(jié)點的值
		QueuePop(&q);//出隊
		if (front->left)//左孩子存在,則將左孩子指針入隊
		{
			QueuePush(&q, front->left);
		}
		if (front->right)//右孩子存在,則將右孩子指針入隊
		{
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);//銷毀隊列
}

2.6銷毀

銷毀的過程:先從最后一層開始銷毀,先銷毀左子樹,再銷毀右子樹,最后銷毀根,即用到后序遍歷的思想,遞歸銷毀

代碼設計思路:

利用后序遍歷的思想,首先判斷當前節(jié)點是否為空,為空則返回。先將左孩子指針作為參數(shù)調用遞歸,再將左孩子指針作為參數(shù)調用遞歸,最后將根節(jié)點釋放

代碼:

void BinaryTreeDestory(BTNode* root)
{
	if(root == NULL)//為空則返回
		return;
	BinaryTreeDestory(root->left);//遞歸銷毀左子樹
	BinaryTreeDestory(root->right);//遞歸銷毀右子樹
	free(root);//最后銷毀根節(jié)點
}

2.7求節(jié)點個數(shù)

一顆樹的節(jié)點個數(shù)可以分為左子樹的節(jié)點數(shù)加上右子樹的節(jié)點數(shù)再加1即根節(jié)點,同樣是遞歸求節(jié)點個數(shù)

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則返回0,然后將左孩子指針作為參數(shù)調用遞歸,再將右孩子指針作為參數(shù)調用遞歸,最后將兩者的值加1返回,即節(jié)點個數(shù)等于左子樹的節(jié)點個數(shù)+右子樹的節(jié)點個數(shù)+1

 代碼:

int  TreeSize(BTNode* root)
{
	if (root == NULL)//當前節(jié)點為空則返回0
		return 0;
	//遞歸左子樹和右子樹,然后將兩者的和加1返回
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

2.8求葉子節(jié)點個數(shù)

一顆樹的葉子節(jié)點個數(shù)可以分為左子樹的葉子節(jié)點個數(shù)+右子樹的葉子節(jié)點個數(shù),即同樣利用遞歸求葉子節(jié)點個數(shù)

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則返回0,然后判斷當前節(jié)點是否為葉子結點,左孩子和右孩子都為空的節(jié)點即為葉子節(jié)點,為葉子節(jié)點則返回1,接著遞歸左子樹和右子樹,并返回兩者的和

代碼:

int TreeLeafSize(BTNode* root)
{
	if (root ==NULL)//當前節(jié)點為空則返回0
		return 0;
	if (root->left == NULL && root->right == NULL)//當前節(jié)點為葉子結點則返回1
		return 1;
	//遞歸左子樹和右子樹,并返回兩者的和
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

2.9求二叉樹的深度

一顆樹的深度等于左右子樹的較大的深度加1(加根節(jié)點),即利用遞歸求左右子樹的深度,將左右子樹較大的深度加1返回

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則返回0,再判斷當前節(jié)點是否為葉節(jié)點,是葉節(jié)點則返回1,接著遞歸左子樹和右子樹,取較大的深度加1返回

代碼:

int TreeHeight(BTNode* root)
{
	if (root == NULL)//當前節(jié)點為空則返回空
		return 0;
	if (root->left == NULL && root->right == NULL)//當前節(jié)點為葉節(jié)點則返回1
		return 1;
	int left = TreeHeight(root->left);//遞歸左子樹
	int right = TreeHeight(root->right);//遞歸右子樹
	return left > right ? left + 1 : right + 1;//將左右子樹較大的深度加1返回
}

2.10求第K層節(jié)點個數(shù)

整顆樹的第k層可以看成第二層的第k-1層,第三層的k-2層,第k層的第1層,即整棵樹的第k層的節(jié)點數(shù)等于左右子樹的第k-1層節(jié)點子樹,即利用遞歸求左右子樹的k-1層節(jié)點個數(shù)

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則返回0,再判斷當前層數(shù)是否為1,為1則返回1,然后遞歸左子樹,傳左子樹指針和k-1,然后遞歸右子樹,傳右子樹指針和k-1,最后返回兩者的和

代碼:

int TreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)//當前節(jié)點為空則返回0
		return 0;
	if (k == 1)//當前層數(shù)為1則返回1
		return 1;
	//遞歸左子樹和右子樹,返回兩者的值
	return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}

2.11查找

查找值為x的節(jié)點,并返回節(jié)點的指針

代碼設計思路:

利用遞歸的思想,首先判斷當前節(jié)點是否為空,為空則返回空,不為空則判斷當前節(jié)點的值是否等于x,等于則返回該節(jié)點,然后遞歸查找左子樹,如果遞歸左子樹返回的值不為空說明找到則返回該值,接著遞歸右子樹,如果遞歸右子樹返回的值不為空說明找到則返回該值,遞歸左右子樹都未找到說明x不存在,返回空

代碼:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//當前節(jié)點為空則返回空
		return NULL;
	if (root->data == x)//當前節(jié)點的值等于x則返回該節(jié)點
		return root;
	//遞歸左子樹查找
	BTNode* left = BinaryTreeFind(root->left,x);
	//返回的值不為空說明已找到,返回該值
	if (left)
		return left;
	//遞歸右子樹查找
	BTNode* right = BinaryTreeFind(root->right, x);
	//返回的值不為空說明已找到,返回該值
	if (right)
		return right;
	//遞歸左右子樹均為找到,則趕回空
	return NULL;
}

2.12判斷是否為完全二叉樹

上一篇文章介紹了完全二叉樹的概念,完全二叉樹可以看成是滿二叉樹以最后一層開始從右往左挖去了幾個節(jié)點而成,即完全二叉樹的倒數(shù)第二層是滿的,倒數(shù)第一層不可能存在只有右孩子而沒有左孩子的情況

代碼設計思路:

類似于層序遍歷的思想,利用隊列,首先判斷第一個節(jié)點是否為空,為空則返回,然后將第一個節(jié)點入隊,接著進入循環(huán)(循環(huán)條件為隊列不為空),取隊頭元素并出隊,如果取到的隊頭元素為空,說明此時已經(jīng)此時二叉樹剛好遍歷結束,則退出循環(huán)檢查后面隊列值地情況如果是完全二叉樹,則隊列后面應該都是空,如果存在不為空的元素則證明不是完全二叉樹。如果取到的隊頭元素不為空,則將其左右孩子(為空也一樣)入隊,如果循環(huán)正常結束,則證明是完全二叉樹

代碼:

bool BinaryTreeComplete(BTNode* root)
{
	Queue q;//定義一個隊列
	QueueInit(&q);//隊列初始化
	if (root == NULL)//當前節(jié)點為空則返回
		return;
	QueuePush(&q, root);//將第一個節(jié)點入隊
	while (!(QueueEmpty(&q)))
	{
		//取隊頭元素并出隊
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		//取到的隊頭元素為空則退出循環(huán),檢查隊列后面值地情況
		if (front==NULL)
		{
			break;
		}
		else//不為空則將左右孩子入隊
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	//檢查隊列后面的值的情況
	while (!(QueueEmpty(&q)))
	{
		BTNode* front = QueueFront(&q);//取隊頭元素并出隊
		QueuePop(&q);
		if (front != NULL)//如果有不為空的元素則證明不是完全二叉樹
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);//銷毀隊列
	return true;//循環(huán)正常結束則返回true
}

好啦,關于鏈式二叉樹就先學到這里,如果對您有所幫助,歡迎一鍵三連~

到此這篇關于數(shù)據(jù)結構之鏈式二叉樹詳解的文章就介紹到這了,更多相關C語言 鏈式二叉樹內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • c++與python實現(xiàn)二分查找的原理及實現(xiàn)

    c++與python實現(xiàn)二分查找的原理及實現(xiàn)

    本文介紹了c++與python實現(xiàn)二分查找的原理及實現(xiàn),二分查找指首先將數(shù)組中間值和目標值進行比較,如果相等則返回;如果不相等,則選擇中間值左邊的一半或者右邊的一半進行比較;不斷重復直到檢索完畢,下文相關資料需要的朋友可以參考一下
    2022-03-03
  • C語言實例之雙向鏈表增刪改查

    C語言實例之雙向鏈表增刪改查

    雙向鏈表(Doubly Linked List)是一種常見的數(shù)據(jù)結構,在單鏈表的基礎上增加了向前遍歷的功能,與單向鏈表不同,雙向鏈表的每個節(jié)點除了包含指向下一個節(jié)點的指針外,還包含指向前一個節(jié)點的指針,本文給大家介紹了C語言中雙向鏈表的增刪改查
    2023-08-08
  • C++/GoLang如何實現(xiàn)自底向上的歸并排序

    C++/GoLang如何實現(xiàn)自底向上的歸并排序

    這篇文章主要給大家介紹了關于C++/GoLang如何實現(xiàn)自底向上的歸并排序的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • c語言中的二級指針做函數(shù)參數(shù)說明

    c語言中的二級指針做函數(shù)參數(shù)說明

    這篇文章主要介紹了c語言中的二級指針做函數(shù)參數(shù)說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 最大對稱字符串的算法

    最大對稱字符串的算法

    題目:輸入一個字符串,輸出該字符串中對稱的子字符串的最大長度。比如輸入字符串“google”,由于該字符串里最長的對稱子字符串是“goog”,因此輸出4。
    2013-03-03
  • C語言求階乘之和的三種實現(xiàn)方法(先階乘再累加)

    C語言求階乘之和的三種實現(xiàn)方法(先階乘再累加)

    對于C/C++初學者來說,可能會經(jīng)常遇到如計算階乘等問題,下面這篇文章主要給大家介紹了關于C語言求階乘之和的三種實現(xiàn)方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • c語言printf函數(shù)的使用詳解

    c語言printf函數(shù)的使用詳解

    本篇文章是對c語言中printf函數(shù)的使用進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C語言 以數(shù)據(jù)塊的形式讀寫文件詳解及實現(xiàn)代碼

    C語言 以數(shù)據(jù)塊的形式讀寫文件詳解及實現(xiàn)代碼

    本文主要介紹 C語言 以數(shù)據(jù)塊的形式讀寫文件,這里對相關知識資料做了整理,并附代碼示例,以便大家學習參考,有學習此部分知識的朋友可以參考下
    2016-08-08
  • C語言結構體中內存對齊的問題理解

    C語言結構體中內存對齊的問題理解

    內存對齊”應該是編譯器的“管轄范圍”。編譯器為程序中的每個“數(shù)據(jù)單元”安排在適當?shù)奈恢蒙?。但是C語言的一個特點就是太靈活,太強大,它允許你干預“內存對齊”。如果你想了解更加底層的秘密,“內存對齊”對你就不應該再模糊了
    2022-02-02
  • 掌握C++:揭秘寫時拷貝與淺深拷貝之間的關系

    掌握C++:揭秘寫時拷貝與淺深拷貝之間的關系

    探索C++的奧秘,本指南將揭秘寫時拷貝與淺深拷貝之間的微妙關系,摸索這些復雜概念背后的邏輯,讓你的編程技能瞬間提升,來吧,讓我們一起進入這個引人入勝的C++世界!
    2024-01-01

最新評論