【一份程式碼搞定資料結構】紅黑樹(更新中)

Bonstoppo發表於2018-09-26

在介紹紅黑樹之前先說明什麼是二叉查詢樹和平衡二叉樹。

一、二叉查詢樹(BST)

1 . 二叉查詢樹本身就是一個空樹。

2 . 左子樹的數值 < 根節點 < 右子樹的數值。

基本四個操作:查詢 , 插入 , 建樹 , 刪除。由於不是重點要說明的,所以只是貼出來用一下。

#include<iostream>
using namespace std;

struct node{
	int data;
	node* lchild;
	node* rchild;
};

node* newNode(int x){
	node* Node = new node;
	Node->data = x;
	Node->lchild = NULL:
	Node->rchild = NULL;
	return Node;
} 

void search(node* root , int x){
	//根節點的數值// 
	if(root == NULL){
		printf("search failed!\n");
		return ;
	}
	//找到的話// 
	if(x == root->data){
		printf("%d\n" , root->data);
	}
	//比它小,往左走// 
	else if(x < root->data){
		search(root->lchild , x);
	}
	//比它大 ,往右走// 
	else{
		search(root->rchild , x);
	}
}

//插入操作//
void insert(node* root , int x){
	if(root == NULL){
		root = newNode(x);
		return ;
	}
	if(x == root->data){
		return ;
	}
	else if(x < root->lchild){
		insert(root->lchild , x);
	}
	else{
		insert(root->rchild , x);
	}
} 
//輔助刪除函式,用於找一個數值的節點// 
node* findMax(node* root){
	while(root->rchild != NULL){
		root = root->rchild;
	}
	return ;
}

node* findMin(node* root){
	while(root->lchild != NULL){
		root = root->lchild;
	}
	return ;
}

