db2 update set里关联表_《STL源码剖析》第5章关联式容器

本文详细介绍了STL中的关联式容器,包括set、map、multiset和multimap,它们都基于红黑树实现。此外,还讨论了平衡二叉树如AVL树和红黑树的概念,以及它们的插入、删除和查找操作。同时,文章提及了非标准的关联式容器如hash_table、hash_set和hash_map,它们基于散列表提供高效的操作。
摘要由CSDN通过智能技术生成

5 关联式容器

标准的STL关联式容器分为set(集合)和map(映射表)两大类,以及这两大类的衍生体multiset(多键集合) 和 multimap(多键映射表),这些容器的底层机制均以RB-tree(红黑树)完成,红黑树也是一个独立的容器,但并不开放给外界使用。

SGI STL还提供了一个不在标准规格之列的关联式容器: hash table(散列表),以及hash table为底层机制的hash_set(散列集合),hash_map(散列映射表),hash_multiset(散列多键集合),hash_multimap(散列多键映射表)

所谓关联式容器:每笔数据(每个元素)都一个键值(key)和一个实值(value)。当元素插入到关联式容器时,容器内部结构(可能是RB-tree ,也可能是hash table)便依照其键值大小,以某种特定规则将这个元素置于适当位置。关联式容器没有所谓的头尾。

关联式容器的内部结构是一个平衡二叉树(balanced binary tree),以便获得良好的搜寻效率,平衡二叉树包括AVL-tree,RB-tree,AA-tree。

5.1 树导览

1)二叉搜索树BST(binary search tree)

二叉搜索树的放置规则是:任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树的每一个节点的键值。因此从根节点一直往左走,直至无左路可走,即得最小元素;从根节点一直往右走,直至无右可走,即得最大元素。

cb2a747fea0df16eabe7720a604fd33e.png

939ab977cbd5cbeeda370a8b0f412490.png

b165282e7b01239304464a21aff07a46.png

2)平衡二叉搜索树(balanced binary search tree)

平衡:没有任何一个节点过深(深度过大)

3) AVL-tree(Adelson-Velskii-Landis tree)

AVL tree是一个“加上了额外平衡条件”的二叉搜索树。其平衡条件的建立是为了确保整棵树的深度为O(logN),AVL tree要求任何节点的左右子树的高度相差做多为1,这是一个较弱的条件,但仍能保证“对数深度”平衡状态。

平衡被破坏的四种情况:

  1. 插入点位于X的左子节点的左子树-左左
  2. 插入点位于X的左子节点的右子树-左右
  3. 插入点位于X的右子节点的左子树-右左
  4. 插入点位于X的右子节点的右子树-右右

80070e8b6a65cf74d6eee1d96d1d27cc.png

单旋转:

将违反AVL tree规则的子树的插入元素边的第一个节点K1提起来,变成跟节点,是K2自然下滑,并将B子树挂到K2的左侧,如下图。

cd824a0474213c8babd7967305e95198.png

双旋转:从下往上,单旋转两次

dc0734d71568d8bf3a576c67a4e83a3e.png

5.2 RB-tree(红黑树)

满足以下规则:

1、每个节点不是红色就是黑色

2、根节点为黑色

3、叶子节点是黑色。

4、如果节点为红色,其子节点必须为黑色

5、任一节点至NULL(树尾端)的任何路径,所含黑节点数必须相同

由规则5 => 新增节点必须为红色。规则4 => 新增节点父节点为黑色。如果插入不满足上述要求,就需要调整RB-tree。

6c01d82f3e1ad573c41c11ccdffa0380.png

插入3

3e981ccb380420e0164c658a48239ff4.png

插入8

1dcbc011eb7c190548290defe098c88f.png

插入75

b1ebd1fbfaf888320ecbee0a01ecd671.png

插入35

b8ddeb14f0ed4add029b5b94598e009b.png

自上而下的程序:

253e5cc7a0a53d0c91e3d3ad2f1e9248.png

插入节点35

cbd4736564c2dd78ebca0b3442f7bb50.png
  • RB-tree 的节点的设计

c54d889bc55d415f0f8ccfd6b753d3bb.png

777d4181f2e20721e71f3f85e017e74b.png

62cf263b0e07ed54ec748495443cf83c.png

b3f41a5ef116084099bc85f452386798.png
  • RB-tree的迭代器

65053e9dd1c30b3723d9ed55826711a2.png
  • RB-tree 的数据结构以及构造与内存管理

详细内容看书。

