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

如何使用C語言實現(xiàn)平衡二叉樹數(shù)據(jù)結構算法

 更新時間:2021年08月19日 16:54:29   作者:sehun?  
對于判斷是否為平衡二叉樹而言,我們需要知道以下特性:是一個二叉樹也是一個二叉排序樹該樹的每個結點上的(深度)左子樹 - 右子樹的值為平衡因子(BF(Balance Factor))該樹的每一個節(jié)點的左子樹和右子樹的高度至多等于1(平衡因子只可能是-1,0,1)

前言

對于一個二叉排序樹而言

  • 它們的結構都是根據(jù)了二叉樹的特性從最左子樹開始在回到該結點上繼續(xù)往右結點走
  • 通過該方式進行遞歸操作,并且該二叉排序樹的結構也是從小到大依次顯示
  • 那么我們假設a[10]={ 3,2,1,4,5,6,7,10,9,8 };我們需要查找改列表中的某一個結點的值

那么我們通過二叉排序樹的展示,會展示成如圖:

在這里插入圖片描述 

可以發(fā)現(xiàn),如果我們想通過二叉排序樹這個深度為8的樹來查找某個數(shù)

我們需要走到最后,這是最壞的打算

但是如果我們能將該樹改變?yōu)槠胶舛鏄?,如圖所示:

在這里插入圖片描述 

相比之下我們這個深度為4的平衡二叉樹在通過查詢值時是效率很平均且穩(wěn)定的,

那么通過上面的問題我們該怎么將這個二叉排序樹的變成平衡二叉樹是我們現(xiàn)在的問題了。

一、平衡二叉樹實現(xiàn)原理

對于平衡二叉樹的特性而言,我們在上面也有舉例到

  • 在二叉排序樹中,按照它的從小到大的結構所排序下來的結點,
  • 當它的平衡因子的絕對值大于1的結點的根的子樹,我們稱為最小不平衡子樹。

而我們該如何去計算該二叉排序樹的平衡因子(平衡因子 = 左子樹 - 右子樹的值)呢?

我們可以結合上面那個最壞打算的二叉排序樹的生成方式進行逐一分析。

如圖:

在這里插入圖片描述

可以看到當我們單步調試二叉排序樹的時候到值1的時候值3的平衡因子為2>1

  • (左子樹有值2、值1所以是2-右子樹0所以等于2)

那么此時并不符合我們的平衡二叉樹的規(guī)則

那么我們需要通過某種形式去調換,使其符合我們的平衡二叉樹的規(guī)則

而圖2就是由圖1通過順時針而得到出來的,在這里我們稱為右旋

  • 此時我們所有的平衡因子<2那么又恢復了正常

之后我們的值4繼續(xù)插入,因為值4>值3,所以根據(jù)二叉排序樹的特性是值3的右子樹

  • 此時我們查看圖3發(fā)現(xiàn)所有的平衡因子<2,

那么也沒出現(xiàn)什么問題之后繼續(xù)插入下一個值5,因為值5>值4,所以值5會到值4的右子樹上。

如圖所示:

在這里插入圖片描述

可以發(fā)現(xiàn)圖4中當插入值5的時候,根節(jié)點、值3的平衡因子的絕對值=2

  • 所以是不符合平衡條件的所以此時我們通過逆時針的方式去旋轉值3下面的所有結點
  • 在這里我們稱為左旋,得到的結果即為圖5所示了,且此時每個結點的平衡因子都<2,恢復平衡了

剩下就不多去演示了,整體是一個遞歸的過程

重要:在過程中反復的把不平衡的結點通過左、右旋的方式將其調整回平衡二叉樹。

要想深刻了解平衡二叉樹的實現(xiàn)原理請自行參考程杰著作的《大話數(shù)據(jù)結構》。

二、平衡二叉樹實現(xiàn)算法

在類型上我們會通過二叉排序樹的基礎上添加一個bf,用于存儲平衡因子,而定義了一個Status則是判斷當前狀態(tài)的代碼。

代碼如下:

typedef int Status;	/* Status是函數(shù)的類型,其值是函數(shù)結果狀態(tài)代碼,如OK等 */
/* 二叉樹的二叉鏈表結點結構定義 */
typedef  struct BiTNode	/* 結點結構 */
{
	int data;	/* 結點數(shù)據(jù) */
	int bf; /*  結點的平衡因子 */
	struct BiTNode* lchild, * rchild;	/* 左右孩子指針 */
} BiTNode, * BiTree;

