> 文章列表 > 【数据结构】手撕红黑树

【数据结构】手撕红黑树

【数据结构】手撕红黑树


目录

一、红黑树简介

1、红黑树的简介

2、红黑树的性质

二、红黑树的插入(看叔叔的颜色就行)

1、为什么新插入的节点必须给红色?

2、插入红色节点后,判定红黑树性质是否被破坏

2.1情况一:uncle存在且为红

2.2情况二:uncle不存在/存在且为黑(直线)

2.3情况三:uncle不存在/存在且为黑(折线)

2.4总结

3、红黑树插入代码

三、红黑树整体代码


一、红黑树简介

1、红黑树的简介

红黑树和AVL树一样,因其逻辑复杂,面试时现场要求手撕就是纯纯刁难面试者。但某大厂面试官曾要求某些求职者现场手撕红黑树(我赌5毛,让面试官撕,他也撕不出来,而且你家员工上班手搓红黑树啊?),随后求职遭遇被发到网上吐槽,这便有了“手撕红黑树”的梗,也让红黑树成为了知名度最高的数据结构。(话虽如此,对于红黑树的性质、插入思想等概念还是需要掌握的)

2、红黑树的性质

红黑树本质也是一种二叉搜索树。底层结构需要使用二叉搜索树的地方,基本上都会使用红黑树来实现,而AVL树也因此坐上了冷板凳。

红黑树通过在每个节点上添加一个存储位,用于存储“RED”或“BLACK”。通过节点上红/黑颜色限制,确保最长路径不超过最短路径的两倍,因而它是接近平衡的树形结构。最短路径:全黑;最长路径:一黑一红交替。

1、红黑树的根节点是黑色的;

2、没有连续的红色节点(如果某个节点为红色,则它的左右孩子必须是黑色)

3、无论哪个节点,其每条路径的黑色节点数量相同;

4、所有的空节点(NIL节点)可以认为是黑色的。

最优情况:全黑或每条路径都是一黑一红的满二叉树,高度logN

最差情况:每颗子树左子树全黑,右子树一黑一红。高度2*logN。

可以发现,最坏情况的时间复杂度和AVL树一样,都是O(logN),但是红黑树这种近似平衡的结构减少了大量旋转,综合性能优于AVL树。

二、红黑树的插入(看叔叔的颜色就行)

1、为什么新插入的节点必须给红色?

新节点给红色,可能会违反上面说的红黑树性质2;如果新节点给黑色,必定会违反性质3。

2、插入红色节点后,判定红黑树性质是否被破坏

情况一调整后可能变成情况一、情况二、情况三。

2.1情况一:uncle存在且为红

这种情况cur、parent、grandfather都是确定颜色的,唯独uncle的颜色是不确定的。

可以这么想:cur为红那么就需要将parent变为黑;parent变黑需要控制每条路径上黑节点的数量相同,那么就要把uncle变黑;如果grandfather不是根,需要反转为红,用以控制路径黑节点数量相同。继续向上调整即可。

2.2情况二:uncle不存在/存在且为黑(直线)

uncle的情况分两种。

uncle不存在,则cur为插入节点,单旋即可。

uncle存在且为黑是第一种情况变过来的。

2.3情况三:uncle不存在/存在且为黑(折线)

uncle的情况分两种。

uncle不存在,则cur为插入节点,两次单旋即可。

uncle存在且为黑,先掰直

2.4总结

插入新节点时,父节点为红,看叔叔的颜色。

1、叔叔存在且为红,变色,向上调整(可能变为三种情况中的任意一种)

2、叔叔不存在/存在且为黑,直线。单旋+变色

3、叔叔不存在/存在且为黑,折线,两次单旋+变色

3、红黑树插入代码

