【关联式容器、键值对、map、set、二叉搜索树、AVL树】

一、关联式容器

1、什么是关联式容器?
关联式容器也是用来存储数据的,与序列式容器(vector、list【底层是线性序列的数据结构】)不同的是,其存储的是<key,value>类型的键值对,数据检索时比序列式容器更加高效;
2、什么时键值对?
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和valuekey代表键值,value代表与key对应的信息;

STL一共实现了两种不同结构的管理式容器:树形结构和哈希结构;

二、树形结构关联式容器
以下四种容器的共同特点是:使用红黑树(平衡搜索树)作为底层结果,容器中的元素是一个有序序列

1、map

(1)map是关联式容器,按照key值的大小比较排序,来存储由键值key和value组成的键值对;
(2)key在map中是用于排序的唯一标识元素,值value存在与对应键值相关的地方,且键值和value的类型可能不一样,但是可以用value_type(pair)将其连接在一起
(3)map通过键值访问单个元素比unorder_map要慢(因为unorder_map是乱序的,支持直接根据键值查找对应value的情况;),但是map允许顺序对元素进行迭代;
(4)map支持下标访问,在[]内放入key即可访问对应的value;注意:当key值不存在时,会用默认的value和当前不存在的key构造键值对然后插入到map中,而用at()会直接抛异常;
(5)一个函数 size_type count(const key_type&x)const;
:返回key值为x的键值在map中的个数,因为map中键值的个数是唯一的,所以可以用来检测map中存不存在这个键值

2、set

(1)set的value就是它的key,可以在容器中进行插入和删除但是不能在容器中修改。
(2)set内部同样按照严格顺序进行排序;
(3) set通过key访问元素的速度比unordered_set慢,但是允许顺序迭代;

3、multimap

(1) multimap和map基本相同,不过,multimap中的键值是可以重复的
(2)multimap中的元素默认是按照key小于进行排序的
(3)multimap没有重载
注:
1)set虽然是只存放value但是底层实际存储的还是key-value键值对
2)set中插入元素不需要构造键值对,可以直接插入;
3)set元素不可以重复
4)set中查找某个元素,时间复杂度为:log(以2为低)n;
5)set中的元素不允许修改,因为键就是值,一旦修改就要重新比较,重新排序;

4、multiset

与set不同点:
(1)multiset其中元素可以重复;
(2)multiset::count(key)的返回值可能大于1。(因为插入了多个关键值)
(3)multiset::size()的返回值是多重集合的势(cardinality),即multiset中元素的个数,而不是值的个数。比如,{1, 1, 2}的size是3,而不是2。
(4)multiset::erase(key)会将对应的key全部删掉,所以对{1, 1, 2}调用erase(1)之后,它就变成了{2}。

三、树形结构
上述容器的共同点是:其底层都是按照二叉搜索树实现的

1、二叉搜索树(也叫二叉排序树)

二叉搜索树可以为空树;
(1)特点:
a)左子树不为空的情况下,左子树上所有节点的值都小于根节点;
b)右子树不为空的情况下,右子树上所有结点的值都大于根节点;
c)其左右子树分别也为二叉搜索树;
在这里插入图片描述(2)二叉搜索树的操作
a)查找
若根节点不为空:
若根节点key==查找的key 返回true;
若跟接待key>查找的key ,前往左子树进行查找;
若根节点key<查找的key ,前往右子树进行查找;
b)插入
树为空,则直接插入;
树不为空,按照二叉搜索树的性质查找插入位置,插入新节点;
(从根节店判断,是插入根节点的右子树还是左子树,并依次进行下去直到找到合适的位置);
c)删除
查找元素是否在二叉搜索树中,不存在则返回,否则:

  • 要删除的结点无孩子结点,直接删除即可;
  • 要删除的结点只有左孩子,删除掉该结点,并且让已删除节点的父节点成为该左孩子的父节点;
    在这里插入图片描述
  • 要删除的结点只有右孩子,删除掉该结点,并且让已删除节点的父节点成为该右孩子的父节点;
    在这里插入图片描述
  • 要删除的结点即有右孩子又有左孩子
    1、找到该节点的右子树中的最左孩子(也就是右子树中序遍历的第一个节点);
    或者找到左子树的最右子树;

    2、把它的值和要删除的节点的值进行交换;
    3、然后删除这个节点即相当于把我们想删除的节点删除了,返回true;
    在这里插入图片描述在这里插入图片描述举例:
    a、左子树没有右孩子
    直接让左孩子继承自己的右孩子和父亲
    b、左子树有右孩子
    一路向右,找到最后的一个右孩子,然后将这个孩子的左子树挂在它父亲的右子树上,然后让它继承要删除节点的人际关系(左右子树和父亲)当要删除的节点是根节点时,不用继承父亲关系,但要修改根节点指向。