為了方便理解,我們前面的提到的左旋和右旋的方式,在c語言的代碼中該如何實現(xiàn)呢?下面我們就來將代碼展示出來。

代碼如下:

void R_Rotate(BiTree* P)
{
	BiTree L;
	L = (*P)->lchild; /*  L指向P的左子樹根結點 */
	(*P)->lchild = L->rchild; /*  L的右子樹掛接為P的左子樹 */
	L->rchild = (*P);
	*P = L; /*  P指向新的根結點 */
}
/* 對以P為根的二叉排序樹作左旋處理, */
/* 處理之后P指向新的樹根結點,即旋轉處理之前的右子樹的根結點0  */
void L_Rotate(BiTree* P)
{
	BiTree R;
	R = (*P)->rchild; /*  R指向P的右子樹根結點 */
	(*P)->rchild = R->lchild; /* R的左子樹掛接為P的右子樹 */
	R->lchild = (*P);
	*P = R; /*  P指向新的根結點 */
}

我們可以看到左旋和右旋的代碼很類似,所以我們通過了解了右旋的代碼的含義自然就可以反推出左旋的含義了。

此函數(shù)的意思是:

  • 當某一個結點P處于一個不平衡狀態(tài)的時候,且平衡因子為正數(shù)需要右旋(順時針),并將它的左孩子結點定義為L。
  • 將L的右子樹變成P的左子樹,再將P改成L的右子樹,最后將L替換P成功根結點。

如果文字還不了解我們可以將上述圖1、圖2的圖片進行推導。

如圖:

在這里插入圖片描述 

那么左旋的操作就是一個逆時針的方向轉換,代碼也是類似這里就不再演示了。

  • 但是在這里我們還有另一種情況,就是當有兩個或以上的平衡因子的絕對值>=2的時候,它們的平衡因子的值可能為負也可能為正,那么這種情況我們就要考慮到雙旋操作了,
  • 我們假設以樹根為頂點的左右子樹都需要考慮,且它們是相對的,所以我們可以通過了解左平衡旋轉處理也就繼而反推出右平衡旋轉處理了。

現(xiàn)在我們來看左平衡旋轉處理的代碼函數(shù):

#define LH +1 /*  左高 */ 
#define EH 0  /*  等高 */ 
#define RH -1 /*  右高 */ 
/*  對以指針T所指結點為根的二叉樹作右平衡旋轉處理, */
/*  本算法結束時,指針T指向新的根結點 */
void RightBalance(BiTree* T)
{
	BiTree R, Rl;
	R = (*T)->rchild; /*  R指向T的右子樹根結點 */
	switch (R->bf)
	{ /*  檢查T的右子樹的平衡度,并作相應平衡處理 */
	case RH: /*  新結點插入在T的右孩子的右子樹上,要作單左旋處理 */
		(*T)->bf = R->bf = EH;
		L_Rotate(T);
		break;
	case LH: /*  新結點插入在T的右孩子的左子樹上,要作雙旋處理 */
		Rl = R->lchild; /*  Rl指向T的右孩子的左子樹根 */
		switch (Rl->bf)
		{ /*  修改T及其右孩子的平衡因子 */
		case RH: (*T)->bf = LH;
			R->bf = EH;
			break;
		case EH: (*T)->bf = R->bf = EH;
			break;
		case LH: (*T)->bf = EH;
			R->bf = RH;
			break;
		}
		Rl->bf = EH;
		R_Rotate(&(*T)->rchild); /*  對T的右子樹作右旋平衡處理 */
		L_Rotate(T); /*  對T作左旋平衡處理 */
	}
}
/*  對以指針T所指結點為根的二叉樹作左平衡旋轉處理 */
/*  本算法結束時,指針T指向新的根結點 */
void LeftBalance(BiTree* T)
{
	BiTree L, Lr;
	L = (*T)->lchild; /*  L指向T的左子樹根結點 */
	switch (L->bf)
	{ /*  檢查T的左子樹的平衡度,并作相應平衡處理 */
	case LH: /*  新結點插入在T的左孩子的左子樹上,要作單右旋處理 */
		(*T)->bf = L->bf = EH;
		R_Rotate(T);
		break;
	case RH: /*  新結點插入在T的左孩子的右子樹上,要作雙旋處理 */
		Lr = L->rchild; /*  Lr指向T的左孩子的右子樹根 */
		switch (Lr->bf)
		{ /*  修改T及其左孩子的平衡因子 */
		case LH: (*T)->bf = RH;
			L->bf = EH;
			break;
		case EH: (*T)->bf = L->bf = EH;
			break;
		case RH: (*T)->bf = EH;
			L->bf = LH;
			break;
		}
		Lr->bf = EH;
		L_Rotate(&(*T)->lchild); /*  對T的左子樹作左旋平衡處理 */
		R_Rotate(T); /*  對T作右旋平衡處理 */
	}
}


