C++关联式容器(上)底层基础

STL的容器大致分为两类,序列式容器和关联式容器,两者直接区分通过数据在容器中的排列来区分。
        关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器内部存储的是键值对,数据检索时效率比序列式容器高。

元素的获取方式:
                     序列式容器:通过访问元素位置获取元素
                     关联式容器:通过key值访问读取元素。

标准STL序列式容器:vector,list,heap(算法),queue(适配器),stack(适配器),priority_queue(适配器)
标准STL关联式容器:map,multimap,set,multiset

键值对:键值对表示的是一种一一对应的结构,其中只包含两个成员变量,key,value;key为键值,value为对应key的信息;

树形关联式容器
STL根据场景不同设计了两种结构的关联式容器,树形结构和哈希结构。
树形结构的容器主要有四种,map,set,multimap.multiset
树形结构底层实现为平衡二叉搜索树(/红黑树)
哈希结构底层实现为哈希表(开散列/闭散列)

底层实现

BS_Tree
二叉搜索树(二叉排序树):

  • 若左子树不为空,则左子树上的所有元素都小于根节点的值
  • 若右子树不为空,则右子树上的所有元素都大于根节点的值
  • 二叉搜索树的左右子树也为二叉搜索树
//模拟实现二叉搜索树
#include<iostream>

using namespace std;

template<class T>
//BSTNode 结点
struct BSTNode{
    BSTNode<T>* _left;
    BSTNode<T>* _right;
    T _val;

    BSTNode(const T& val = T())
    :_left(nullptr)
    ,_right(nullptr)
    ,_val(val)
    {}
};

template<class T>

class BSTree{
public:
    typedef BSTNode<T> Node;
    typedef BSTNode<T>* pNode;
    
    BSTree()
    :_root(nullptr)
    {}
    BSTree(const BSTree<T>& bst)                        //拷贝构造
    {
        _root = Copy(bst._root);
    }
    ~BSTree()                                           //析构函数
    {
        destory(_root);
    }
    BSTree<T>& operator=(const BSTree<T>& bst)          //重载赋值运算符=      //先清空原有的,再拷贝
    {
        if(_root !=  bst._root)     //this != &bst  也可以                    
        {
            if(_root)
            {
                destory(_root);
            }
            _root = Copy(bst._root);
        }
        return *this;
    }
    pNode Copy(pNode root)
    {
        if(root == nullptr)
            return root;
        //按照先序遍历顺序进行拷贝
        pNode newNode = new Node(root->_val);
        newNode->_left = Copy(root->_left);
        newNode->_right = Copy(root->_right);
        return newNode;
    }
    void destory(pNode root)
    {
        if(root== nullptr)
        return ;
        //按照后序遍历的顺序进行删除
        destory(root->_left);
        destory(root->_right);
        delete root;
        root = nullptr;
    }
    //查找指定元素
    pNode Find(const T& val)
    {
        if(_root == nullptr)
        {
            return nullptr;
        }
        pNode cur = _root;
        while(cur)
        {
            if(cur->_val == val)
            {
                return cur;
            }else if(cur->_val > val)
            {
                cur = cur->_left;
            }else
            {
                cur = cur->_right;
            }
        }
        return cur;
    }