bool Insert(const pair<K,V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;//根节点给黑色return true;}//_root不为空Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else//相等说明元素相同,插入失败return false;}//开始插入cur = new Node(kv);cur->_col = RED;//新插入节点给红色,可能违反规则。如果给黑色会导致其他路径的黑色节点数量不相同,必定违反规则。  if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;//维护cur的父指针}else{parent->_left = cur;cur->_parent = parent;}//调整while (parent&&parent->_col == RED){Node* grandfather = parent->_parent;//找到祖父if (grandfather->_left == parent)//如果父亲是祖父的左孩子{Node* uncle = grandfather->_right;//找到叔叔//情况一:叔叔存在且为红if (uncle != nullptr && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//情况二或情况三{if (cur == parent->_left)//情况二,直线{RotateRight(grandfather);//右单旋parent->_col = BLACK;grandfather->_col = RED;}else//情况三,折线{RotateLeft(parent);//左单旋RotateRight(grandfather);//右单旋cur->_col = BLACK;grandfather->_col = RED;}break;}}else//如果父亲是祖父的右孩子{Node* uncle = grandfather->_left;if (uncle != nullptr && uncle->_col == RED){parent->_col =uncle->_col= BLACK;grandfather->_col = RED; cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right)//情况二,直线{//g//  p//    cRotateLeft(grandfather);//左单旋parent->_col = BLACK;grandfather->_col = RED;}else//情况三,折线{//g//  p//c   RotateRight(parent);//右单旋RotateLeft(grandfather);//左单旋cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col=BLACK;return true;
}

三、红黑树整体代码

#pragma once
#include <iostream>
#include <map>
#include <set>
#include <string>
using namespace std;
enum Color
{RED,BLACK,
};
template <class K,class V>
struct RBTreeNode
{RBTreeNode(const pair<K,V>& kv):_parent(nullptr),_left(nullptr),_right(nullptr),_kv(kv),_col(RED){}RBTreeNode<K,V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;pair<K, V> _kv;Color _col;
};
template <class K, class V>
class RBTree
{
public:typedef RBTreeNode<K,V> Node;bool Insert(const pair<K,V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;//根节点给黑色return true;}//_root不为空Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else//相等说明元素相同,插入失败return false;}//开始插入cur = new Node(kv);cur->_col = RED;//新插入节点给红色,可能违反规则。如果给黑色会导致其他路径的黑色节点数量不相同,必定违反规则。  if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;//维护cur的父指针}else{parent->_left = cur;cur->_parent = parent;}//调整while (parent&&parent->_col == RED){Node* grandfather = parent->_parent;//找到祖父if (grandfather->_left == parent)//如果父亲是祖父的左孩子{Node* uncle = grandfather->_right;//找到叔叔//情况一:叔叔存在且为红if (uncle != nullptr && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//情况二或情况三{if (cur == parent->_left)//情况二,直线{RotateRight(grandfather);//右单旋parent->_col = BLACK;grandfather->_col = RED;}else//情况三,折线{RotateLeft(parent);//左单旋RotateRight(grandfather);//右单旋cur->_col = BLACK;grandfather->_col = RED;}break;}}else//如果父亲是祖父的右孩子{Node* uncle = grandfather->_left;if (uncle != nullptr && uncle->_col == RED){parent->_col =uncle->_col= BLACK;grandfather->_col = RED; cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right)//情况二,直线{//g//  p//    cRotateLeft(grandfather);//左单旋parent->_col = BLACK;grandfather->_col = RED;}else//情况三,折线{//g//  p//c   RotateRight(parent);//右单旋RotateLeft(grandfather);//左单旋cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col=BLACK;return true;}void Inorder(){_Inorder(_root);}bool IsBalance(){return _IsBalance();}
private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Inorder(root->_right);}bool Check(Node* root){if (root == nullptr)return true;if (root->_col == RED && root->_parent->_col == RED){cout << "父子均为红" << endl;return false;}return Check(root->_left) && Check(root->_right);}bool _IsBalance(){if (_root == nullptr)return true;if (_root->_col != BLACK){return false;}return Check(_root);}void RotateLeft(Node* parent)//左单旋{Node* grandfather = parent->_parent;Node* cur = parent->_right;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (grandfather->_left == parent)//需要判定parent原来属于grandfather的哪一边grandfather->_left = cur;elsegrandfather->_right = cur;cur->_parent = grandfather;}parent->_right = cur->_left;if (cur->_left != nullptr)cur->_left->_parent = parent;cur->_left = parent;parent->_parent = cur;}void RotateRight(Node* parent)//右单旋{Node* grandfather = parent->_parent;Node* cur = parent->_left;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = cur;cur->_parent = grandfather;}else{grandfather->_right = cur;cur->_parent = grandfather;}}parent->_parent = cur;parent->_left = cur->_right;if (cur->_right != nullptr)cur->_right->_parent = parent;cur->_right = parent;}
private:Node* _root=nullptr;
};
void TestAVLTree()
{//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };//int a[] = { 9,8,7,6,5,4,3,2,1};RBTree<int, int> t;for (auto e : a){t.Insert(make_pair(e, e));}t.Inorder();//cout << t.IsBalance() << endl;
}