如圖:

在這里插入圖片描述

首先通過代碼可以看到,我們定義了三個常數(shù)變量,分別代碼1、0、-1。

然后呢因為是左平衡旋轉處理我們需要獲取當前圖片所示的P結點(也就是我之前假設的根節(jié)點的頂點)的左子樹,

這里要注意重點:

  • 當我們這個函數(shù)可以執(zhí)行的時候已經(jīng)確認了當前子樹是不平衡的狀態(tài);
  • 且結點N的值(即新插入結點) < 結點P的值(即上述圖片根結點P)和結點P->左子樹高于結點P->右子樹需要調整;
  • 并且你還要記住此時進來的結點P才是不平衡狀態(tài),且通過該P結點->左子樹的平衡因子來進行分支判斷。
  1. 如果等于1了則執(zhí)行一次右旋操作,即上圖右旋推導的并且把它們的BF值(平衡因子)都改為0操作;
  2. 反之如果等于-1了則像上圖一樣進行雙旋操作;
  3. 不過在那之前還要通過獲取圖2上L的右子樹LR(貼合代碼上的Lr);
  4. 然后再判斷一下Lr的平衡因子的值并再次進行分支判斷;
  5. 修改根節(jié)點P(即代碼中的根節(jié)點T)根據(jù)Lr的BF值進行修改它們的BF值;
  6. 最后在將Lr的BF值賦為0,進行雙旋操作這里先左旋后右旋;
  7. 那么右平衡旋轉處理函數(shù)則取反。

三、全部代碼

#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存儲空間初始分配量 */
typedef int Status;	/* Status是函數(shù)的類型,其值是函數(shù)結果狀態(tài)代碼,如OK等 */
/* 二叉樹的二叉鏈表結點結構定義 */
typedef  struct BiTNode	/* 結點結構 */
{
	int data;	/* 結點數(shù)據(jù) */
	int bf; /*  結點的平衡因子 */
	struct BiTNode* lchild, * rchild;	/* 左右孩子指針 */
} BiTNode, * BiTree;
/* 對以p為根的二叉排序樹作右旋處理, */
/* 處理之后p指向新的樹根結點,即旋轉處理之前的左子樹的根結點 */
void R_Rotate(BiTree* P)
{
	BiTree L;
	L = (*P)->lchild; /*  L指向P的左子樹根結點 */
	(*P)->lchild = L->rchild; /*  L的右子樹掛接為P的左子樹 */
	L->rchild = (*P);
	*P = L; /*  P指向新的根結點 */
}

