红黑树学习前篇
来源:互联网 发布:油性漆与水性漆 知乎 编辑:程序博客网 时间:2024/06/13 00:37
最近学习红黑树,个人觉得,作为一名程序员,一些经典的算法非常值得研究,他的思想他的操作对自己都是一个很好地补充学习。不过学习红黑树之前,必须要先学会二叉查找树,红黑树操作=二叉查找树操作+为符合红黑树性质所做的特殊修正。
本文为红黑树学习前篇,内容主要参考:http://blog.csdn.net/yukid2012/article/details/40479067。
自己做了一些修改,添加了一些自己的理解的注释,都是为了让读者更快更清楚的了解内容,少一些雨里雾里的绕绕。
1,性质:
二叉查找树:或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
叶子节点:在算法导论中,叶子结点的表示是用一个结点nil[T]来表示。
如下图:
typedef struct rbtree_node_s rbtree_node_t;typedef struct rbtree_s rbtree_t;//表示某一节点的结构struct rbtree_node_s { rbtree_node_t * left; rbtree_node_t * right; rbtree_node_t * parent; int key;};//表示树的结构struct rbtree_s{ rbtree_node_t * root; rbtree_node_t * sentinel;};
3,查找操作:
查找可以采用递归的做法,但是递归调用函数,会造成很多资源的浪费。采用非递归方式来进行查找。
//node:传入的根节点,sentinel:叶节点,key:查询的值rbtree_node_t * search_node( rbtree_node_t *node ,rbtree_node_t * sentinel,int key){rbtree_node_t * temp = node; for(;;){ if(temp == sentinel){ break; } if(temp->key < key) temp = temp->right ; else if (temp->key > key) temp = temp->left ; if(temp->key == key) break; } return temp;}
4,查找某个结点的孩子中最小的孩子结点操作:
对于二叉查找树而言,其孩子结点中最小的孩子结点,肯定是其最左下角的孩子结点,不清楚的话参考性质.
rbtree_node_t * minimum( rbtree_node_t * node,rbtree_node_t * sentinel){ rbtree_node_t *temp = node; //判断条件是知道左孩子为叶子节点。 for(;temp-> left != sentinel;){ temp = temp-> left; } return temp;}
5,创建结点操作:
创建节点需要malloc一段内存,然后置空后赋值,最后返回创建节点的地址。
rbtree_node_t * rbtree_create_node( int key){ rbtree_node_t * newnode = NULL; newnode = ( rbtree_node_t *)malloc(sizeof( rbtree_node_t)); if(NULL == newnode) return NULL; memset(newnode,0,sizeof(rbtree_node_t)); newnode-> key = key; return newnode;}
6,遍历二叉树操作
二叉查找树采用的中序遍历,递归调用。
void rbtree_traverse( rbtree_node_t *node,rbtree_node_t * sentinel){//递归结束条件是遍历到叶子节点。 if(node == sentinel) return ; rbtree_traverse(node-> left,sentinel); printf("key : %d color :%d\n" ,node->key,node-> color); rbtree_traverse(node-> right,sentinel);}
7,插入结点操作:
首先找到插入位置,然后修改插入节点的对应指针,最后做特殊情况的特殊处理。特殊情况就是空树的情况。
操作过程如下图:7,插入结点操作:
首先找到插入位置,然后修改插入节点的对应指针,最后做特殊情况的特殊处理。特殊情况就是空树的情况。
操作过程如下图:
//node节点要插入树rbtree中void insert_node_to_tree( rbtree_t *rbtree,rbtree_node_t * node){ rbtree_node_t ** pp; rbtree_node_t * temp = rbtree->root ; /*空树的特殊处理*/ if(rbtree->root == rbtree->sentinel){ rbtree-> root = node; node -> parent = rbtree-> sentinel ; node-> left = rbtree-> sentinel ; node-> right = rbtree-> sentinel ; return ; } /*查找要插入的节点位置 * */ for(;;){ if(temp->key < node->key) pp = &temp->right ; else if (temp->key > node-> key) pp = &temp->left ; else{ printf("existing node\n" ); return; }//此处退出循环是,temp为查找位置节点的父节点。 if(*pp == rbtree->sentinel ) break; temp = *pp; } /*修改相应指针*/ //首先找出应该插入的左右位置。 if(temp->key < node->key) temp-> right = node; else temp-> left = node;//修改插入节点的父、左、右节点指针。 node-> parent = temp; node-> left = rbtree->sentinel ; node-> right = rbtree->sentinel ; return ;}
8,删除结点操作:
声明:删除的结点为node,实际被删除的结点为subst,要取代该删除结点subst的结点为temp,叶子结点为sentinel。
8.1,删除的场景:
case1:删除结点node的两个孩子均为叶子结点sentinel,那么subst就是node,temp为叶子结点,直接删除即可。
case2:删除结点node只有一个孩子child,那么subst就是node,temp就是该孩子结点child,更改其指针即可。
case3:删除结点node有两个孩子结点lchild 和rchild,我们要寻找rchild这颗子树上最小的结minnode(原因:找到最小的顶替node可保持二叉查找树的性质不变,依然是右子节点大于父节点,父节点大于左子节点),那么subst就是minnode。 令node的key的取值等于minnode的key的取值,然后删除subst(也就是minnode)即可,情况转化为case1 or case2。
8.2,删除的步骤:
(1)首先寻找真正要删除的结点subst和要取代subst的结点temp。
(2)修改subst和temp相应的指针。如果删除的根结点,那么还需要修改rbtree_t中的root指针。
(3)是否需要修改node的key值。
8.3,操作如下图:
void rbtree_delete( rbtree_t *rbtree,rbtree_node_t *node){ rbtree_node_t * temp,*subst; //step 1: find the location of subst and temp . if(node-> left == rbtree->sentinel ){ subst = node; temp = subst-> right; }else if(node->right == rbtree->sentinel){ subst = node; temp = subst-> left; }else{ subst = rbtree_min_node(node-> right,rbtree->sentinel ); temp = subst-> right;//不可能左子节点,有的话最小节点就应该是左子节点。 } //step 2:replace node with subst //指针处理:需要更改temp节点的父节点指向,subst的父节点的左右子节点指向。 temp-> parent = subst->parent ; if(subst-> parent ==rbtree->sentinel) rbtree-> root = temp; else if(subst == subst->parent ->right) subst-> parent->right = temp; else subst-> parent->left = temp; //step 3: change the value of node with the content of subst if(subst != node){ node-> key = subst->key ; } free(subst);}
9,完整的测试程序操作(不一定正确,部分代码稍加改动即可,太晚了直接复制别人):
int main( void){ rbtree_t *rbtree; rbtree_node_t *node = NULL,*sentinel = NULL; rbtree_node_t *del_node = NULL; int key_array[] = {12,1,9,2,0,11,7,19,4,15,18,5,14,13,10,16,6,3,8,17}; int i; /*begin initial*/ sentinel = ( rbtree_node_t*)malloc (sizeof( rbtree_node_t)); rbtree = ( rbtree_t *)malloc(sizeof( rbtree_t)); if(NULL == sentinel || NULL ==rbtree) return -1; node_black(sentinel); rbtree->root = sentinel; rbtree->sentinel = sentinel; rbtree->insert = insert_value; /*end initial*/ for(i = 0; i < sizeof(key_array)/sizeof(int); i++){ node = rbtree_create_node(key_array[i]);#if BST insert_node_to_tree(rbtree,node);#else rbtree_insert_node( rbtree, node);#endif } rbtree_traverse(rbtree-> root,rbtree->sentinel ); for(i = 0; i < sizeof(key_array)/sizeof(int); i++){ del_node = rbtree_search_key( rbtree,key_array[i]); if(del_node == rbtree->sentinel ){ printf("there is no key\n" ); return -1; } // rbtree_delete(rbtree,del_node);/*binary search tree delete*/#if BST rbtree_delete(rbtree,del_node);#else rbtree_fixup_delete(rbtree,del_node);#endif printf("after delete node %d\n" ,key_array[i]); rbtree_traverse(rbtree-> root,rbtree->sentinel ); } return 0;}
- 红黑树学习前篇
- unity学习前篇
- HTM5学习前篇
- java学习笔记-前篇
- LD 学习前篇(First Day)
- 机器学习----决策树前篇(熵)
- Linux学习第二篇--学习linux前的理论知识.
- ASP学习前准备
- 学习前的准备。。。
- ASP学习前准备
- socket_学习前传
- 写在学习前的话
- 在学习opengl前
- ACE学习前准备工作
- 假前学习总结
- 假期前学习总结
- swift学习前准备
- android学习前传
- Unity3D中的Update、LateUpdate和FixedUpdate的意义
- Linux互斥与同步应用(六):文件锁
- 机器学习(写给自己看的)
- HTML中常用的实体字符
- 车牌识别技术详解六--基于Adaboost+haar训练的车牌检测
- 红黑树学习前篇
- 使用Eratosthenes 方法找出指定范围内的所有质数
- nyoj 325 zb的生日 【01-背包】
- 第十七章 路由交换机编程
- SDR SDRAM,DDR1 SDRAM,DDR2 SDRAM和DDR3 SDRAM频率知识区分
- 逃避,是懦弱的表现
- .NET MVC 扩展 HtmlHelper 从无到有(二)[双model,双表单]
- Nike Is Rereleasing Sneakers That Look Like iPhone Cases For Your FeetNike Air Max 2009
- 算法时间复杂度的计算