    //插入指定元素
    //保证不重复    查找重复则不插入
    //找到插入位置
    bool Insert(const T& val)
    {
        if(_root == nullptr)
        {
            _root = new Node(val);
            return true;
        }
        pNode cur = _root;
        pNode parent = nullptr;

        while(cur)
        {
            if(cur->_val == val)
            {
                return false;
            }
            parent = cur;
            if(cur->_val > val)
            {
                cur = cur->_left;
            }else
            {
                cur = cur->_right;
            }
        }
        pNode tmp = new Node(val);
        //判断插入位置
        if(parent->_val > val)
        {
            parent->_left = tmp;
        }else
        {
            parent->_right = tmp;
        }
        return true;
    }
    //删除指定元素
    //查找是否存在
    //1.叶子结点
    //2.左结点为空
    //3.右结点为空
    //4.左右结点都不为空
    bool Erase(const T& val)
    {
        pNode cur = _root;
        pNode parent = nullptr;
        while(cur)
        {
            if(cur->_val == val)
            {
                break;
            }
            parent = cur;

            if(cur->_val > val)
            {
                cur = cur->_left;
            }else
            {
                cur = cur->_right;
            }
        }
        if(cur->_left == nullptr && cur->_right == nullptr)	//左右子树都为空
        {
            if(cur != _root)
            {
                if(parent->_left == cur)
                {
                    parent->_left = nullptr;
                }else
                {
                    parent->_right = nullptr;
                }
            }else
            {
                _root = nullptr;
            }
            delete cur;
            cur = nullptr;
        }else if(cur->_left == nullptr)		//左子树为空,将右子树挂在parent上
        {
            if(cur != _root)
            {
                if(parent->_left == cur)
                {
                    parent->_left = cur->_right;
                }else
                {
                    parent->_right = cur->_right;
                }
                delete cur;
                cur = nullptr;
            }else
            {
                _root = cur->_right;
                delete cur;
            }
            
        }else if(cur->_right == nullptr)	//右子树为空,将左子树挂在parent上
        {
            if(cur != _root)
            {
                if(parent->_left == cur)
                {
                    parent->_left = cur->_left;
                }else
                {
                    parent->_right = cur->_left;
                }
                delete cur;
            }else
            {
                _root = cur->_left;
                delete cur;
                cur = nullptr;
            }
        }else			//左右子树都不为空
        {
            //先找到左子树最大结点(左子树最右)/右子树最小结点(右子树最左)
            //进行交换
            //删除左子树最大/右子树最小结点
            pNode next = cur->_left;             //左子树最大结点
            parent = cur;
            while(next->_right != nullptr)      //获取左子树最大结点
            {
                parent = next;
                next = next->_right;
            }
            cur->_val =  next->_val;
            //无论next是否有右子树
            if (parent->_left == next)
				parent->_left = next->_left;
			else
				parent->_right = next->_left;

			delete next;
			next = nullptr;
        }
        return true;
    }

    void Inorder()		//中序遍历
    {
        pNode root = _root;
        _Inorder(root);
        cout<<endl;
    }
private:
    pNode _root;
    void _Inorder(pNode root)
    {
        if(root == nullptr)
            return ;
        _Inorder(root->_left);
        cout<<root->_val<<" ";
        _Inorder(root->_right);
    }

};

二叉搜索树的查找,删除,插入的平均时间复杂度为O(logn)
当而插入搜索树插入数据接近有序时,会造成单链表,使得性能下降为O(n)。

平衡二叉搜索树:一种特殊的二叉搜索树,其对于深度平衡有条件,根据条件不同,性能有所不同。常见的平衡二叉搜索树有:AVL_Tree,RB_Tree

AVL_Tree
模拟实现AVL_Tree

/*
    AVL树:左右子树高度相差小于等于1
    二叉平衡搜索树
*/
#include<iostream>
#include<stdlib.h>
using namespace std;

template<class T>
struct AVLTNode{
	AVLTNode(const T& data)
	:_pLeft(nullptr)
	, _pRight(nullptr)
	, _pParent(nullptr)
	, _data(data)
	, _bf(0)
	{}
	AVLTNode<T>* _pLeft;
	AVLTNode<T>* _pRight;
	AVLTNode<T>* _pParent;
	T _data;
	int _bf;                    //平衡因子 = 右子树高度 - 左子树高度
};

template<class T>
class AVLTree{
public:
	typedef AVLTNode<T> Node;
	typedef Node* pNode;

	AVLTree()
		:_pRoot(nullptr)
	{}

	bool Find(const T& data)
	{
		pNode cur = _pRoot;
		while (cur)
		{
			if (cur->_data == data)
				return true;
			else if (cur->_data > data)
			{
				cur = cur->_pLeft;
			}
			else
			{
				cur = cur->_pRight;
			}
		}
		return false;
	}