/* 對以P為根的二叉排序樹作左旋處理, */
/* 處理之后P指向新的樹根結點,即旋轉處理之前的右子樹的根結點0  */
void L_Rotate(BiTree* P)
{
	BiTree R;
	R = (*P)->rchild; /*  R指向P的右子樹根結點 */
	(*P)->rchild = R->lchild; /* R的左子樹掛接為P的右子樹 */
	R->lchild = (*P);
	*P = R; /*  P指向新的根結點 */
}
#define LH +1 /*  左高 */ 
#define EH 0  /*  等高 */ 
#define RH -1 /*  右高 */ 
/*  對以指針T所指結點為根的二叉樹作左平衡旋轉處理 */
/*  本算法結束時,指針T指向新的根結點 */
void LeftBalance(BiTree* T)
{
	BiTree L, Lr;
	L = (*T)->lchild; /*  L指向T的左子樹根結點 */
	switch (L->bf)
	{ /*  檢查T的左子樹的平衡度,并作相應平衡處理 */
	case LH: /*  新結點插入在T的左孩子的左子樹上,要作單右旋處理 */
		(*T)->bf = L->bf = EH;
		R_Rotate(T);
		break;
	case RH: /*  新結點插入在T的左孩子的右子樹上,要作雙旋處理 */
		Lr = L->rchild; /*  Lr指向T的左孩子的右子樹根 */
		switch (Lr->bf)
		{ /*  修改T及其左孩子的平衡因子 */
		case LH: (*T)->bf = RH;
			L->bf = EH;
			break;
		case EH: (*T)->bf = L->bf = EH;
			break;
		case RH: (*T)->bf = EH;
			L->bf = LH;
			break;
		}
		Lr->bf = EH;
		L_Rotate(&(*T)->lchild); /*  對T的左子樹作左旋平衡處理 */
		R_Rotate(T); /*  對T作右旋平衡處理 */
	}
}
/*  對以指針T所指結點為根的二叉樹作右平衡旋轉處理, */
/*  本算法結束時,指針T指向新的根結點 */
void RightBalance(BiTree* T)
{
	BiTree R, Rl;
	R = (*T)->rchild; /*  R指向T的右子樹根結點 */
	switch (R->bf)
	{ /*  檢查T的右子樹的平衡度,并作相應平衡處理 */
	case RH: /*  新結點插入在T的右孩子的右子樹上,要作單左旋處理 */
		(*T)->bf = R->bf = EH;
		L_Rotate(T);
		break;
	case LH: /*  新結點插入在T的右孩子的左子樹上,要作雙旋處理 */
		Rl = R->lchild; /*  Rl指向T的右孩子的左子樹根 */
		switch (Rl->bf)
		{ /*  修改T及其右孩子的平衡因子 */
		case RH: (*T)->bf = LH;
			R->bf = EH;
			break;
		case EH: (*T)->bf = R->bf = EH;
			break;
		case LH: (*T)->bf = EH;
			R->bf = RH;
			break;
		}
		Rl->bf = EH;
		R_Rotate(&(*T)->rchild); /*  對T的右子樹作右旋平衡處理 */
		L_Rotate(T); /*  對T作左旋平衡處理 */
	}
}
/*  若在平衡的二叉排序樹T中不存在和e有相同關鍵字的結點,則插入一個 */
/*  數(shù)據(jù)元素為e的新結點,并返回1,否則返回0。若因插入而使二叉排序樹 */
/*  失去平衡,則作平衡旋轉處理,布爾變量taller反映T長高與否。 */
Status InsertAVL(BiTree* T, int e, Status* taller)
{
	if (!*T)
	{ /*  插入新結點,樹“長高”,置taller為TRUE */
		*T = (BiTree)malloc(sizeof(BiTNode));
		(*T)->data = e; (*T)->lchild = (*T)->rchild = NULL; (*T)->bf = EH;
		*taller = TRUE;
	}
	else
	{
		if (e == (*T)->data)
		{ /*  樹中已存在和e有相同關鍵字的結點則不再插入 */
			*taller = FALSE; return FALSE;
		}
		if (e < (*T)->data)
		{ /*  應繼續(xù)在T的左子樹中進行搜索 */
			if (!InsertAVL(&(*T)->lchild, e, taller)) /*  未插入 */
				return FALSE;
			if (*taller) /*   已插入到T的左子樹中且左子樹“長高” */
				switch ((*T)->bf) /*  檢查T的平衡度 */
				{
				case LH: /*  原本左子樹比右子樹高,需要作左平衡處理 */
					LeftBalance(T);	*taller = FALSE; break;
				case EH: /*  原本左、右子樹等高,現(xiàn)因左子樹增高而使樹增高 */
					(*T)->bf = LH; *taller = TRUE; break;
				case RH: /*  原本右子樹比左子樹高,現(xiàn)左、右子樹等高 */
					(*T)->bf = EH; *taller = FALSE; break;
				}
		}
		else
		{ /*  應繼續(xù)在T的右子樹中進行搜索 */
			if (!InsertAVL(&(*T)->rchild, e, taller)) /*  未插入 */
				return FALSE;
			if (*taller) /*  已插入到T的右子樹且右子樹“長高” */
				switch ((*T)->bf) /*  檢查T的平衡度 */
				{
				case LH: /*  原本左子樹比右子樹高,現(xiàn)左、右子樹等高 */
					(*T)->bf = EH; *taller = FALSE;	break;
				case EH: /*  原本左、右子樹等高,現(xiàn)因右子樹增高而使樹增高  */
					(*T)->bf = RH; *taller = TRUE; break;
				case RH: /*  原本右子樹比左子樹高,需要作右平衡處理 */
					RightBalance(T); *taller = FALSE; break;
				}
		}
	}
	return TRUE;
}
int main(void)
{
	int i;
	int a[10] = { 3,2,1,4,5,6,7,10,9,8 };
	BiTree T = NULL;
	Status taller;
	for (i = 0; i < 10; i++)
	{
		InsertAVL(&T, a[i], &taller);
	}
	printf("本樣例建議斷點跟蹤查看平衡二叉樹結構");
	return 0;
}

