最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言之平衡二叉樹詳解

 更新時(shí)間:2023年04月25日 14:23:33   作者:Cukor丘克  
平衡二叉樹是具有平衡屬性的有序二叉樹,本文主要介紹了C語言中的平衡二叉樹,具有一定的參考價(jià)值,需要的小伙伴可以參考閱讀

什么是平衡二叉樹

平衡二叉樹是具有平衡屬性的有序二叉樹,所謂的平衡即當(dāng)前樹的左右子樹高度差的絕對值不超過1。因?yàn)槠胶舛鏄涫怯商K聯(lián)數(shù)學(xué)家Adelson-Velskii和Landis提出,所以又稱為AVL樹。

平衡二叉樹的基本特點(diǎn)

  • 是特殊的有序二叉樹
  • 左右子樹高度差的絕對值不超過1
  • 左右子樹仍然是平衡二叉樹

為什么會出現(xiàn)平衡二叉樹

在學(xué)習(xí)平衡二叉樹之前必定已經(jīng)學(xué)過有序二叉樹,有序二叉樹的結(jié)構(gòu)特點(diǎn)就是將數(shù)據(jù)有序的排好,查找起來快,但是有序二叉樹有一個(gè)缺點(diǎn),就是當(dāng)節(jié)點(diǎn)呈現(xiàn)的狀態(tài)是一邊倒,那查找數(shù)據(jù)的時(shí)候就沒有發(fā)揮出二叉樹折半查找的優(yōu)勢了,這個(gè)時(shí)候是線性的查找(類似于鏈表的查找)。平衡二叉樹就是解決有序二叉樹一邊倒的問題。如果有序二叉樹是平衡的,那么查找數(shù)據(jù)就很快。時(shí)間復(fù)雜度為O ( l o g n ) O(logn)O(logn)。這樣就充分發(fā)揮了二叉樹的優(yōu)勢。

二叉樹四種不平衡的情況

當(dāng)樹的左右子樹高度差的絕對值大于1的時(shí)候就需要進(jìn)行旋轉(zhuǎn)操作,將不平衡的樹變成平衡的樹。以下是會出現(xiàn)的四種不平衡的情況。

  • 左左不平衡
  • 右右不平衡
  • 左右不平衡
  • 右左不平衡

左左不平衡旋轉(zhuǎn)成平衡狀態(tài):

右右不平衡旋轉(zhuǎn)成平衡狀態(tài):

左右不平衡旋轉(zhuǎn)成平衡狀態(tài):

右左不平衡旋轉(zhuǎn)成平衡狀態(tài):

上面是圖解這四種不平衡狀態(tài)旋轉(zhuǎn)成平衡狀態(tài)的情況。

C語言實(shí)現(xiàn)平衡二叉樹

平衡二叉樹的結(jié)構(gòu)體描述:

#define Ty int  //以整型數(shù)據(jù)為例
typedef struct Node
{
	Ty data;             //數(shù)據(jù)
	int height;          //高度
	struct Node* LChild; //左子樹
	struct Node* RChild; //右子樹
}Node,AVLTree;

初始化函數(shù):

AVLTree* creatAVLTree(Ty data)
{
    AVLTree* tree = (AVLTree*)malloc(sizeof(AVLTree));
    assert(tree);
    tree->data = data;
    tree->height = 0;
    tree->LChild = NULL;
    tree->RChild = NULL;
    return tree;
}

輔助宏函數(shù):

//輔助函數(shù)
#define HEIGHT(x) ((x==NULL)?(-1):(x->height))
#define MAX(a,b)  ((a>b)?(a):(b))
//獲取樹的新高度
#define GET_NEW_HEIGHT(x) (MAX(HEIGHT(x->LChild),HEIGHT(x->RChild)) + 1)

使用宏函數(shù)的好處是運(yùn)行過程中不需要進(jìn)行函數(shù)壓棧的操作,效率快一點(diǎn)。

前序遍歷平衡二叉樹:

//前序打印
void show_pre(AVLTree* root)
{
	if(root==NULL)
		return;
	printf("data:%d\theight:%d\n",root->data,root->height);
	show_pre(root->LChild);
	show_pre(root->RChild);
}

使用前序遍歷能更好地看出節(jié)點(diǎn)的高度,更方便還原平衡二叉樹。

四種不平衡狀態(tài)旋轉(zhuǎn)的算法實(shí)現(xiàn):

算法核心思想:找到新根的位置,然后進(jìn)行對應(yīng)的調(diào)整,最后返回新根的地址,這樣就實(shí)現(xiàn)了旋轉(zhuǎn)的操作,因?yàn)樾D(zhuǎn)后節(jié)點(diǎn)的高度改變了,所以在返回之前先調(diào)整一下節(jié)點(diǎn)的高度。

