rbtree 設計

什麼是樹?
大街上到處都是,大夥別說沒看過,除非你在沙漠中。
樹大致可以分爲根,莖,枝,葉。
大樹枝會套小樹枝,樹枝上都有葉子。
西方人有個聖誕節,聖誕來了,聖庭老人在樹上的每個枝丫上都掛了禮物,禮物上都寫上一個小朋友的名字,讓小朋友去樹上摘。
問題來了,現在有兩顆樹,
A                                                                  B


A是身材高大的水杉,不管有多少禮物,總有一個高度能滿足你。
B是不太高但枝繁葉茂的荔枝樹,每個枝丫的高度都差不多。
選哪顆樹呢,如果選擇A,分在低枝上的小朋友很容易拿,高一點的就會抱怨了。
看來要果斷點選擇B了。
可是還有一個問題,如何才能讓小朋友快速知道自己的禮物在哪個枝上呢?

這個問題直到1972年才被Rudolf Bayer這個傢伙解決了,解決思路是這樣的:

1.把所有枝丫都掛個顏色標籤,要麼是紅的要麼是黑的。
2.根枝丫是黑的。
3.每顆葉子都是黑色的。
4.紅色枝丫的分枝或葉子是黑色的。
5.任一枝丫到其分枝的任何一個葉子路徑中都經過相同的黑色支丫。
6.禮物掛在枝丫或者葉子上
7.按小朋友的出生年月排序掛上。

看問題看實質,這個解決方案的實質是解決了小朋友的公平性,聖誕老人要累點,每插入一個禮物,可能都要改變一下相鄰節點的方位,沒關係,聖誕老人可以請零時工哈,樹枝掛斷了也不用負責多好。

這樣,我們的紅黑樹就應運而生了。

我們先來看看紅黑樹的定義,維基百科上是這麼說的:
紅黑樹是一種自平衡二叉查找樹,是在計算機科學中用到的一種數據結構,典型的用途是實現關聯數組。它是在1972年由魯道夫·貝爾發明的,他稱之爲"對稱二叉B樹",它現代的名字是在 Leo J. Guibas 和 Robert Sedgewick 於1978年寫的一篇論文中獲得的。它是複雜的,但它的操作有着良好的最壞情況運行時間,並且在實踐中是高效的: 它可以在O(log n)時間內做查找,插入和刪除,這裏的n是樹中元素的數目。
紅黑樹具有以下的性質:
紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色爲紅色或黑色。在二叉查找樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求:
性質1. 節點是紅色或黑色。
性質2. 根是黑色。
性質3. 所有葉子都是黑色(葉子是NIL節點)。
性質4. 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
性質5. 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

實際開發中,如何定義一個紅黑樹呢?
我是這樣定義的:

#define YUMEI_RBTREE_COLOR_BLACK  0
#define YUMEI_RBTREE_COLOR_RED    1

typedef struct yumei_rbtree_s yumei_rbtree_t;
typedef struct yumei_rbtree_node_s yumei_rbtree_node_t;
typedef struct yumei_rbtree_key_s yumei_rbtree_key_t;

struct yumei_rbtree_key_s
{
	int key[ 8 ];
};

struct yumei_rbtree_s
{
	yumei_rbtree_node_t   *root;
	yumei_rbtree_node_t   *leaf;
	int                    count;

	yumei_mem_pool_t      *pool;
};

struct yumei_rbtree_node_s
{
	yumei_rbtree_node_t *left;
	yumei_rbtree_node_t *right;
	yumei_rbtree_node_t *parent;
	int                  color;

	yumei_rbtree_key_t   key;
	long                 data[0];

};

#define yumei_rbtree_black( node ) \
	( node )->color = YUMEI_RBTREE_COLOR_BLACK 
#define yumei_rbtree_red( node ) \
	( node )->color = YUMEI_RBTREE_COLOR_RED

#define yumei_rbtree_is_red( node ) \
	( node )->color

#define yumei_rbtree_is_black( node ) \
	!( node )->color

yumei_rbtree_key_t 這個結構以現實遇到的爲準,可以是你現基本類型。
爲什麼要定義一個leaf呢,其實它是所有末節點的葉子,爲了能夠在操作中有相同的操作方式,所以不用NULL,而指向同一個葉子。
初始函數
yumei_rbtree_t* yumei_rbtree_create()
{
	yumei_rbtree_t         *tree;
	yumei_rbtree_node_t    *leaf;
	yumei_mem_pool_t       *pool;
	yumei_mem_buf_t        *buf;
	int                     size;

#define YUMEI_RBTREE_POOL_BLOCK_SIZE 1024
#define YUMEI_RBTREE_POOL_BLOCK_NUM  8
	pool = yumei_mem_pool_create( YUMEI_RBTREE_POOL_BLOCK_SIZE, YUMEI_RBTREE_POOL_BLOCK_NUM );

	if( !pool ){
		goto error;
	}

	size = sizeof( yumei_rbtree_t );
	buf = yumei_mem_buf_malloc( pool, size );
	if( !buf ){
		goto pool_error;
	}

	tree = buf->data;

	size = sizeof( yumei_rbtree_node_t );
	buf = yumei_mem_buf_malloc( pool, size );
	if( !buf ){
		goto pool_error;
	}

	leaf = buf->data;
	
	tree->root = tree->leaf = leaf;
	tree->pool = pool;
	tree->count = 0;

	yumei_rbtree_black( leaf );

	return tree;

pool_error:
	yumei_mem_pool_free( pool );

error:
	return 0;

}