0279331554e0d0154cca362db4a9de4c.png
  • RB-tree 的元素操作

元素插入操作:插入键值,节点键值允许重复

Template <class key, class value, class KeyOfValue, class compare, class Alloc>

Typename rb_tree<key,value,KeyOfValue,compare,Alloc>::iterator

Rb_tree< key,value,KeyOfValue,compare,Alloc>::insert_equal(const value& x){…}

插入键值不允许重复,若重复则插入无效

template <class key, class value, class KeyOfValue, class compare, class Alloc>

pair<typename rb_tree<key,value,KeyOfValue,compare,Alloc>::iterator,bool>

rb_tree< key,value,KeyOfValue,compare,Alloc>::insert_equal(const value& x){…}

元素的搜寻操作:

template <class key, class value, class KeyOfValue, class compare, class Alloc>

typename rb_tree<key,value,KeyOfValue,compare,Alloc>::iterator

rb_tree< key,value,KeyOfValue,compare,Alloc>::find (const value& x){…}

5.3 set

Set的特性是,所有元素都会根据元素的键值自动排序。Set的元素的键值就是实值,实值就是键值,set不允许两个元素有相同的键值。Set以RB-tree为底层机制。在客户端对set进行插入或删除操作后,之前的迭代器依然有效。

1)元素操作

begin() 返回指向map头部的迭代器

clear() 删除所有元素

count() 返回指定元素出现的次数

empty() 如果map为空则返回true

end() 返回指向map末尾的迭代器

equal_range() 返回集合中与给定值相等的上下限的两个迭代器

erase() 删除一个元素

find() 查找一个元素

get_allocator() 返回map的配置器

insert() 插入元素

key_comp() 返回比较元素key的函数

lower_bound() 返回键值>=给定元素的第一个位置

max_size() 返回可以容纳的最大元素个数

rbegin() 返回一个指向map尾部的逆向迭代器

rend() 返回一个指向map头部的逆向迭代器

size() 返回map中元素的个数

swap() 交换两个map

upper_bound() 返回键值>给定元素的第一个位置

value_comp() 返回比较元素value的函数

2)使用实例

#include<set>

Int ia[5]={0, 1,2,3,4,5};

Set<int>iset(ia,ia+5);

Cout<<”size: “<<iset.size()<<endl;

Cout<<”3 count = “<<iset.count(3)<<endl;

iset.insert(6);

iset.erase(1);

set<int>::iterator ite1=iset.begin();

set<int>::iterator ite2=iset.end();

for(;ite1 != ite2;++ite1){

cout<<*ite1; }

ite1 = find(iset.begin(),iset.end(),3 );

if(ite1 != iset.end()){

cout<<”3 found.”<<endl;}

else{ cout<<” 3 not found”<<endl; }

5.4 map

Map的特性是,所有元素都会根据元素的键值自动排序。Map的所有元素都是pair,同时拥有实值(value)和键值(key),pair的第一元素视为键值,第二元素视为实值,map不允许两个元素拥有相同的键值。当用户对map进行了增加和删除操作后,所有的迭代器都依然有效。

9e58c4c0e3d6686f0f0214043a20cfd0.png

1)元素操作

9e138188bfafd519215b31ec4d990103.png

2)使用实例

map<string, int> m ;

m["苹果"] = 1;

m["香蕉"] = 2;

m["梨"] = 3;

m.insert(pair<string,int>(“橙子", 4));

pair<string,int>value(“火龙果”,5);

m.insert(value);

m.insert(make_pair("榴莲", 6));

m.erase("香蕉");

map<string ,int>::iterator miterator=m.begin();

for(;miterator != m.end();++miterator){

cout<<miterator->first<<’ ‘<<miterator->second<<endl; }

int num = m[“苹果”];

cout<<num<<endl;

map<string ,int>::ite1;

it1 = m.find("香蕉") ;

if( it1 !=m.end() ){

cout<<”found”<<endl;}

else{ cout<<”not found” <<endl; }

5.5 multiset

Multiset的特性与set完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制RB-tree的insert_equal()而非insert_unique().

5.6 multimap

Multimap的特性与map完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制RB-tree的insert_equal()而非insert_unique().

5.7 hashtable

hash table(散列表)数据结构,在插入、删除、查找等操作具有 “常数平均时间” O(1) 的表现,这种表现是以统计为基础,不需要依赖输入元素的随机性。

1)概述

1.hashtable在插入,删除,搜寻操作上具有"常数平均时间"的表现,不依赖输入元素的随机性.

2.hashtable通过hash function将元素映射到不同的位置,但当不同的元素通过hash function映射到相同位置时,便产生了"碰撞"问题.解决碰撞问题的方法主要有线性探测,二次探测,开链法等.

3.线性探测

当hash function计算出某个元素的插入位置,而该位置的空间已不可用时,循序往下寻找下一个可用位置(到达尾端时绕到头部继续寻找),会产生primary clustering(一次聚集)问题.

4.二次探测

当hash function计算出某个元素的插入位置为H,而该位置的空间已经被占用,就尝试用H+1²、H+2²…,会产生secondary clustering(二次聚集)问题.

5.开链

在每一个表格元素中维护一个list:hash function为我们分配某个list,在那个list上进行元素的插入,删除,搜寻等操作.SGI STL解决碰撞问题的方法就是此方法.表格大小以质数来设计,SGI STL事先将28个质数存储,以备随时访问。

2)hashtable 的桶子(buckets)与节点(nodes)

