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

數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解)

這篇具有很好參考價(jià)值的文章主要介紹了數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

寫在前面

二叉樹的幾乎所有實(shí)現(xiàn)都是依靠遞歸實(shí)現(xiàn),遞歸的核心思路是把任何一個(gè)二叉樹看成根和左右子樹,而二叉樹遞歸的核心玩法就是把二叉樹的左右子樹再看成根,再找左右子樹,再看成根…

因此,解決二叉樹問題實(shí)際上要把二叉樹轉(zhuǎn)換成一個(gè)一個(gè)子樹的過程,找到一個(gè)一個(gè)的子樹再組裝起來就形成了二叉樹

二叉樹的創(chuàng)建

二叉樹建立的正統(tǒng)方法是利用遞歸,這里展示遞歸的一種寫法

BTNode* BuyNode(BTDataType a)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = a;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = BuyNode(a[*pi]);
	(*pi)++;

	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);

	return root;
}

如果你對(duì)遞歸的認(rèn)識(shí)并不熟悉,下面我畫了一幅遞歸展開圖,更好解釋這其中的原理

數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解),數(shù)據(jù)結(jié)構(gòu),知識(shí)總結(jié),數(shù)據(jù)結(jié)構(gòu),筆記
(右子樹的部分過程由于空間原因不夠完全展開,但如果看懂左子樹的構(gòu)建過程,右子樹不難自己畫出)

這里注意的是,函數(shù)棧幀的創(chuàng)建并不是每一個(gè)都不一樣的位置,當(dāng)一個(gè)棧幀被銷毀后,另外一個(gè)棧幀創(chuàng)建就會(huì)在原來的位置,因此在計(jì)算空間復(fù)雜度的時(shí)候需要考慮這個(gè)問題:
空間是可以重復(fù)利用的,時(shí)間是一去不復(fù)返的

從這幅圖中,相信可以理解這段代碼的含義了

首先創(chuàng)建一個(gè)根節(jié)點(diǎn),根節(jié)點(diǎn)去尋找左子樹進(jìn)入遞歸,而進(jìn)入遞歸后繼續(xù)尋找左子樹…直到遇到NULL截止,而遇到空后就尋找右子樹,右子樹也找到空后返回,這樣就構(gòu)建出了二叉樹中最小的一棵樹,而這棵樹會(huì)返回,作為根的左子樹,然后繼續(xù)進(jìn)入遞歸尋找根的右子樹…

由于是借助鏈表進(jìn)行實(shí)現(xiàn)的,因此我們可以理解為首先創(chuàng)建好根,而根的左右子樹用函數(shù)創(chuàng)建好后再連接起來…當(dāng)遇到#就返回,形成了一個(gè)一個(gè)的小樹,小樹最后組成大樹,就形成了二叉樹

二叉樹的遍歷

二叉樹的遍歷主要有前序遍歷,中序遍歷,后序遍歷,層序遍歷

前序遍歷

前序遍歷是指遍歷時(shí)先訪問根,再訪問左子樹,再訪問右子樹

代碼實(shí)現(xiàn)如下

void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

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

下面依舊畫出它的遞歸展開圖

數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解),數(shù)據(jù)結(jié)構(gòu),知識(shí)總結(jié),數(shù)據(jù)結(jié)構(gòu),筆記

中序遍歷

中序遍歷是先訪問左子樹,再訪問根,最后訪問右子樹

void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

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

遞歸圖和上面基本類似,就不做畫出了

后序遍歷

后序遍歷是先訪問左子樹,再訪問右子樹,最后訪問根

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

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

遞歸圖和上面基本類似,就不做畫出了

層序遍歷

層序遍歷指的是對(duì)二叉樹進(jìn)行一層一層的遍歷,每一層分別遍歷,這里借助隊(duì)列進(jìn)行遍歷,基本思路把根放到隊(duì)列中,當(dāng)某一個(gè)根要出隊(duì)列時(shí),就令該根的左子樹和右子樹進(jìn)隊(duì)列,這樣就能實(shí)現(xiàn)一層一層遍歷,畫法如下:

數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解),數(shù)據(jù)結(jié)構(gòu),知識(shí)總結(jié),數(shù)據(jù)結(jié)構(gòu),筆記
代碼實(shí)現(xiàn)相較于前面來說簡單一些,但需要引入隊(duì)列的,關(guān)于隊(duì)列的介紹:

數(shù)據(jù)結(jié)構(gòu)—手撕隊(duì)列棧并相互實(shí)現(xiàn)