例如:左左不平衡進(jìn)行旋轉(zhuǎn)操作

因?yàn)槭亲笞蟛黄胶?,所以新根的位置是?dāng)前根的左子樹,那就使用一個(gè)指針(newRoot)去接收當(dāng)前根的左子樹,然后使勁將當(dāng)前根拉下來,讓新根代替當(dāng)前根的位置,那就必須將當(dāng)前根的LChild指向newRoot的右子樹(因?yàn)閚ewRoot不一定是空的所以不能直接讓curRoot→LChild指向空)。最后就是將newRoot→RChild指向curRoot這樣就把位置調(diào)整好了。在返回newRoot之前把curRoot和newRoot的高度調(diào)整一下。保持樹的準(zhǔn)確性。

其他的不平衡情況也是類似的操作。

//出現(xiàn)不平衡的情況
//左左不平衡
Node *LL_Rotation(Node *curRoot)
{
	Node *newRoot = curRoot->LChild;
	curRoot->LChild = newRoot->RChild;
	newRoot->RChild = curRoot;

	curRoot->height = GET_NEW_HEIGHT(curRoot);
	newRoot->height = GET_NEW_HEIGHT(newRoot);
	return newRoot;
}

//右右不平衡
Node *RR_Rotation(Node *curRoot)
{
	Node *newRoot = curRoot->RChild;
	curRoot->RChild = newRoot->LChild;
	newRoot->LChild = curRoot;

	curRoot->height = GET_NEW_HEIGHT(curRoot);
	newRoot->height = GET_NEW_HEIGHT(newRoot);
	return newRoot;
}
//左右不平衡
Node *LR_Rotation(Node *curRoot)
{
	curRoot->LChild = RR_Rotation(curRoot->LChild);
	return LL_Rotation(curRoot);
}
//右左不平衡
Node *RL_Rotation(Node *curRoot)
{
	curRoot->RChild = LL_Rotation(curRoot->RChild);
	return RR_Rotation(curRoot);
}

平衡二叉樹的插入操作:

插入操作需要考慮到四種情況:

  • 當(dāng)前節(jié)點(diǎn)是空的
  • 要插入進(jìn)來的數(shù)據(jù)比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)小
  • 要插入進(jìn)來的數(shù)據(jù)比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)大
  • 要插入進(jìn)來的數(shù)據(jù)和當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)一樣大

情況一的解決方案:直接申請一個(gè)節(jié)點(diǎn)內(nèi)存。

情況二的解決方案:遞歸往左邊跑,然后跑到對應(yīng)的位置就申請內(nèi)存,插入完成后判斷需不需要調(diào)整。

情況三的解決方案:遞歸往右邊跑,然后跑到對應(yīng)的位置就申請內(nèi)存,插入完成后判斷需不需要調(diào)整。

情況四的解決方案:因?yàn)槲覀冏龅氖且豢脹]有重復(fù)數(shù)據(jù)的平衡二叉樹所以遇到這種情況的時(shí)候不進(jìn)行插入操作。當(dāng)然如果做的是一棵可以有重復(fù)數(shù)據(jù)的平衡二叉樹,那遇到這種情況的時(shí)候可以個(gè)人的想法放左邊放右邊都可以。

源代碼:

//插入數(shù)據(jù)
Node *insertNode(Node *curRoot, Ty data)
{
	//插入分有四個(gè)大情況
	if (curRoot == NULL)			  //當(dāng)前節(jié)點(diǎn)是空的
		curRoot = creatAVLTree(data); //如果是空就直接創(chuàng)建一個(gè)新的節(jié)點(diǎn)
	else if (data < curRoot->data)	  //要插入的數(shù)據(jù)比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)小
	{
		//往左邊跑
		curRoot->LChild = insertNode(curRoot->LChild, data);
		//插入完成之后,判斷需不需要調(diào)整樹
		if (HEIGHT(curRoot->LChild) - HEIGHT(curRoot->RChild) == 2)
			//因?yàn)椴迦氲奈恢迷谧筮?,所以插入完成之后,左子樹的高度大于等于右子樹高?
			curRoot = data < curRoot->LChild->data ? LL_Rotation(curRoot) : LR_Rotation(curRoot);
	}
	else if (data > curRoot->data) //要插入的數(shù)據(jù)比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)大
	{
		//往右邊跑
		curRoot->RChild = insertNode(curRoot->RChild, data);
		if (HEIGHT(curRoot->RChild) - HEIGHT(curRoot->LChild) == 2)
			//因?yàn)椴迦氲奈恢迷谟疫?,所以插入完成之后,右子樹的高度大于等于左子樹高?
			curRoot = data > curRoot->RChild->data ? RR_Rotation(curRoot) : RL_Rotation(curRoot);
	}
	else //要插入的數(shù)據(jù)和當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)一樣大
		printf("無法插入數(shù)據(jù)\n");
	//獲取新高度
	curRoot->height = GET_NEW_HEIGHT(curRoot);
	return curRoot; //插入完成之后返回當(dāng)前節(jié)點(diǎn)的指針
}