void deleteNode(node* &root , int x){
	//範圍// 
	if(root == NULL){
		return ;
	}
	//當輸入的數值和要刪除的數值是一樣的話// 
	if(root->data == x){
		//本身數值沒有左右子節點,放空即可// 
		if(root->lchild == NULL && root->rchild == NULL){
			root = NULL;
		}
		//如果左結點不空的時候// 
		else if(root->lchild != NULL){
			//刪除結點的數值// 
			node* pre = findMax(root->lchild);
			root->data = pre->data;
			deleteNode(root->lchild , pre->data);
		}
		//如果右結點不空的時候// 
		else{
			node* next = findMin(root->rchild);
			root->data = next->data;
			deleteNode(root->rchild , next->data);
		}
	//如果找的左右節點的數值不一樣的話,接著找.// 
	else if(root->data > x){
		deleteNode(root->lchild , x);
	}
	else{
		deleteNode(root->rchild , x);		
	} 
}

 

二、平衡二叉樹(AVL)

平衡二叉樹最關鍵的一點就是結點的平衡因子。所謂平衡因子就是左子樹的數值和右子樹的高度差。當每個結點的平衡因子的數值的決定不超過1的時候,即為平衡二叉樹。

基本操作幾乎一致,主要是旋轉註意一下。

#include<iostream>
using namespace std;

struct node{
	int v ;
	int height;//當前結點高度// 
	node* lchild;
	node* rchild;
}; 

//建立結點//
node* newNode(int v){
	node* Node = new node;
	Node->v = v;
	Node->height = 1;
	Node->rchild = NULL;
	Node->lchild = NULL;
	return Node;
} 

//結點高度函式//
int getheight(node* root){
	//結點為空// 
	if(root == NULL)	
		return 0;
	return root->height;
} 

//平衡因子的計算//
int getBalance(node* root){
	return getheight(root->lchild) - getheight(root->rchild);
} 

//更新結點的高度//
void updateheight(node *root){
	root->height = max(getheight(root->lchild) , getheight(root->rchild) + 1);
} 

//查詢操作//
void search(node* root ,int x){
	if(root == NULL){
		printf("search Falled!\n");
		return ;
	}
	//找到即輸出// 
	if(x == root->v){
		printf("%d\n" , root->v);
	}
	//類似二叉搜尋樹查詢// 
	else if(x < root->v){
		search(root->lchild , x);
	}
	else{
		search(root->rchild , x);
	}
} 

//左旋// 
void L(node* &root){
	node *temp = root->rchild;
	root->rchild = temp->rchild;
	temp->lchild = root;
	updateheight(root);
	updateheight(temp);
	root = temp;
}

//右旋//
void R(node* &root){
	node *temp = root->lchild;
	root->lchild = temp->lchild;
	temp->rchild = root;
	updateheight(root);
	updateheight(temp);
	root = temp;
}

void insert(node* &root , int x){
	if(root == NULL){
		root = newNode(x);
		return ;
	}
	//類似平衡二叉樹的插入演算法 , 加了一點東西// 
	if(x < root->v){
		insert(root->lchild , x);
		//更新高度//
		updateheight(root);
		if(getBalance(root) == 2){
			if(getBalance(root->lchild) == 1){
				R(root);
			}
			else if(getBalance(root->rchild) == -1){
				L(root->lchild);
				R(root);
			}
		}
	}
	else{
		insert(root->rchild , x);
		updateheight(root);
		if(getBalance(root) == -2){
			if(getBalance(root->lchild) == -1){
				L(root);
			}
			else if(getBalance(root->rchild) == 1){
				R(root->rchild);
				L(root);
			}
		}
	}
}

//新建二叉樹// 
node* creat(int data[] , int n){
	node* root = NULL;
	//迴圈插入// 
	for(int i = 0 ; i < n ; i ++) {
		insert(root , data[i]);
	}
	return root;
}

 

三、最終進化 -- 紅黑樹(RBT)

紅黑樹的起源,自然是二叉查詢樹。這種樹結構從根節點開始,左子節點小於它,右子節點大於它。每個節點都符合這個特性,所以易於查詢,是一種很好的資料結構。但是它有一個問題,就是容易偏向某一側,這樣就像一個連結串列結構了,失去了樹結構的優點,查詢時間會變壞, 在這種需求下,平衡樹的概念就應運而生了

紅黑樹的用途:

  • STL(標準模板庫)中在set map是基於紅黑樹實現的。
  • Java中在TreeMap使用的也是紅黑樹。
  • epoll在核心中的實現,用紅黑樹管理事件塊。
  • linux程式排程Completely Fair Scheduler,用紅黑樹管理程式控制塊

紅黑樹與他們的前輩們的比較:

  • 從實現細節上來講,如果插入一個結點引起了樹的不平衡,AVL樹和紅黑樹都最多需要2次旋轉操作,即兩者都是O(1);但是在刪除node引起樹的不平衡時,最壞情況下,AVL需要維護從被刪node到root這條路徑上所有node的平衡性,因此需要旋轉的量級O(logN),而RB-Tree最多隻需3次旋轉,只需要O(1)的複雜度。

  • 從兩種平衡樹對平衡的要求來講,AVL的結構相較RB-Tree來說更為平衡,在插入和刪除node更容易引起Tree的unbalance,因此在大量資料需要插入或者刪除時,AVL需要rebalance的頻率會更高。因此,RB-Tree在需要大量插入和刪除node的場景下,效率更高。自然,由於AVL高度平衡,因此AVL的search效率更高。

紅黑樹的基本的特點:

  1. 根是黑色。
  2. 所有葉子都是黑色(葉子是NIL結點)。
  3. 每個紅色結點必須有兩個黑色的子結點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色結點。)
  4. 從任一結點到其每個葉子的所有簡單路徑都包含相同數目的黑色結點。
  5. http://www.cnblogs.com/skywang12345/p/3624202.html
  6. https://www.sohu.com/a/201923614_466939

 

相關文章