下面展示要使用的隊(duì)列的相關(guān)函數(shù)實(shí)現(xiàn)

// queue.h
typedef struct BinaryTreeNode* QDataType;

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

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

// queue.c
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

QNode* BuyQnode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = BuyQnode(x);
	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

bool QueueEmpty(Queue* pq)
{
	if (pq->size == 0)
	{
		return true;
	}
	return false;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* newhead = pq->phead->next;
		free(pq->phead);
		pq->phead = newhead;
	}

	pq->size--;
}

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

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	return pq->ptail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

那么借助隊(duì)列我們來實(shí)現(xiàn)剛才的層序遍歷

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->data);
		QueuePop(&q);
		if (front->left)
			QueuePush(&q, front->left);
		if(front->right)
			QueuePush(&q, front->right);
	}
	printf("\n");
	//BinaryTreeDestory(&q);
}

二叉樹的銷毀

在知道了遍歷的多種途徑后,二叉樹的銷毀就很簡單了,在確定代碼如何實(shí)現(xiàn)前,先思考問題:應(yīng)該選用哪種遍歷來進(jìn)行二叉樹的銷毀?

結(jié)果是很明顯的,選用后序遍歷是最方便的,原因在于銷毀是需要進(jìn)行節(jié)點(diǎn)的釋放的,如果使用前序或者中序遍歷,那么在遍歷的過程中根節(jié)點(diǎn)會(huì)被先銷毀,那么找左子樹或者右子樹就帶來了不便(可以定義一個(gè)變量保存位置),因此最好用后序遍歷,把子樹都銷毀了最后銷毀根

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

二叉樹節(jié)點(diǎn)個(gè)數(shù)

二叉樹節(jié)點(diǎn)個(gè)數(shù)也是轉(zhuǎn)換成子樹來解決,如果為NULL就返回0,其他情況返回左右相加再加上節(jié)點(diǎn)本身

int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

二叉樹葉子節(jié)點(diǎn)的個(gè)數(shù)

葉子節(jié)點(diǎn)個(gè)數(shù)求解和上述方法相似,也是找最小的子樹,但不同的地方是,葉子節(jié)點(diǎn)找到的條件是葉子作為根,它的左右子樹都為空,符合這樣的就是葉子節(jié)點(diǎn),那么根據(jù)這個(gè)想法求得代碼不難得到:

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	int leftleave = BinaryTreeSize(root->left);
	int rightleave = BinaryTreeSize(root->right);

	return leftleave + rightleave;
}

二叉樹查找值為x的節(jié)點(diǎn)

這個(gè)相對(duì)較麻煩一點(diǎn),先上展開圖和代碼

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}

數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解),數(shù)據(jù)結(jié)構(gòu),知識(shí)總結(jié),數(shù)據(jù)結(jié)構(gòu),筆記
這里的關(guān)鍵動(dòng)作就是判斷ret1和ret2是否為空,如果是,則說明找到正確的值,那么就會(huì)一路被送出函數(shù),相反會(huì)一直返回NULL,因此就不必?fù)?dān)心送出其他元素的情況

二叉樹是否為完全二叉樹

這個(gè)思路也較為復(fù)雜,首先,需要解決的問題是用什么思路,基本思路是一個(gè)一個(gè)看,如果在某一個(gè)節(jié)點(diǎn)已經(jīng)為空的前提下,它后面的節(jié)點(diǎn)還不為空,那么就說明這個(gè)不是完全二叉樹

因此這個(gè)思路是不是和層序遍歷很像呢?我們借助這樣的思路繼續(xù)寫下去

當(dāng)隊(duì)列不為空就繼續(xù)入隊(duì)列出隊(duì)列進(jìn)行層序遍歷,當(dāng)遇到空就跳出,跳出循環(huán)后此時(shí)隊(duì)列并不為空,繼續(xù)出隊(duì)列,如果發(fā)現(xiàn)某個(gè)元素不為空,那么就證明這并不是完全二叉樹

代碼實(shí)現(xiàn)如下:

int BinaryTreeComplete(BTNode* root)
{
	assert(root);
	Queue q;
	QueueInit(&q);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q,front->left);
		QueuePush(&q,front->right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			BinaryTreeDestory(&q);
			return 0;
		}
	}
	BinaryTreeDestory(&q);
	return 1;
}