	//1.按照二叉搜索树顺序将节点插入AVL树中
	//2.检查平衡性(平衡因子),旋转调整
	bool Insert(const T& data)
	{
		if (_pRoot == nullptr)
		{
			_pRoot = new Node(data);
			return true;
		}
		pNode cur = _pRoot;
		pNode parent = nullptr;
		//检查重复
		while (cur)
		{
			if (cur->_data == data)
				return false;
			parent = cur;
			if (cur->_data > data)
			{
				cur = cur->_pLeft;
			}
			else
			{
				cur = cur->_pRight;
			}
		}
		cur = new Node(data);
		if (parent->_data > data)
		{
			parent->_pLeft = cur;
			cur->_pParent = parent;
		}
		else
		{
			parent->_pRight = cur;
			cur->_pParent = parent;
		}
		//检查平衡性调整
		while (parent)
		{
			//更新双亲的平衡因子
			if (parent->_pLeft == cur)
			{
				--parent->_bf;
			}
			else
			{
				++parent->_bf;
			}
			//检测平衡因子
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//继续更新向上结点
				cur = parent;
				parent = parent->_pParent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				if (parent->_bf == 2 && cur->_bf == 1)                        //单左旋
				{
					RotateLeft(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1)                //单右旋
				{
					RotateRight(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)                 //先左旋再右旋
				{
					pNode subL =  parent->_pLeft;
					pNode subLR = subL->_pRight;
					int bf = subLR->_bf;
					RotateLeft(cur);
					RotateRight(parent);
					//修改平衡因子
					if(bf == 1)
					{
						parent->_bf = 0;
						subL->_bf = -1;
					}else if(bf == -1)
					{
						parent->_bf = 1;
						subL->_bf = 0;
					}
				}
				else if (parent->_bf == 2 && cur->_bf == -1)                 //先右旋再左旋
				{
					pNode subR = parent->_pRight;
					pNode subRL = subR->_pLeft;
					int bf = subRL->_bf;
					RotateRight(cur);
					RotateLeft(parent);
					//修改平衡因子
					if(bf == 1)
					{
						subR->_bf = 0;
						parent->_bf = -1;
					}else if(bf == -1)
					{
						subR->_bf = 1;
						parent->_bf = 0;
					}
				}
				break;
			}
		}
		return true;
	}
    //中序遍历
	void Inorder()
	{
		if (_pRoot == nullptr)
			return;
		_Inorder(_pRoot);
		cout << endl;
	}
    //验证是否为平衡树
    bool IsBalance()
    {
        return _IsBalance(_pRoot);
    }
private:
	void _Inorder(pNode root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_pLeft);
		cout << root->_data << " ";
		_Inorder(root->_pRight);
	}

    int Height(pNode root)
    {
        if(root == nullptr)
        return 0;
        int leftH = Height(root->_pLeft);
        int rightH = Height(root->_pRight);

        return leftH>rightH? leftH+1 : rightH+1;
    }

    bool _IsBalance(pNode root)
    {
        if(root == nullptr)
        return true;
        
        int leftH = Height(root->_pLeft);
        int rightH = Height(root->_pRight);

        if(root->_bf != rightH - leftH)
        {
            root->_bf = rightH - leftH;
            cout<<root->_data<<": 平衡结点数值错误"<<endl;
            return false;
        }
        return true;
    }

	//旋转
	//1.改变链接
	//2。更新平衡因子
	void RotateLeft(pNode root)         //左旋
	{
		pNode subR = root->_pRight;
		pNode subRL = subR->_pLeft;
		//改变链接
		subR->_pLeft = root;
		root->_pRight = subRL;
		if (subRL != nullptr)
		{
			subRL->_pParent = root;
		}
		if (root != _pRoot)
		{
			pNode gParent = root->_pParent;
			if (gParent->_pLeft == root)
			{
				gParent->_pLeft = subR;
			}
			else
			{
				gParent->_pRight = subR;
			}
			subR->_pParent = gParent;
		}
		else
		{
			_pRoot = subR;
			subR->_pParent = nullptr;
		}
		root->_pParent = subR;
		//更新平衡因子
		subR->_bf = root->_bf = 0;
	}