遵循SGI的命名,称hash table表格内的元素为桶子(buckets),此名称的大约意义是,表格内的每个单元,涵盖的不只是个节点(元素),甚且可能是一“桶”节点。

ffe76a4855a788e9f77b38948b35d41a.png

下面是hash table节点的定义

template <class Value>

struct __hashtable_node

{

__hashtable_node* next;

Value val;

};

918632515edf8a532b17ef9dad8dc27f.png

3)hashtable的迭代器

hashtable迭代器必须永远维系与整个”buckets vector”的关系,并记录目前所知节点.hashtable的迭代器没有后退操作,也没有逆向迭代器.

前进操作:首先尝试从目前所指的节点出发,前进一个位置(节点),由于节点被安置于list内,所以利用节点的next指针即可轻易达成前进操作,如果节点正巧是list的尾端,就跳至下一个bucket身上,那正是指向下一个list的头部节点。

4)hashtable的数据结构

bc9a7933bbd50a5115649176bf935da6.png

0e907736c19e093c55f48330e20816cd.png

value : 节点的实值类别
key : 节点的键值类别
HashFcn : hash function函数类别
ExtractKey : 从节点中取出键值的方法
EqualKey : 判断键值相同与否的方法
Alloc : 空间配置器,默认使用std::alloc

首先是三个仿函数,这些仿函数都是从模板参数指定的,然后在构造函数中赋值

hash:用于获取 key 对应的哈希值,以确定要放到哪一个 bucket 中

equals:用于判断 key 是否相等

get_key:用于从 value 中取得 key,前面说过 value = key + data

接下来是 buckets 和 num_elements

buckets:维护哈希表的 bucket,是一个指针数组,每个元素都是 node* 类型

num_elements:元素的个数

6facfe14c9f25df8b84987c92a94f2e5.png
  1. hashtable的构造与内存管理

fed585a75d01c45b6dadb1d7cc18352a.png

表格重建操作:

重建表格是要将每一个元素从小进行hash,然后再delete oldhashtable的所有元素。表格是否需要重建判断原则:拿元素个数和bucket vector的大小来比,如果前者比后者大就重建表格.因此,每个bucket(list)的最大容量和bucket vector的大小相同.

b7f68974168a7f02dd41afcf60f00c5b.png

5002e4b1f54c6cab76fa0d79eb0d2be2.png

first = buckets[bucket];

  1. hashtable的操作
  • 构造函数

6cf199fdde1fca4dd4bfb9ca9c25d1a9.png

f9cf37ae3b70f200051ec03fd791eb92.png
  • 析构函数

75f085ed314e4c1aba93b89dd76d4b72.png

db765c39d516d30e7797702efac5e059.png
  • 插入元素:不允许键值重复

回到 insert_unique_noresize,在计算完应该插入到哪个 bucket 之后,获取指定的 bucket,然后遍历该 bucket 链表,如果该链表上有一个节点的 key 和 插入元素的 key 相等,那么就返回插入失败。否则,生成一个新的节点,然后插入到指定的 bucket 链表中

19762b67e2bf9392a255c30d9847f459.png
  • 插入元素:允许键值重复

首先确定要在哪一个bucket插入,然后遍历bucket链表,如果找到相等的节点,那么就在该节点处之后插入。否则,在bucket链表头插入

e9e503f5d67ca069528889951382eabf.png
  • 判断元素的落脚处

6517701746c09316219523b6a1d75032.png
  • 复制