至此,二叉樹的基本功能實(shí)現(xiàn)就都結(jié)束了,很明顯,二叉樹的實(shí)現(xiàn)并不容易,它不僅需要對(duì)遞歸有一定深度的認(rèn)識(shí),還需要你對(duì)隊(duì)列有足夠的理解,才能解決層序遍歷和判斷完全二叉樹

這需要進(jìn)行更多的練習(xí),也為后面更復(fù)雜的樹打基礎(chǔ)和鋪墊

數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解),數(shù)據(jù)結(jié)構(gòu),知識(shí)總結(jié),數(shù)據(jù)結(jié)構(gòu),筆記文章來源地址http://www.zghlxwxcb.cn/news/detail-576796.html

到了這里,關(guān)于數(shù)據(jù)結(jié)構(gòu)---手撕圖解二叉樹(含大量遞歸圖解)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • 【數(shù)據(jù)結(jié)構(gòu)】---幾分鐘簡單幾步學(xué)會(huì)手撕鏈?zhǔn)蕉鏄?上)

    【數(shù)據(jù)結(jié)構(gòu)】---幾分鐘簡單幾步學(xué)會(huì)手撕鏈?zhǔn)蕉鏄?上)

    ??個(gè)人主頁:@小沈熬夜禿頭中???? ??小編介紹:歡迎來到我的亂七八糟小星球?? ??專欄:數(shù)據(jù)結(jié)構(gòu) ??本章內(nèi)容:手撕鏈?zhǔn)蕉鏄?送給各位??:我從沒覺得孤獨(dú) 說的浪漫點(diǎn) 我完全自由 記得 評(píng)論?? +點(diǎn)贊?? +收藏?? +關(guān)注??哦~ 提示:以下是本篇文章正文內(nèi)容,下

    2024年02月07日
    瀏覽(32)
  • 【數(shù)據(jù)結(jié)構(gòu)】---幾分鐘簡單幾步學(xué)會(huì)手撕鏈?zhǔn)蕉鏄?下)

    【數(shù)據(jù)結(jié)構(gòu)】---幾分鐘簡單幾步學(xué)會(huì)手撕鏈?zhǔn)蕉鏄?下)

    ??個(gè)人主頁:@小沈熬夜禿頭中???? ??小編介紹:歡迎來到我的亂七八糟小星球?? ??專欄:數(shù)據(jù)結(jié)構(gòu) ??本章內(nèi)容:手撕鏈?zhǔn)蕉鏄?送給各位??:成為更好的自己才是應(yīng)該做的事 記得 評(píng)論?? +點(diǎn)贊?? +收藏?? +關(guān)注??哦~ 提示:以下是本篇文章正文內(nèi)容,下面案例可

    2024年02月08日
    瀏覽(21)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹篇|超清晰圖解和詳解:二叉樹的序列化和反序列化

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹篇|超清晰圖解和詳解:二叉樹的序列化和反序列化

    博主簡介: 努力學(xué)習(xí)的22級(jí)計(jì)算機(jī)科學(xué)與技術(shù)本科生一枚?? 博主主頁: @是瑤瑤子啦 每日一言??: 你不能要求一片海洋,沒有風(fēng)暴,那不是海洋,是泥塘——畢淑敏 ?? 序列化 :本質(zhì)就是 二叉樹的遍歷 ,就那么幾個(gè):前序、中序、后序、層序。而序列化只不過就是 在遍歷到

    2024年02月10日
    瀏覽(20)
  • 數(shù)據(jù)結(jié)構(gòu)刷題篇 之 【力扣二叉樹基礎(chǔ)OJ】詳細(xì)講解(含每道題鏈接及遞歸圖解)

    數(shù)據(jù)結(jié)構(gòu)刷題篇 之 【力扣二叉樹基礎(chǔ)OJ】詳細(xì)講解(含每道題鏈接及遞歸圖解)

    有沒有一起拼用銀行卡的,取錢的時(shí)候我用,存錢的時(shí)候你用 難度等級(jí):? 直達(dá)鏈接:相同的樹 難度等級(jí):? 直達(dá)鏈接:單值二叉樹 難度等級(jí):?? 直達(dá)鏈接:對(duì)稱二叉樹 難度等級(jí):??? 直達(dá)鏈接:二叉樹的前序遍歷 難度等級(jí):???? 直達(dá)鏈接:另一顆子樹 注:

    2024年04月16日
    瀏覽(50)
  • 手撕二叉樹(圖解+代碼)

    手撕二叉樹(圖解+代碼)

    在學(xué)習(xí)二叉樹之前,我們有必要了解一下樹的概念和常用術(shù)語。接下來以下面這棵樹為例來回顧下樹相關(guān)概念: 樹的概念:一種非線性的數(shù)據(jù)結(jié)構(gòu),由n(n =0)個(gè)節(jié)點(diǎn)組成的一個(gè)具有層次關(guān)系的集合。 樹的術(shù)語: 節(jié)點(diǎn)的度 一個(gè)節(jié)點(diǎn)含有子樹的個(gè)數(shù),例如上圖中A的度為6. 樹的度

    2023年04月22日
    瀏覽(19)
  • 數(shù)據(jù)結(jié)構(gòu)---手撕圖解雙向循環(huán)鏈表

    數(shù)據(jù)結(jié)構(gòu)---手撕圖解雙向循環(huán)鏈表

    在前面學(xué)完單鏈表后,我們思考這樣一個(gè)問題,單鏈表和順序表比起來,功能確實(shí)相當(dāng)強(qiáng)大,有很多優(yōu)勢(shì),但是于此同時(shí),我們也應(yīng)思考下面的問題 單鏈表有什么不足的地方? 如果你把單鏈表的各個(gè)函數(shù)都自己實(shí)現(xiàn)過,那么下面的問題你一定有相同的感悟 單鏈表實(shí)現(xiàn)尾插尾

    2024年02月15日
    瀏覽(20)
  • 數(shù)據(jù)結(jié)構(gòu):手撕圖解雙向循環(huán)鏈表

    數(shù)據(jù)結(jié)構(gòu):手撕圖解雙向循環(huán)鏈表

    在前面學(xué)完單鏈表后,我們思考這樣一個(gè)問題,單鏈表和順序表比起來,功能確實(shí)相當(dāng)強(qiáng)大,有很多優(yōu)勢(shì),但是于此同時(shí),我們也應(yīng)思考下面的問題 單鏈表有什么不足的地方? 如果你把單鏈表的各個(gè)函數(shù)都自己實(shí)現(xiàn)過,那么下面的問題你一定有相同的感悟 單鏈表實(shí)現(xiàn)尾插尾

    2024年02月15日
    瀏覽(20)
  • 數(shù)據(jù)結(jié)構(gòu):手撕圖解七大排序(含動(dòng)圖演示)

    數(shù)據(jù)結(jié)構(gòu):手撕圖解七大排序(含動(dòng)圖演示)

    插入排序分為直接插入排序和希爾排序,其中希爾排序是很值得學(xué)習(xí)的算法 希爾排序的基礎(chǔ)是直接插入排序,先學(xué)習(xí)直接插入排序 直接插入排序類似于打撲克牌前的整牌的過程,假設(shè)我們現(xiàn)在有2 4 5 3四張牌,那么應(yīng)該怎么整牌? 方法很簡單,把3插到2和4中間,這樣就完成了

    2024年02月15日
    瀏覽(30)
  • 【C++圖解專欄】手撕數(shù)據(jù)結(jié)構(gòu)與算法,探尋算法的魅力

    【C++圖解專欄】手撕數(shù)據(jù)結(jié)構(gòu)與算法,探尋算法的魅力

    ?個(gè)人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343 ??專欄定位:為 0 基礎(chǔ)剛?cè)腴T數(shù)據(jù)結(jié)構(gòu)與算法的小伙伴提供詳細(xì)的講解,也歡迎大佬們一起交流~ ??專欄簡介:在這個(gè)專欄,我將帶著大家一起用 C++ 手撕基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)與算法,每一講都有詳細(xì)的講解,29 篇文章共

    2024年02月09日
    瀏覽(27)
  • 數(shù)據(jù)結(jié)構(gòu):手撕圖解堆的實(shí)現(xiàn)和TopK的應(yīng)用

    數(shù)據(jù)結(jié)構(gòu):手撕圖解堆的實(shí)現(xiàn)和TopK的應(yīng)用

    要講到堆,先要說兩個(gè)關(guān)于二叉樹的概念 滿二叉樹:一個(gè)二叉樹如果每一層的節(jié)點(diǎn)數(shù)都是最大值,那么這個(gè)二叉樹就是滿二叉樹 完全二叉樹:完全二叉樹是效率很高的數(shù)據(jù)結(jié)構(gòu),完全二叉樹是滿二叉樹的變形,對(duì)于深度為k的樹有n個(gè)節(jié)點(diǎn)的二叉樹,當(dāng)且僅當(dāng)其每一個(gè)節(jié)點(diǎn)都與

    2024年02月15日
    瀏覽(23)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包