	void RotateRight(pNode root)        //右旋
	{
		pNode subL = root->_pLeft;
		pNode subLR = subL->_pRight;
		//改变链接
		subL->_pRight = root;
		root->_pLeft = subLR;
		if (subLR != nullptr)
		{
			subLR->_pParent = root;
		}
		if (root != _pRoot)
		{
			if (root->_pParent->_pLeft == root)
			{
				root->_pParent->_pLeft = subL;
			}
			else
			{
				root->_pParent->_pRight = subL;
			}
			subL->_pParent = root->_pParent;
		}
		else
		{
			_pRoot = subL;
			subL->_pParent = nullptr;
		}
		root->_pParent = subL;
		//更新平衡因子
		subL->_bf = root->_bf = 0;
	}

	pNode _pRoot;
};

AVL_Tree中引用平衡因子来判断左右子树的高度差是否超过1,若超过1,根据相应情况进行调整
平衡因子为右子树高度减去左子树高度.

关于平衡因子问题,对于右左双旋和左右双旋时的平衡因子更新问题:(画的,嗯。。就看看)
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
RB_Tree
模拟实现RB_Tree

/*
红黑树:
接近平衡的二叉搜索树
规则:
根节点为黑色
叶节点为黑色
红色节点下必须为黑色结点
每条路径下黑色结点个数相同(保证了最长路径不超过最短路径的两倍)
默认插入结点为颜色红色,保证路径下黑色结点数目相同
*/
#include<iostream>

using namespace std;

enum Color{ RED, BLACK };

template<class T>

struct RBTreeNode{
	RBTreeNode(const T& data = T())
	:_left(nullptr)
	, _right(nullptr)
	, _parent(nullptr)
	, _data(data)
	, _color(RED)
	{}
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Color _color;
};

template<class T>

class RBTree{
public:
	typedef RBTreeNode<T> Node;
	typedef RBTreeNode<T>* pNode;
	RBTree()
		:_head(new Node())
	{
		_head->_parent = nullptr;
		_head->_left = _head;
		_head->_right = _head;
	}