平衡二叉樹的刪除操作:

刪除操作也是要考慮四個(gè)大情況:

  • 當(dāng)前節(jié)點(diǎn)是空的
  • 要?jiǎng)h除的數(shù)據(jù)比當(dāng)前數(shù)據(jù)小
  • 要?jiǎng)h除的數(shù)據(jù)比當(dāng)前數(shù)據(jù)大
  • 要?jiǎng)h除的數(shù)據(jù)和當(dāng)前數(shù)據(jù)一樣大

情況一的解決方案:沒有刪除的必要了,結(jié)束掉函數(shù)

情況二的解決方案:往左邊遞歸找到對應(yīng)位置,然后進(jìn)行刪除操作

情況三的解決方案:往右邊遞歸找到對應(yīng)位置,然后進(jìn)行刪除操作

情況四的解決方案:針對這個(gè)情況又要分為兩個(gè)小情況

  • 當(dāng)前節(jié)點(diǎn)的左子樹和右子樹都存在
  • 當(dāng)前節(jié)點(diǎn)的左右子樹至多有一個(gè)存在

具體解決方案看代碼和注釋

源代碼:

//查找節(jié)點(diǎn)
//找最大節(jié)點(diǎn)
Node *maxNode(Node *curRoot)
{
	if (curRoot == NULL)
		return NULL;
	//往右邊找
	while (curRoot->RChild)
		curRoot = curRoot->RChild;
	return curRoot;
}

//找最小節(jié)點(diǎn)
Node *minNode(Node *curRoot)
{
	if (curRoot == NULL)
		return NULL;
	while (curRoot->LChild)
		curRoot = curRoot->LChild;
	return curRoot;
}

//刪除數(shù)據(jù)
Node *deleteNode(Node *curRoot, Ty data)
{
	//刪除數(shù)據(jù)有四個(gè)大情況
	if (curRoot == NULL)	  //當(dāng)前節(jié)點(diǎn)是空的
		return NULL;		  //刪除了個(gè)寂寞直接結(jié)束掉整個(gè)函數(shù)
	if (data < curRoot->data) //要?jiǎng)h除的數(shù)據(jù)比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)小
	{
		//往左邊跑
		curRoot->LChild = deleteNode(curRoot->LChild, data);
		//獲取新高度
		curRoot->height = GET_NEW_HEIGHT(curRoot);
		//判斷需不需要調(diào)整
		if (HEIGHT(curRoot->RChild) - HEIGHT(curRoot->LChild) == 2)
			curRoot = HEIGHT(curRoot->RChild->LChild) > HEIGHT(curRoot->RChild->RChild) ? RL_Rotation(curRoot) : RR_Rotation(curRoot);
	}
	else if (data > curRoot->data) //要?jiǎng)h除的數(shù)據(jù)比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)大
	{
		//往右邊跑
		curRoot->RChild = deleteNode(curRoot->RChild, data);
		curRoot->height = GET_NEW_HEIGHT(curRoot);
		if (HEIGHT(curRoot->LChild) - HEIGHT(curRoot->RChild) == 2)
			curRoot = HEIGHT(curRoot->LChild->RChild) > HEIGHT(curRoot->LChild->LChild) ? LR_Rotation(curRoot) : LL_Rotation(curRoot);
	}
	else
	{ //要?jiǎng)h除的數(shù)據(jù)和當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)一樣大
		//針對于curRoot這個(gè)節(jié)點(diǎn)做刪除操作
		//主要有兩個(gè)主要的情況
		if (curRoot->LChild && curRoot->RChild) // curRoot有左子樹和右子樹
		{
			//先判斷左右子樹的高度,將高度比較高的子樹的節(jié)點(diǎn)拿到上面來
			if (HEIGHT(curRoot->LChild) > HEIGHT(curRoot->RChild))
			{ //左子樹的高度比右子樹的高度高
				//找到左子樹的最大節(jié)點(diǎn)
				Node *max = maxNode(curRoot->LChild);
				//找到之后就將max的數(shù)據(jù)替換curRoot的數(shù)據(jù)
				curRoot->data = max->data;
				//賦值完成之后繼續(xù)遞歸,然后釋放掉max對應(yīng)的節(jié)點(diǎn),不能直接在這里釋放,因?yàn)橐{(diào)整樹的高度
				curRoot->LChild = deleteNode(curRoot->LChild, max->data);
			}
			else
			{ //左子樹的高度小于等于右子樹的高度
				//找到右子樹的最小節(jié)點(diǎn)
				Node *min = minNode(curRoot->RChild);
				curRoot->data = min->data;
				curRoot->RChild = deleteNode(curRoot->RChild, min->data);
			}
		}
		else //上一種情況的否定,即curRoot沒有子樹或者只有一邊
		{
			//釋放內(nèi)存
			Node *temp = curRoot;
			// curRoot拿到存在的子樹
			curRoot = curRoot->LChild ? curRoot->LChild : curRoot->RChild;
			free(temp);
		}
	}

	return curRoot; //刪除完成之后就返回當(dāng)前節(jié)點(diǎn)
}