if (cur->m_left && cur->m_right)
//当这个数的左右子树都不为空时
{
//把cur赋给pre2;把cur的左子树赋给cur
TreeNode<T> * cur2 = cur->m_left;
TreeNode<T> * pre2 = cur;
//找到cur的左子树的最右子树继承cur的位置
if (cur2->m_right)//如果右子树存在
 {
	for (; cur2->m_right; pre2 = cur2, cur2 = cur2->m_right);
	//找到最右的子树来继承cur
	pre2->m_right = cur2->m_left;
	//如果cur2有左子树就把他给cur2父节点的右子树,没有的话就给一个空,不影响
	cur2->m_left = cur->m_left;//继承cur的左子树
	}
   cur2->m_right = cur->m_right;//(当它的左子树没有右子树时)直接继承它的右子树
    if (cur == pre)
	{
	 m_root = cur2;
	 //如果删除的是根节点,则继承cur的左右孩子关系后直接把cur2赋给m_root
	}
	else
	{
	if (cur->m_data < pre->m_data)
	//继承cur的父亲关系
	 {
		pre->m_left = cur2;
	 }
	else
	 {
		pre->m_right = cur2;
	 }
  }
		delete cur;//删除cur
}

(3)二叉搜索树实现
https://github.com/Wilingpz/sunny/tree/master/11.18%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/11.18%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91

(4)二叉搜索树性能分析
a)插入和删除操作必须先进行查找,查找效率代表了其性能
b)最优情况下,二叉树搜索树为完全二叉树,平均比较次数为:log(以2为底)N
最查情况下,二叉搜索树为单支,平均比较次数为N/2;

2、AVL树
1、AVL树的特点:(AVL树也可以为空树)

(1)每个结点的左右子树高度之差(平衡因子)的绝对值不超过1(-1/0/1);
(2)它的左右子树都是AVL树;
注:若一棵二叉搜索树是平衡的,那么它就是AVL树,其搜索的时间复杂度为log(以二为底的n)。

2、AVL树的操作

(1)AVL树结点的定义

template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _bf(0)
{}
AVLTreeNode<T>* _pLeft; 
// 该节点的左孩子
AVLTreeNode<T>* _pRight; 
// 该节点的右孩子
AVLTreeNode<T>* _pParent; 
// 该节点的双亲
T _data;
int _bf; // 该节点的平衡因子
};

(2)AVL树的插入

a)按照二叉搜索树的方式插入新节点;
b)调整结点的平衡因子;
即对树进行部分的旋转

(3)AVL树的旋转(四种情况)

a)左单旋(较高右子树的右侧插入)
在这里插入图片描述

b)右单旋(较高左子树的左侧插入)
在这里插入图片描述

c)左右双旋(较高左子树的右插入)
在这里插入图片描述

d)右左双旋(较高右子树的左侧插入)
在这里插入图片描述
(4)AVL树旋转总结:
以root为根的子树不平衡,即root的平衡因子为2或者-2;(右子树 树高减去左子树树高)
1、root的平衡因子为2,说明root的右子树比左子树高;
设root的右子树根为Rroot,则:
当Rroot的平衡因子为-1时,执行右左双旋;
当Rroot的平衡因子为1时,执行左旋;
2、root的平衡因子为-2时,说明root的左子树比右子树高;
设root的右子树根为Lroot;
当Lroot的平衡因子为1时,执行右左双旋;
当Lroot的平衡因子为-1时,执行右旋;

(5)如何验证是否为AVL树呢?

a)验证其是否为二叉搜索树(中序遍历为一个有序的序列
b)验证其为平衡树
每个结点的子树高度差不超过1;(同时要注意平衡因子的计算是否正确)

(6)AVL树的性能

AVL树是一颗绝对平衡的二叉搜索树;要求每个节点的左右子树高度差绝对值不超过1,以保证AVL树的查询时高效(即时间复杂度为log(以2为底)的N)。但是因为要维护其平衡,在插入和删除的过程中会需要不断地进行旋转,效率比较低;

代码:https://github.com/Wilingpz/sunny/tree/master/11.25%20AVL%E6%A0%91

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值