红黑树总结
来源:互联网 发布:docker搭建java web 编辑:程序博客网 时间:2024/06/05 13:28
- 红黑树学习笔记
- 红黑树的5个性质
- 红黑树的插入
- 红黑树的删除
- 红黑树学习笔记
红黑树学习笔记
红黑树的5个性质
- 每个结点要么是红的,要么是黑的。
- 根结点是黑的。
- 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
- 如果一个结点是红的,那么它的俩个儿子都是黑的。
- 对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。
注意黑色的nil结点,对于红黑树的删除的理解很重要。
红黑树的插入
插入一个初始为红色的节点,有以下3种情况。
1.空树,使其颜色转为黑,成为根。2.父为黑,无须调整。3.父为红,根据以下图片进行调整。
流程图如下所示
根据图片就可以写出代码了~~~
红黑树的删除
红黑树的删除有点难以理解。
如果删除一个节点,他有后继节点,我们就用它的后继节点(或者前继节点)转变成它的颜色顶替它的位置。所以我们不用考虑删除的节点是什么颜色,而是应该考虑顶替它的后继节点的什么颜色。因为是后继节点,所以最终删除的要么只有一个右孩子,要么就是叶子节点(有两个nil节点)。
基于以上的情况,如果删除的该节点是红色,那么直接删除就可以了。
如果是黑色,那么就有以下两种情况
1.删除的节点(5)只有一个红色的右孩子(对于用后继节点顶替来考虑),则将儿子-》黑色-》结束。
2.删除的节点(5)的儿子是黑色(包括nil节点),将以儿子为当前点,进行调整。**
下面只分析将删除的节点的儿子为当前点,并且是左儿子的情况(也就是删除的最终节点是父节点的左儿子的情况…好难说清楚…)
比如下面删除2节点,用5节点(2的后继)顶替,那么5才是认为的删除的节点。然后5就是5的父节点的左儿子,下面处理的就是这种情况。
删除节点2后
注意此时6一定有兄弟节点,不然就不是红黑树了!!
然后将6做为当前点,按下图进行操作即可~(编程时要加上是右儿子的情况)
感觉删除那里很难理解~这是对之前算法复习时候笔记的整理,有点久了,可能记错了,请不要怪罪 = =
// RBTree.cpp : Defines the entry point for the console application.///*性质1. 节点是红色或黑色。性质2. 根节点是黑色。性质3 每个叶节点(NIL节点,空节点)是黑色的。性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。*/#include "stdafx.h"#include <iostream>#include <queue>using namespace std;struct Node{ int key;//结点的值 char color; Node *left, *right, *parent; // Constructor Node(int key) { this->key = key; left = right = parent = NULL; color = 'R'; }};static Node* Tnil = new Node(-1);// Class to represent Red-Black Treeclass RBTree{public: Node *root;protected: void rotateLeft(Node *&, Node *&);//左旋操作,传入根结点和旋转的结点,成为自己右孩子的左孩子 void rotateRight(Node *&, Node *&); //右旋操作,传入根结点和旋转的结点,成为自己左孩子的右孩子 void transplant(Node*&u, Node*&v);//v子树代替u子树 Node* minimum(Node*);//某个子树的最小值 Node* find(Node*root,int data);//某值 void RBTInsertFixup(Node *&, Node *&); void RBTDeleteFixup(Node *&, Node *&); Node* RBTInsert(Node*&root, Node *z);public: // Constructor RBTree() { root = Tnil; Tnil->color = 'B'; } void insert(const int &n); void deleteN(const int &n); void clear(Node *&); Node* RBTDelete(Node*&root, Node *z);//删除z结点};void RBTree::clear(Node*&d){ //判断二叉树是否为空 if (d != Tnil) { //1.清空左子树 clear(d->left); //2.清空右子树 clear(d->right); //3.清空根节点 // cout << d->key << endl; delete d; d = NULL; } d = Tnil;}// 对违反红黑树的地方进行调整void RBTree::RBTInsertFixup(Node *&root, Node *&x){ Node *parent_x = Tnil; Node *grand_parent_x = Tnil; while ((x != root) && (x->parent->color == 'R') && (x->color != 'B'))//当x的父结点为红色时,才需要调整,循环到当前点的父结点为黑色为至 { parent_x = x->parent;//当前点的父节点 grand_parent_x = x->parent->parent;//当前点的祖父节点 //父为祖父左孩子 if (parent_x == grand_parent_x->left) { Node *uncle_x = grand_parent_x->right;//叔结点 if (uncle_x != Tnil &&uncle_x->color == 'R')//叔结点为红色 { parent_x->color = 'B'; uncle_x->color = 'B'; grand_parent_x->color = 'R'; x = grand_parent_x; } else//叔节点为黑色 { if (x == parent_x->right) { x = parent_x; rotateLeft(root, x); } x->parent->color = 'B'; x->parent->parent->color = 'R'; rotateRight(root, grand_parent_x); x = x->parent; } } else//父为祖父右孩子 { Node *uncle_x = grand_parent_x->left;//叔结点 if (uncle_x != Tnil &&uncle_x->color == 'R')//叔结点为红色 { parent_x->color = 'B'; uncle_x->color = 'B'; grand_parent_x->color = 'R'; x = grand_parent_x; } else//叔节点为黑色 { if (x == parent_x->left) { x = parent_x; rotateRight(root, x); } x->parent->color = 'B'; x->parent->parent->color = 'R'; rotateLeft(root, grand_parent_x); x = x->parent; } } } root->color = 'B';}void RBTree::RBTDeleteFixup(Node *&root, Node *&x){ Node* sibling = Tnil; while (x != root&&x->color == 'B') { if (x==x->parent->left)//x是其父亲的左孩子 { sibling = x->parent->right; if (sibling->color=='R')//第一种情况,兄弟颜色为红 { sibling->color = 'B'; x->parent->color = 'R'; rotateLeft(root, x->parent); sibling = x->parent->right;//兄弟节点变了 } else//兄弟颜色为黑 { if (sibling->left->color=='B'&&sibling->right->color=='B')//情况二,兄弟的两个孩子都为黑色 { sibling->color = 'R'; x = x->parent; } else { if (sibling->right->color=='B')//情况三,兄弟右子为黑,左子为红 { sibling->color = 'R'; sibling->left->color = 'B'; rotateRight(root, sibling); sibling = x->parent->right;//兄弟又变了 } sibling->color = x->parent->color;//情况四,兄弟右子为红,左子颜色任意。 x->parent->color = 'B'; sibling->right->color = 'B'; rotateLeft(root, x->parent); x = root;//结束 } } } else//x是其父亲的右孩子 { sibling = x->parent->left; if (sibling->color == 'R')//第一种情况,兄弟颜色为红 { sibling->color = 'B'; x->parent->color = 'R'; rotateRight(root, x->parent); sibling = x->parent->left;//兄弟节点变了 } else//兄弟颜色为黑 { if (sibling->left->color == 'B'&&sibling->right->color == 'B')//情况二,兄弟的两个孩子都为黑色 { sibling->color = 'R'; x = x->parent; } else { if (sibling->left->color =='B')//情况三,兄弟左子为黑,右子为红 { sibling->color = 'R'; sibling->right->color = 'B'; rotateLeft(root, sibling); sibling = x->parent->left;//兄弟又变了 } sibling->color = x->parent->color;//情况四,兄弟左子为红,右子颜色任意。 x->parent->color = 'B'; sibling->left->color = 'B'; rotateRight(root, x->parent); x = root;//结束 } } } } x->color = 'B';}/* 像普通二叉树一样插入,返回根结点*/Node* RBTree::RBTInsert(Node* &root, Node *z){ Node*y = Tnil;//记录当前x的父结点 Node*x = root; while (x != Tnil) { y = x; if (z->key < x->key) x = x->left; else x = x->right; } z->parent = y; if (y == Tnil) { z->color = 'B'; root = z; } else if (z->key < y->key) { y->left = z; } else { y->right = z; } if (y->color=='R') RBTInsertFixup(root, z); return root;}//左旋操作,传入根结点和旋转的结点,成为自己右孩子的左孩子void RBTree::rotateLeft(Node *&root, Node *&x){ Node *y = x->right;//y是x的右孩子 x->right = y->left; if (x->right != Tnil)//原来y没有左子树的就不需要改变y子树的父结点指向 x->right->parent = x; y->parent = x->parent;//修改y的父结点 //x的父结点有以下三种情况 if (x->parent == Tnil)//如果原来x是根结点,则y变成新的根结点 root = y; else if (x == x->parent->left)//x原来是x父结点的左孩子 x->parent->left = y; else x->parent->right = y;//x原来是x父结点的右孩子 y->left = x; x->parent = y;}//右旋操作,传入根结点和旋转的结点,成为自己左孩子的右孩子void RBTree::rotateRight(Node*&root, Node*&x){ Node*y = x->left; x->left = y->right; if (y->right != Tnil) x->left->parent = x; y->parent = x->parent; if (x->parent == Tnil) root = y; else if (x == x->parent->left) x->parent->left = y; else x->parent->right = y; y->right = x; x->parent = y;}// Function to insert a new node with given keyvoid RBTree::insert(const int &key){ Node *x = new Node(key); x->parent = x->left = x->right = Tnil; // Do a normal BST insert root = RBTInsert(root, x);}void RBTree::deleteN(const int &key){ Node *x = find(root,key); if(x!=Tnil) // Do a normal BST insert root = RBTDelete(root, x);}//v子树代替u子树void RBTree::transplant(Node*&u, Node*&v){ if (u->parent == Tnil)//u为根节点 root = v; else if (u == u->parent->left)//u为其父的左节点 u->parent->left = v; else u->parent->right = v;/* if (v != Tnil)*/ v->parent = u->parent;}Node*RBTree::minimum(Node*x)//x子树的最小值{ while (x->left != Tnil) { x = x->left; } return x;}Node * RBTree::find(Node *root,int key){ //int value = root->key; while (root->key!=key) { if (key > root->key) root = root->right; else root = root->left; if (root == Tnil) { cout << "树中没有 " << key << "这个值" << endl; return Tnil; } } return root;}Node* RBTree::RBTDelete(Node*&root, Node *z)//删除z结点{ Node*y = z; Node*x = Tnil; char y_original_color = y->color;// if (z->left==Tnil&&z->right==Tnil)// {// if (z->parent->left == z)// z->parent->left = Tnil;// else// z->parent->right = Tnil;// return root;// } if (z->left == Tnil) { x = z->right; transplant(z, x); } else if (z->right == Tnil) { x = z->left; transplant(z, x); } else { y = minimum(z->right);//y为z的后继 y_original_color = y->color; x = y->right; if (y->parent == z) x->parent = y; else { transplant(y, y->right); y->right = z->right; y->right->parent = y; } transplant(z, y); y->left = z->left; y->left->parent = y; y->color = z->color; } if (y_original_color == 'B') RBTDeleteFixup(root, x);//x可能引起红黑树的性质被破坏 return root;}/*树形打印作用域root:子树根blk:缩进次数*/void printTree(Node*root, int blk){ if (root == Tnil) return; printTree(root->right, blk + 1); for (int i = 0; i < blk; i++)printf("| ");//缩进时输出"|"符号 //for (int i = 0; i<blk; i++)printf(" ");//缩进 printf("|—<%d %c>\n", root->key, root->color);//打印"|—<id>"形式 printTree(root->left, blk + 1);}int main(){ //FILE *stream; //freopen_s(&stream,"D:\\xx.txt", "w", stdout); RBTree tree; int data,sum; cout << "请输入树的节点总数,输入0结束" << endl; while (scanf_s("%d", &sum) != EOF) { RBTree tree; if (sum == 0 ) break; cout << "请输入节点数值" << endl; for (int i=0;i<sum; i++) { cin >> data; tree.insert(data); } cout << "======================" << endl; printTree(tree.root, 0); cout << endl << "输入要删除的节点,输入-1表示结束" << endl; cin >> data; while (data!=-1) { tree.deleteN(data); cout << "删除" << data << "后:" << endl; cout << "======================" << endl; printTree(tree.root, 0); cout << endl << "输入要删除的节点,输入-1表示结束" << endl; cin >> data; } tree.clear(tree.root); printTree(tree.root, 0); cout << endl << "输入要删除的节点,输入-1表示结束" << endl; cout << "请输入树的节点总数,输入0结束" << endl; } return 0;}
借鉴了几个博客(并偷了两张图片)和算法导论。没有记下博客。。。就不贴出来了。谢谢在网上分享的各位大神。
0 0
- 【算法总结】红黑树总结
- 红黑树总结
- 红黑树总结
- 红黑树总结
- 红黑树总结
- 红黑树总结
- 红黑树实现总结
- 红黑树的总结
- 红黑树简要总结
- 总结
- 总结
- 总结
- 总结
- 总结!
- 总结
- 总结.
- 总结。
- 总结
- 总结
- 前辈多年嵌入式开发经验
- Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用
- AVAudioPlayer
- C++ UrlEncode UrlDecode
- 红黑树总结
- layout_gravity和gravity属性的区别(Android)
- 前端之路
- 点击事件
- Innobackup mysql 多实例环境搭建主从同步
- Android文件系统分析
- Java 关于IO流的全面介绍
- java.util.Date、java.sql.Date、java.sql.Time、java.sql.Timestamp区别和联系
- Android自定义View高级(二)-Canvas绘制图形