主函數(shù)測試:

int main()
{
	AVLTree *tree = NULL;
	for (int i = 1; i < 10; i++)
		tree = insertNode(tree, i);
	show_pre(tree); //前序打印樹
	printf("----------------------------\n");
	//刪除6這個(gè)節(jié)點(diǎn)
	tree = deleteNode(tree, 6);
	show_pre(tree);
	printf("程序結(jié)束\n");
	return 0;
}

運(yùn)行結(jié)果:

刪除前和刪除后的平衡二叉樹:

到此這篇關(guān)于C語言之平衡二叉樹詳解的文章就介紹到這了,更多相關(guān)C++平衡二叉樹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 圖文詳解C語言位運(yùn)算基礎(chǔ)知識

    圖文詳解C語言位運(yùn)算基礎(chǔ)知識

    這篇文章主要以圖文結(jié)合的方式為大家詳細(xì)介紹了C語言位運(yùn)算基礎(chǔ)知識,感興趣的小伙伴們可以參考一下
    2016-07-07
  • C++實(shí)現(xiàn)LeetCode(157.用Read4來讀取N個(gè)字符)

    C++實(shí)現(xiàn)LeetCode(157.用Read4來讀取N個(gè)字符)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(157.用Read4來讀取N個(gè)字符),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語言中sizeof()與strlen()函數(shù)的使用入門及對比

    C語言中sizeof()與strlen()函數(shù)的使用入門及對比

    這篇文章主要介紹了C語言中sizeof()與strlen()函數(shù)的使用入門及對比,同時(shí)二者在C++中的使用情況也基本上同理,是需要的朋友可以參考下
    2015-12-12
  • StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    這篇文章主要介紹了StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 深入理解char *a與char a[]的區(qū)別

    深入理解char *a與char a[]的區(qū)別

    很多人可能或多或少知道char *a與char a[]的一些區(qū)別,但如果詳細(xì)的說出來卻不知如何說去,下面這篇文章就給大家詳細(xì)介紹了關(guān)于C語言中char *a與char a[]的區(qū)別,有需要的朋友們可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2016-12-12
  • C++中繼承與組合的區(qū)別詳細(xì)解析

    C++中繼承與組合的區(qū)別詳細(xì)解析

    C++的“繼承”特性可以提高程序的可復(fù)用性。正因?yàn)椤袄^承”太有用、太容易用,才要防止亂用“繼承”
    2013-09-09
  • C語言中scanf函數(shù)與空格回車的用法說明

    C語言中scanf函數(shù)與空格回車的用法說明

    這篇文章主要介紹了C語言中scanf函數(shù)與空格回車的用法說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Matlab繪制散點(diǎn)密度圖的教程詳解

    Matlab繪制散點(diǎn)密度圖的教程詳解

    這篇文章主要介紹了如何使用MATLAB繪制散點(diǎn)密度圖(二維核密度),文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下
    2022-02-02
  • 用C語言實(shí)現(xiàn)通訊錄

    用C語言實(shí)現(xiàn)通訊錄

    這篇文章主要為大家詳細(xì)介紹了用C語言實(shí)現(xiàn)通訊錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C語言 OpenCV實(shí)現(xiàn)柱面投影

    C語言 OpenCV實(shí)現(xiàn)柱面投影

    在做全景拼接的時(shí)候,為了保持圖片中的空間約束與視覺的一致性,需要進(jìn)行柱面投影,否則離中心圖像距離越遠(yuǎn)的圖像拼接后變形越大。本文將具體介紹一下這如何實(shí)現(xiàn),需要的可以參考一下
    2021-12-12

最新評論

鄂伦春自治旗| 海口市| 林口县| 荆州市| 明光市| 英山县| 郯城县| 长治县| 赤峰市| 龙里县| 金坛市| 栾川县| 隆子县| 阿拉善左旗| 西安市| 东辽县| 瑞金市| 丰顺县| 和田县| 北川| 探索| 读书| 金阳县| 安新县| 凉山| 花垣县| 邳州市| 河津市| 夹江县| 云南省| 江西省| 沅江市| 东海县| 射洪县| 金山区| 博乐市| 北流市| 阿巴嘎旗| 柳林县| 高要市| 邹城市|