到此這篇關于如何使用C語言實現(xiàn)平衡二叉樹 的文章就介紹到這了,更多相關C語言實現(xiàn)平衡二叉樹內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C++類與對象之日期類的實現(xiàn)

    C++類與對象之日期類的實現(xiàn)

    這篇文章主要介紹如何實現(xiàn)C++中的日期類相關資料,需要的朋友可以參考下面文章的具體內容
    2021-09-09
  • C語言利用system調用系統(tǒng)命令行詳情

    C語言利用system調用系統(tǒng)命令行詳情

    這篇文章主要介紹了C語言利用system調用系統(tǒng)命令行詳情,system就是調用系統(tǒng)命令行,輸入為字符串,然后把這個字符串輸出給命令行,讓命令行執(zhí)行。下文的具體內容,需要的小伙伴可以參考一下
    2022-01-01
  • 詳細解讀C++編程中的匿名類類型和位域

    詳細解讀C++編程中的匿名類類型和位域

    這篇文章主要介紹了C++編程中的匿名類類型和位域,是C++入門學習中的基礎知識,需要的朋友可以參考下
    2016-01-01
  • C++中的運算符和運算符優(yōu)先級總結

    C++中的運算符和運算符優(yōu)先級總結

    這篇文章主要介紹了C++中的運算符和運算符優(yōu)先級總結,主要整理了算術、關系、邏輯、位和賦值運算符的用法,需要的朋友可以參考下
    2016-05-05
  • C++線性時間的排序算法分析

    C++線性時間的排序算法分析

    這篇文章主要介紹了C++線性時間的排序算法分析,是非常經(jīng)典的非比較排序算法,對于C++程序員有很大的借鑒價值,需要的朋友可以參考下
    2014-08-08
  • C語言詳細解析有符號數(shù)與無符號數(shù)的表示

    C語言詳細解析有符號數(shù)與無符號數(shù)的表示

    我們知道,在C語言中存在無符號數(shù)和有符號數(shù),但是對于計算機而言,其本身并不區(qū)別有符號數(shù)和無符號數(shù),因為在計算機里面都是O或者1,但是在我們的實際使用中有時候需要使用有符號數(shù)來表示一個整數(shù),因此我們規(guī)定,當最高位為1的時,表示為負數(shù),最高位為0時,表示為正數(shù)
    2022-04-04
  • 基于Qt開發(fā)獲取CTP量化交易接口測試數(shù)據(jù)工具

    基于Qt開發(fā)獲取CTP量化交易接口測試數(shù)據(jù)工具

    這篇文章主要為大家詳細介紹了如何使用Qt軟件開發(fā)K線股P相關軟件,先開發(fā)一個通過CTP量化交易的sdk獲取相關推送數(shù)據(jù)的工具,需要的可以參考下
    2024-04-04
  • C語言代碼實現(xiàn)通訊錄管理系統(tǒng)

    C語言代碼實現(xiàn)通訊錄管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言代碼實現(xiàn)通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C++與QML進行數(shù)據(jù)交互實現(xiàn)方式介紹

    C++與QML進行數(shù)據(jù)交互實現(xiàn)方式介紹

    迫于無奈開始寫android的程序,以前使用QWidget的方式試過,雖然界面可以實現(xiàn),但是最后調用攝像頭時,未能成功,再沒有繼續(xù)。這幾天開始使用qml進行嘗試,在使用的過程中,其中的一個難點,就是在qml與c++中數(shù)據(jù)的交互
    2022-09-09
  • C語言超詳細講解隊列的實現(xiàn)及代碼

    C語言超詳細講解隊列的實現(xiàn)及代碼

    隊列(Queue)與棧一樣,是一種線性存儲結構,它具有如下特點:隊列中的數(shù)據(jù)元素遵循“先進先出”(First?In?First?Out)的原則,簡稱FIFO結構。在隊尾添加元素,在隊頭刪除元素
    2022-04-04

最新評論

静乐县| 恩施市| 盘山县| 巴塘县| 武邑县| 分宜县| 来安县| 上蔡县| 西畴县| 石嘴山市| 吉安县| 泸水县| 清涧县| 综艺| 囊谦县| 景谷| 北碚区| 犍为县| 藁城市| 陇南市| 常宁市| 阿拉尔市| 沈阳市| 淮南市| 银川市| 临高县| 涞水县| 荃湾区| 射阳县| 扎赉特旗| 闽清县| 普兰店市| 且末县| 武汉市| 武城县| 桂东县| 永寿县| 宝清县| 绥化市| 祁门县| 澜沧|