淺談基礎(chǔ)算法之AVL tree和splay tree(三)
序
承接上文,我們繼續(xù)聊這個(gè)話題。
平衡二叉樹:AVL Tree(1962)
上文我們只實(shí)現(xiàn)了單旋,但是實(shí)際中為了達(dá)到平衡很多是要做雙旋操作的。
先來看一張雙旋后的一張圖,明顯右邊的圖查詢的時(shí)候會(huì)更便捷。
整個(gè)過程
下面我們就進(jìn)行代碼實(shí)踐。
- #include <stdio.h>
- #include <stdlib.h>
- #define max(a,b) (((a) > (b)) ? (a) : (b))
- typedef struct AvlNode{
- int data;
- struct AvlNode *left_child, *right_child;
- } AvlNode;
- AvlNode *root;
- /* 旋轉(zhuǎn)動(dòng)作開始 */
- AvlNode *rotate_LL(AvlNode *parent){
- AvlNode *child = parent->left_child;
- parent->left_child = child->right_child;
- child->right_child = parent;
- return child;
- }
- AvlNode *rotate_RR(AvlNode *parent){
- AvlNode *child = parent->right_child;
- parent->right_child = child->left_child;
- child->left_child = parent;
- return child;
- }
- AvlNode *rotate_RL(AvlNode *parent){
- AvlNode *child = parent->right_child;
- parent->right_child = rotate_LL(child);
- return rotate_RR(parent);
- }
- AvlNode *rotate_LR(AvlNode *parent){
- AvlNode *child = parent->left_child;
- parent->left_child = rotate_RR(child);
- return rotate_LL(parent);
- }
- /* 旋轉(zhuǎn)動(dòng)作結(jié)束 */
- int get_height(AvlNode *node){
- int height = 0;
- if(node != NULL){
- height = 1 + max(get_height(node->left_child), get_height(node->right_child));
- }
- return height;
- }
- int get_balance(AvlNode *node){
- if(node == NULL) return 0;
- return get_height(node->left_child) - get_height(node->right_child);
- }
- /* 平衡二叉樹 */
- AvlNode *balance_tree(AvlNode **node){
- int height_diff = get_balance(*node); /* 平衡因子在-1到1之間*/
- if(height_diff > 1){
- if(get_balance((*node)->left_child) > 0){
- *node = rotate_LL(*node);
- }else{
- *node = rotate_LR(*node);
- }
- }else if(height_diff < -1){
- if(get_balance((*node)->right_child) < 0){
- *node = rotate_RR(*node);
- }else{
- *node = rotate_RL(*node);
- }
- }
- return *node;
- }
- AvlNode *avl_add(AvlNode **root, int key){
- if(*root == NULL){
- *root = (AvlNode *)malloc(sizeof(AvlNode));
- if(*root == NULL){
- printf("內(nèi)存分配失敗!\n");
- exit(-1);
- }
- (*root)->data = key;
- (*root)->left_child = (*root)->right_child = NULL;
- }else if(key < (*root)->data){
- (*root)->left_child = avl_add(&((*root)->left_child), key);
- //balance_tree(root);
- }else if(key > (*root)->data){
- (*root)->right_child = avl_add(&((*root)->right_child), key);
- //balance_tree(root);
- }else{
- printf("復(fù)制%d失敗!\n", key);
- exit(-1);
- }
- return *root;
- }
- AvlNode *avl_print(AvlNode *node){
- if(node == NULL) return NULL;
- printf("%d->", node->data);
- avl_print(node->left_child);
- avl_print(node->right_child);
- return node;
- }
- int main(){
- avl_add(&root, 24);
- avl_add(&root, 17);
- avl_add(&root, 40);
- avl_add(&root, 8);
- avl_add(&root, 22);
- avl_add(&root, 18);
- avl_add(&root, 23);
- printf("打印二叉樹\n");
- avl_print(root);
- printf("\n");
- balance_tree(&root);
- printf("打印二叉樹\n");
- avl_print(root);
- printf("\n");
- return 0;
- }
讓我們看看伸展樹!
伸展樹(Splay Tree,1985)
伸展樹的基本原理:
舉例
我抽取一部分lighttpd-1.4.31.tar.gz中的代碼,來講解
想看具體的代碼實(shí)踐的,可以到如下位置觀看
我的代碼結(jié)構(gòu):
代碼部分
- /*~ splaytree.h~*/
- typedef struct tree_node{
- struct tree_node *left, *right;
- int key; /* 關(guān)鍵字 */
- int size; /* 結(jié)點(diǎn)數(shù)目 */
- void *data;
- } splay_tree;
- /*我現(xiàn)在只寫這兩個(gè)方法*/
- splay_tree * splaytree_insert(splay_tree *t, int key, void *data);
- splay_tree * splaytree_splay(splay_tree *t, int key);
- #define node_size(x) (((x)==NULL) ? 0 : ((x)->size))
這個(gè)沒有必要多講,看注釋和方法名稱自然就知道干嗎的了!
- /* splaytree.c */
- #include "splaytree.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #define compare(i,j) ((i) - (j))
- splay_tree * splaytree_insert(splay_tree *t, int key, void *data){
- splay_tree * new;
- if (t != NULL) {
- t = splaytree_splay(t, key);
- if(compare(key, t->key) == 0){ /* 該結(jié)點(diǎn)已存在 */
- return t;
- }
- }
- new = (splay_tree *) malloc (sizeof (splay_tree));
- assert(new);
- if (t == NULL) {
- new->left = new->right = NULL;
- } else if (compare(key, t->key) < 0) {
- new->left = t->left;
- new->right = t;
- t->left = NULL;
- t->size = 1 + node_size(t->right);
- } else {
- new->right = t->right;
- new->left = t;
- t->right = NULL;
- t->size = 1 + node_size(t->left);
- }
- new->key = key;
- new->data = data;
- new->size = 1 + node_size(new->left) + node_size(new->right);
- return new;
- }
- splay_tree * splaytree_splay(splay_tree *t, int key){
- splay_tree N, *l, *r, *child; /* 臨時(shí)變量用于裝配*t使用 */
- int cmp, l_size, r_size;
- if (t == NULL) return t;
- N.left = N.right = NULL;
- l = r = &N;
- l_size = r_size = 0;
- for (;;) {
- cmp = compare(key, t->key);
- if (cmp < 0) {
- if(t->left == NULL) break;
- if (compare(key, t->left->key) < 0) {
- child = t->left; /* 右旋 */
- t->left = child->right;
- child->right = t;
- t->size = 1 + node_size(t->left) + node_size(t->right);
- t = child;
- if(t->left == NULL) break;
- }
- r->left = t; /* 右鏈 */
- r = t;
- t = t->left;
- r_size += 1 + node_size(r->right);
- } else if (cmp > 0) {
- if(t->right == NULL) break;
- if (compare(key, t->right->key) > 0) {
- child = t->right;
- t->right = child->left;
- child->left = t;
- t->size = 1 + node_size(t->left) + node_size(t->right);
- t = child;
- if(t->right == NULL) break;
- }
- l->right = t;
- l = t;
- t = t->right;
- l_size += 1 + node_size(l->left);
- } else {
- break;
- }
- }
- l_size += node_size(t->left);
- r_size += node_size(t->right);
- t->size = 1 + l_size + r_size;
- l->right = r->left = NULL;
- /* 校驗(yàn)size數(shù)據(jù) */
- /*右孩子的左結(jié)點(diǎn)不計(jì)算在內(nèi)*/
- for(child = N.right; child != NULL; child = child->right){
- child->size = l_size;
- l_size -= 1 + node_size(child->left);
- }
- for(child = N.left; child != NULL; child = child->left){
- child->size = r_size;
- r_size -= 1 +node_size(child->right);
- }
- /* 裝配數(shù)據(jù) */
- l->right = t->left;
- r->left = t->right;
- t->left = N.right;
- t->right = N.left;
- return t;
- }
看到上面一坨代碼估計(jì)大家不知所云了?
我就針對(duì)性的講解一下。
>> 重點(diǎn)講講講核心算法splaytree_splay()方法吧!
這個(gè)if講通,那么另一個(gè)else if也好理解。
- if (cmp < 0) {
- if(t->left == NULL) break;
- if (compare(key, t->left->key) < 0) {
- child = t->left; /* 右旋 */
- t->left = child->right;
- child->right = t;
- t->size = 1 + node_size(t->left) + node_size(t->right);
- t = child;
- if(t->left == NULL) break;
- }
- r->left = t; /* 右鏈 */
- r = t;
- t = t->left;
- r_size += 1 + node_size(r->right);
- }
這是一個(gè)右旋的過程。
child = t->left
t->left = child->right; child->right = t;
最后:t = child
這是一個(gè)右鏈的過程
r->left = t;r=t;
t = t->left
3>main.c
- #include <stdio.h>
- #include "splaytree.h"
- splay_tree * splaytree_print(splay_tree *t){
- if(t != NULL){
- printf("t->data:%d\t t->key:%d t->size:%d\n", *((int *)t->data), t->key, t->size);
- splaytree_print(t->left);
- splaytree_print(t->right);
- }
- }
- int main(){
- splay_tree *root;
- root = NULL;
- int data1 = 1000;
- root = splaytree_insert(root, 20, &data1);
- int data2 = 200;
- root = splaytree_insert(root, 5, &data2);
- int data3 = 300;
- root = splaytree_insert(root, 3, &data3);
- int data4 = 100;
- root = splaytree_insert(root, 10, &data4);
- printf("打印結(jié)果*************\n");
- splaytree_print(root);
- //這里應(yīng)該有個(gè)數(shù)據(jù)查詢過程,但是我沒有寫。注意在調(diào)用的時(shí)候調(diào)用一下splay方法,
- //那么熱數(shù)據(jù)自然就跑到頂端了。
- printf("查詢方法中調(diào)用這個(gè)伸展函數(shù)之后,打印結(jié)果*************\n");
- root = splaytree_splay(root, 3);
- splaytree_print(root);
- return 0;
- }
這里我沒有把查詢伸展樹的過程寫下來,只是強(qiáng)調(diào)一下,在查詢的過程中,只要調(diào)用這個(gè)核心方法,那么自然熱數(shù)據(jù)就跑到頂端了。
看一下過程
原文鏈接:http://www.cnblogs.com/baochuan/archive/2012/10/16/2716641.html
【編輯推薦】