一、BST的元素删除
//注意传引用,才能作用到树本身
void delete(node* &root, int x){
if(!root){ //空树,则没找到
cout << "Not Found\n";
return;
}
//当前结点值大于x时,根据BST的性质,则去左子树上继续找
if(root->data > x) delete(root->left, x);
else if(root->data < x) delete(root->right, x); //小于x时,去右子树上找
else{ //相等了,那就删这个结点
if(!root->left && !root->right) root = NULL; //没孩子(叶子结点),直接设为NULL即可
else if(root->left){ //有左子树,那就找前驱(左子树的最右孩子,不一定是直接的)
node*temp = root->left;
while(temp->right) temp = temp->right; //找到最右子
root->data = temp->data; //用前驱的值覆盖与删除结点的值
delete(root->left, temp->data); //递归调用delete函数,在左子树中删除掉最右子(关键步骤)
}
else { //有右子树,那就找后继(原理同上)
node*temp = root->right;
while(temp->left) temp = temp->left;
root->data = temp->data;
delete(root->right, temp->data);
}
}
}
二、AVL的插入
//结点对应的结构体
struct node{
int data, h; //data是内容域,h是高度,
node*lc, *rc; //lc和rc分别对应着左右孩子
};
//得到结点的高度
int geth(node*root){
return root ? root->h : 0;
}
//更新结点的高度
void update(node*root){
root->h = max(geth(root->lc), geth(root->rc)) + 1;
}
//得到结点的平衡因子
int getfac(node*root){
return geth(root->lc) - geth(root->rc);
}
//左旋
void L(node* &root){
node*temp = root->rc;
root->rc = temp->lc;
temp->lc = root;
update(root);
update(temp);
root = temp;
}
//右旋
void R(node* &root){
node*temp = root->lc;
root->lc = temp->rc;
temp->rc = root;
update(root);
update(temp);
root = temp;
}
//插入(关键部分)
void insert(node* &root, int x){
if(!root){ //空树,直接插
node*temp = new node;
temp->data = x, temp->lc = temp->rc = NULL, temp->h = 1; //把高度设为1
root = temp;
}
if(root->data == x) return; //已存在,不插了
if(root->data < x) insert(root->rc, x); //当前结点值小于x,则在右子树插
else insert(root->lc, x); //否则,在左子树插
update(root); //插完之后,得更新高度
//然后判断是哪种类型
if(getfac(root) == 2)
if(getfac(root->lc) == 1)
R(root); //LL型,直接右旋即可
else{ //LR型,先左旋左孩子得到LL型,然后右旋
L(root->lc);
R(root);
}
else if(getfac(root) == -2)
if(getfac(root->rc) == -1) L(root); //RR型,直接左旋即可
else{ //RL型,先右旋右孩子得到RR型,然后左旋
R(root->rc);
L(root);
}
}
总结分析:
-
使用递归,树这一节很多代码都可以用递归实现,递归的边界一般都是到达空树停止(需要重点掌握)
-
注意传引用,因为只有传引用才能作用到树本身(注意点)
-
熟练写出BST的删除函数(其他的比较简单)
-
熟练写出AVL的插入函数(其实附带的有好几个函数!!!)
三、set的底层实现
set
set是一种关联式容器,其特性如下:
- set以RBTree作为底层容器
- 所得元素的只有key没有value,value就是key
- 不允许出现键值重复
- 所有的元素都会被自动排序
- 不能通过迭代器来改变set的值,因为set的值就是键
针对这五点来说,前四点都不用再多作说明,第五点需要做一下说明。如果set中允许修改键值的话,那么首先需要删除该键,然后调节平衡,在插入修改后的键值,再调节平衡,如此一来,严重破坏了set的结构,导致iterator失效,不知道应该指向之前的位置,还是指向改变后的位置。所以STL中将set的迭代器设置成const,不允许修改迭代器的值。
set的数据结构
// 比较器默认采用less,内部按照升序排列,配置器默认采用alloc
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set
{
public:
// 在set中key就是value, value同时也是key
typedef Key key_type;
typedef Key value_type;
// 用于比较的函数
typedef Compare key_compare;
typedef Compare value_compare;
private:
// 内部采用RBTree作为底层容器
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t; // t为内部RBTree容器
public:
// 用于提供iterator_traits<I>支持
typedef typename rep_type::const_pointer pointer;
typedef typename rep_type::const_pointer const_pointer;
typedef typename rep_type::const_reference reference;
typedef typename rep_type::const_reference const_reference;
typedef typename rep_type::difference_type difference_type;
// 设置成const迭代器,set的键值不允许修改
typedef typename rep_type::const_iterator iterator;
typedef typename rep_type::const_iterator const_iterator;
// 反向迭代器
typedef typename rep_type::const_reverse_iterator reverse_iterator;
typedef typename rep_type::const_reverse_iterator const_reverse_iterator;
typedef typename rep_type::size_type size_type;
iterator begin() const { return t.begin(); }
iterator end() const { return t.end(); }
reverse_iterator rbegin() const { return t.rbegin(); }
reverse_iterator rend() const { return t.rend(); }
bool empty() const { return t.empty(); }
size_type size() const { return t.size(); }
size_type max_size() const { return t.max_size(); }
// 返回用于key比较的函数
key_compare key_comp() const { return t.key_comp(); }
// 由于set的性质, value比较和key使用同一个比较函数
value_compare value_comp() const { return t.key_comp(); }
// 声明了两个友元函数,重载了==和<操作符
friend bool operator== __STL_NULL_TMPL_ARGS (const set&, const set&);
friend bool operator< __STL_NULL_TMPL_ARGS (const set&, const set&);
// ...
}
set的构造函数
set提供了如下几个构造函数用于初始化一个set
// 注:下面相关函数都在set类中定义,为了介绍方便才抽出来单独讲解
// 空构造函数,初始化一个空的set
set() : t(Compare()) {}
// 支持自定义比较器,如set<int,greater<int> > myset的初始化
explicit set(const Compare& comp) : t(comp) {}
// 实现诸如set<int> myset(anotherset.begin(),anotherset.end())这样的初始化
template <class InputIterator>
set(InputIterator first, InputIterator last)
: t(Compare()) { t.insert_unique(first, last); }
// 支持自定义比较器的初始化操作
template <class InputIterator>
set(InputIterator first, InputIterator last, const Compare& comp)
: t(comp) { t.insert_unique(first, last); }
// 以另一个set来初始化
set(const set<Key, Compare, Alloc>& x) : t(x.t) {}
// 赋值运算符函数
set<Key, Compare, Alloc>& operator=(const set<Key, Compare, Alloc>& x)
{
t = x.t;
return *this;
}
set的操作函数
insert
插入函数,调用RBTree的插入函数即可
typedef pair<iterator, bool> pair_iterator_bool;
// 由于set不允许键值重复,所以必须调用RBTree的insert_unique函数
// second表示插入操作是否成功
pair<iterator,bool> insert(const value_type& x)
{
pair<typename rep_type::iterator, bool> p = t.insert_unique(x);
return pair<iterator, bool>(p.first, p.second);
}
// 在position处插入元素, 但是position仅仅是个提示, 如果给出的位置不能进行插入,
// STL会进行查找, 这会导致很差的效率
iterator insert(iterator position, const value_type& x)
{
typedef typename rep_type::iterator rep_iterator;
return t.insert_unique((rep_iterator&)position, x);
}
// 将[first,last)区间内的元素插入到set中
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
t.insert_unique(first, last);
}
erase
擦除函数,用于擦除单个元素或者区间内的元素,直接调用RBTree的函数即可
// 擦除指定位置的元素, 会导致内部的红黑树重新排列
void erase(iterator position)
{
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)position);
}
// 会返回擦除元素的个数, 其实就是标识set内原来是否有指定的元素
size_type erase(const key_type& x)
{
return t.erase(x);
}
// 擦除指定区间的元素, 会导致红黑树有较大变化
void erase(iterator first, iterator last)
{
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)first, (rep_iterator&)last);
}
clean
清除整个set容器,直接调用RBTree的clean函数即可
void clear() { t.clear(); }
find
查找函数,RBTree也提供了,直接调用即可
// 查找指定的元素
iterator find(const key_type& x) const { return t.find(x); }
count
查找制定元素的个数
// 返回指定元素的个数, set不允许键值重复,其实就是测试元素是否在set中
size_type count(const key_type& x) const { return t.count(x); }
重载操作符
set重载了==和<操作符,基本上都是调用RBTree的接口函数即可,如下所示:
template <class Key, class Compare, class Alloc>
inline bool operator==(const set<Key, Compare, Alloc>& x,
const set<Key, Compare, Alloc>& y) {
return x.t == y.t;
}
template <class Key, class Compare, class Alloc>
inline bool operator<(const set<Key, Compare, Alloc>& x,
const set<Key, Compare, Alloc>& y) {
return x.t < y.t;
}
其他操作函数
// 返回小于当前元素的第一个可插入的位置
iterator lower_bound(const key_type& x) const
{
return t.lower_bound(x);
}
// 返回大于当前元素的第一个可插入的位置
iterator upper_bound(const key_type& x) const
{
return t.upper_bound(x);
}
// 返回与指定键值相等的元素区间
pair<iterator,iterator> equal_range(const key_type& x) const
{
return t.equal_range(x);
}