template <class V, class K, class HF, class Ex, class Eq, class A>

void hashtable<V, K, HF, Ex, Eq, A>::clear() {…}

  • 整体删除

template <class V, class K, class HF, class Ex, class Eq, class A>

void hashtable<V, K, HF, Ex, Eq, A>::copy_from(const hash_table& ht) {…}

  • begin()指向第一个元素的迭代器

Iterator begin(){…}

  • end()指向结尾的迭代器

iterator end(){…}

  • find()查找指定key的节点

首先找到对应的bucket,然后遍历bucket链表查找等于指定 key 的节点

iterator find(const key_type& key) {…}

7) 使用实例

e65cb441c179253abd5e3dab8b0b74f2.png

00a06a2f6e5791c381f766ab5dfa149d.png

c393e9042e9ba370a67cc1791c791a5c.png

5acffc387feb1dc0dd3b4d5950ffbb9d.png

867bd78d24cb8df819c83946f2355e20.png

be0da5b24f96aa1cc8c427680e8b94c6.png

4c7df3c28bfad2590c2e989f6decb27a.png
  1. hash functions

aa7cba8ab9db43b5e22a1a2d9121441e.png

hash_set

以hash table为底层机制,由于hash_set所供应的操作接口,hashtable都提供了,所以几乎所有hash_set的操作行为,都只是转调用hashtable的操作行为而已。Set的底层机制是RB-tree可以自动排序,hash_set的底层机制是hashtable不能自动排序。hash_set的使用方式与set完全相同。运用set就是为了能够快速搜寻元素。

1)使用实例

a2a47b57e91eb26ab92281a8f243f368.png

407f12b5823bf00dc4427ec25954a2e1.png

2245903bb855e3b1484afecfe90406e4.png

hash_map

以hash table为底层机制,由于hash_map所供应的操作接口,hashtable都提供了,所以几乎所有hash_map的操作行为,都只是转调用hashtable的操作行为而已。map的底层机制是RB-tree可以自动排序,hash_map的底层机制是hashtable不能自动排序。hash_map的使用方式与map完全相同。运用map就是为了能够根据键值快速搜寻元素。

Map的特性是每一个元素都拥有一个键值(key)和一个实值(value)

1)hash_table常用操作

Void resize(size_type hint) {…} //表格重建

size_type bucket_count() const {…} //桶的大小

size_type max_bucket_count() const{…} //桶的大小的最大值

size_type elems_in_bucket(size_type n) const {…} //n号桶里有几个元素

2) 使用实例

17861bc69a72a236048c8b2e6f173381.png

e94f18c245d6d090abbd94c8fe77b410.png

hash_multiset

hash_multiset与multiset的特性完全相同,唯一差别在于它的底层机制是hashtable,也因此hash_multiset的元素并不会被自动排序。Multiset底层机制是RB-tree会自动排序。

hash_multiset 与 hash_set 实现上唯一差别在于,前者的元素插入操作采用底层机制hashtable的inset_equal(),而后者采用的是insert_unique()。

1)hash_multiset常用操作

Void resize(size_type hint) {…} //表格重建

size_type bucket_count() const {…} //桶的大小

size_type max_bucket_count() const{…} //桶的大小的最大值

size_type elems_in_bucket(size_type n) const {…} //n号桶里有几个元素

5.11 hash_mutimap

hash_multimap与multimap的特性完全相同,唯一差别在于它的底层机制是hashtable,也因此hash_multimap的元素并不会被自动排序。Multimap底层机制是RB-tree会自动排序。

hash_multimap 与 hash_map 实现上唯一差别在于,前者的元素插入操作采用底层机制hashtable的inset_equal(),而后者采用的是insert_unique()。

1)常用操作

Size_type size() const {…}

Size_type max_size() const {…}

Bool empty() const {…}

Void swap(hash_multimap& hm) {…}

Iterator begin() {…}

Iterator end() {…}

Const_iterator begin() const {…}

Const iterator end() const {…}

Iterator insert(const value_type& obj) { …}

Template<class InputIterator>

Void insert(InputIterator f, InputIterator l) {…}

Iterator find(const key_type& key) {…}

Size_type count(const key_type& key) const {…}

Pair<iterator,iterator> equal_range(const key_type& key) {…}

Size_type erase(const key_type& key) { …}

Void erase(iterator it) {…}

Void erase(iterator f,iterator l) {….}

Void clear() {…}

  1. 使用实例

57f1915a9f8661d78b31733554b6dd4b.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值