釋放函數
#define YUMEI_RBTREE_ERROR  -1
#define YUMEI_RBTREE_OK      0

int yumei_rbtree_free( yumei_rbtree_t *tree )
{
	yumei_mem_pool_t        *pool;

	if( !tree ){
		return YUMEI_RBTREE_ERROR;
	}

	pool = tree->pool;

	yumei_mem_pool_free( pool );

	return YUMEI_RBTREE_OK;
}

插入函數:

#define YUMEI_RBTREE_PARAM_ERROR 2
#define YUMEI_RBTREE_KEY_EXIST   3

int yumei_rbtree_insert( yumei_rbtree_t *tree,  yumei_rbtree_key_t* key, long data )
{
	yumei_rbtree_node_t         *node, *leaf, *root, *cur, **temp;
	yumei_rbtree_key_t          *nkey;
	yumei_mem_pool_t            *pool;
	yumei_mem_buf_t             *buf;


	int                          i;
	int                          flag;
	int                          size;

	if( !tree || !key ){
		return YUMEI_RBTREE_PARAM_ERROR;
	}

	leaf = tree->leaf;
	size = sizeof( yumei_rbtree_node_t );
	if( data ){
		size += sizeof( long );
	}

	pool = tree->pool;
	if( !pool ){
		return YUMEI_RBTREE_ERROR;
	}

	buf = yumei_mem_buf_malloc( pool, size );
	if( !buf ){
		return YUMEI_RBTREE_ERROR;
	}

	
	node = buf->data;
	nkey = &node->key;

	i = 8;
	while( i-- ){
		nkey->key[ i ] = key->key[ i ];
	}

	if( data ){
		node->data = data;
	}

    node->left = node->right = leaf;

	if( !tree->count ){
		yumei_rbtree_black( node );
		node->parent = 0;
		tree->root = node;
		return YUMEI_RBTREE_OK;
	}

	cur = tree->root;
	while( 1 ){
          for( i = 0; i < 8; ++i ){
          		flag = key->key[ i ] - cur->key->key[ i ];
          		if( flag < 0 ){
          			temp = &cur->left;
          			break;
          		}
          		else if( flag > 0 ){
          			temp = &cur->right;
          			break;
          		}
          }

          if( !flag ){
          	return YUMEI_RBTREE_KEY_EXIST;
          }

          if( leaf == *temp ){
          	break;
          }

          cur = *temp;

	}

	*temp = node;
	yumei_rbtree_red( node );

	root = tree->root;
	while( node != root ){
		if( node->parent == node->parent->parent->right ){
				cur = node->parent->parent->left;
				flag = yumei_rbtree_is_black( cur );

				if( flag ){
					if( node ==  node->parent->left ){
						node = node->parent;
						yumei_rbtree_right_rot( tree, node );
					}

					yumei_rbtree_black( node->parent );
					yumei_rbtree_red( node->parent->parent );
					yumei_rbtree_left_rot( tree, node>parent->parent );
				}
				else{
					yumei_rbtree_black( node->parent );
					yumei_rbtree_black( cur );
					yumei_rbtree_red( cur->parent );
					node = cur->parent;
				}

		}
		else{
				cur = node->parent->parent->right;
				flag = yumei_rbtree_is_black( cur );
				if( flag ){
					if( node == node->parent->right ){
						node = node->parent;
						yumei_rbtree_left_rot( tree, node );
					}

					yumei_rbtree_black( node->parent );
					yumei_rbtree_red( node->parent->parent );
					yumei_rbtree_right_rot( tree, node->parent->parent );
				}
				else{
					yumei_rbtree_black( node->parent );
					yumei_rbtree_black( cur );
					yumei_rbtree_red( cur->parent );
					node = cur->parent;
				}
		}

		flag = yumei_rbtree_is_black( node->parent );
		if( flag ){
			break;
		}
	}

	yumei_rbtree_black( root );

	return YUMEI_RBTREE_OK;
}

旋轉函數和查詢就不貼上來了,有興趣的同學可以練一練,把大學裏學到的知識又回顧了一遍,一個紅黑樹就實現了,聖誕老人可以無憂矣。
大家可以看到我的比較函數是這樣的:

 for( i = 0; i < 8; ++i ){
          		flag = key->key[ i ] - cur->key->key[ i ];
          		if( flag < 0 ){
          			temp = &cur->left;
          			break;
          		}
          		else if( flag > 0 ){
          			temp = &cur->right;
          			break;
          		}
 }

對於一個32字的key,比較函數可以有很多種,可以用字符串比較,也可以多字節一齊比較,這裏用的是化零是整,轉成int 再作比較。比如
以下面兩個字符串:

A: 0x65 0x66 0x67 0x68 0x69 0x70 0x71 0x72
B: 0x65 0x66 0x67 0x68 0x69 0x70 0x72 0x71

用字符串比較要比較7次纔有結果,而用整型只需要2次,用長整型只需要一次,是不是節省了太多時間。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章