	pNode& GetRoot()
	{
		return _head->_parent;
	}
	/*
	首先保证红黑树中无重复元素
	插入结点至合适位置,判断是否满足条件,进行相关调整
	*/
	bool find(const T& data)
	{
		pNode cur = _head->_parent;
		while (cur)
		{
			if (cur->_data > data)
			{
				cur = cur->_left;
			}
			else if (cur->_data < data)
			{
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		return true;
	}
	bool Insert(const T& data)
	{
		//如果为空树
		if (_head->_parent == nullptr)
		{
			pNode root = new Node(data);
			root->_parent = _head;
			_head->_parent = root;
			_head->_left = root;
			_head->_right = root;
			root->_color = BLACK;
			return true;
		}
		//检查是否有重复结点
		pNode cur = _head->_parent;
		pNode parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (cur->_data == data)
			{
				return false;
			}
			else if (cur->_data > data)
			{
				cur = cur->_left;
			}
			else
			{
				cur = cur->_right;
			}
		}
		cur = new Node(data);
		cur->_parent = parent;
		if (parent->_data > data)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		//修改颜色旋转
		//若cur为root结点则会影响边_head->left 和 _head->_right 的颜色
		while (parent->_color == RED && cur != GetRoot())			
		{
			pNode gParent = parent->_parent;
			if (parent == gParent->_left)    //左侧
			{
				pNode uncle = gParent->_right;
				if (uncle != nullptr && uncle->_color == RED)    //第一种情况  uncle 和 parent的颜色均为红色
				{
					uncle->_color = BLACK;
					parent->_color = BLACK;
					gParent->_color = RED;
					cur = gParent;
					parent = gParent->_parent;
				}
				else   //uncle节点不存在或为黑色
				{
					if (cur == parent->_right)    //先左旋再右旋  //第三种
					{
						RotateLeft(parent);
						//旋转后parent 和  cur 的位置互换
						swap(parent, cur);
					}
					//右旋                                      //第二种
					gParent->_color = RED;
					parent->_color = BLACK;
					RotateRight(gParent);
					break;
				}
			}
			else               //右侧             
			{
				pNode uncle = gParent->_left;
				if (uncle != nullptr && uncle->_color == RED)    //第一种
				{
					uncle->_color = BLACK;
					parent->_color = BLACK;
					gParent->_color = RED;
					cur = gParent;
					parent = gParent->_parent;
				}
				else
				{
					if (cur == parent->_left)         //先右旋再左旋 //第三种
					{
						RotateRight(parent);
						//旋转后parent 和  cur 的位置互换
						swap(parent, cur);
					}
					gParent->_color = RED;                          //第二种
					parent->_color = BLACK;
					RotateLeft(gParent);
					break;
				}
			}
		}
		_head->_parent->_color = BLACK;
		_head->_left = leftMost();
		_head->_right = rightMost();
		return true;
	}

	pNode leftMost()
	{
		pNode cur = _head->_parent;
		while (cur && cur->_parent)
		{
			cur = cur->_left;
		}
		return cur;
	}

	pNode rightMost()
	{
		pNode cur = _head->_parent;
		while (cur && cur->_parent)
		{
			cur = cur->_right;
		}
		return cur;
	}

	void InOrder()
	{
		_InOrder(_head->_parent);
		cout << endl;
	}

	bool IsRBTree()
	{
		//判断红黑树
		//1.中序遍历有序
		//2.每条路径的黑色结点数相同
		//3.红色节点不连续
		pNode root = GetRoot();
		if (root == nullptr)
			return true;
		//检测根节点情况
		if (root->_color == RED)
		{
			cout << "根节点为黑色,违反条件" << endl;
			return false;
		}
		//比较每条路径上的黑色节点个数
		size_t blackCount = 0;
		pNode cur = root;
		while (cur)
		{
			if (cur->_color == BLACK)
				blackCount++;
			cur = cur->_left;
		}
		size_t k = 0; //记录路径上节点个数
		return _IsRBTree(root, k, blackCount);
	}

private:
	pNode _head;        //头节点:为了更好的实现迭代器

	bool _IsRBTree(pNode root, size_t k, size_t blackCount)
	{
		if (root == nullptr)
		{
			if (k != blackCount)
			{
				cout << "每条路径上的黑色结点数不相同" << endl;
				return false;
			}
			return true;
		}

		if (root->_color == BLACK)
		{
			++k;
		}
		else if (root->_parent->_color == RED)          //检测结点和双亲是否同为红色
		{
			cout << "连续两个结点为红色" << endl;
			return false;
		}

		return _IsRBTree(root->_left, k, blackCount) && _IsRBTree(root->_right, k, blackCount);
	}

	void _InOrder(pNode root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_data << " ";
		_InOrder(root->_right);
	}
	void RotateRight(pNode root)        //右旋
	{
		pNode subL = root->_left;
		pNode subLR = subL->_right;
		subL->_right = root;
		root->_left = subLR;
		if (subLR != nullptr)
		{
			subLR->_parent = root;
		}
		if (root != _head->_parent)
		{
			pNode parent = root->_parent;
			if (parent->_left == root)
			{
				parent->_left = subL;
			}
			else
			{
				parent->_right = subL;
			}
			subL->_parent = parent;
		}
		else
		{
			_head->_parent = subL;
			subL->_parent = _head;
		}
		root->_parent = subL;
	}
	void RotateLeft(pNode root)     //左旋
	{
		pNode subR = root->_right;
		pNode subRL = subR->_left;
		subR->_left = root;
		root->_right = subRL;
		if (subRL != nullptr)
		{
			subRL->_parent = root;
		}
		if (root != _head->_parent)
		{
			pNode parent = root->_parent;
			if (root == parent->_left)
			{
				parent->_left = subR;
			}
			else
			{
				parent->_right = subR;
			}
			subR->_parent = parent;
		}
		else
		{
			_head->_parent = subR;
			subR->_parent = _head;
		}
		root->_parent = subR;
	}
};

红黑树的调整问题:(画工不好见谅)
在这里插入图片描述在这里插入图片描述在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值