learn C++ NO.23——map、set的模拟实现

news2024/11/25 2:48:51

STL库的实现方式

map和set的底层用的红黑树是一样的吗?从容器特点的角度出发,这里个容器的底层应该分别用key搜索模型的红黑树和key value 搜索模型的红黑树。但是,从库的设计角度出发,这两者用同一份红黑树代码更好。而STL就是用的同样一份key value 搜索模型的红黑树进行对map和set的封装的。下面就通过STL库的源码来简单看一看。

在这里插入图片描述
可以看到,STL库里set也是用key vlaue搜索模型的红黑树。这颗红黑树的key存的是key,value存的也是key。下面再看看map。
在这里插入图片描述
通过上图可以看到,库里面的map的底层用的也是一样的红黑树。不同的是这颗红黑树的value存的是一个pair结构。为什么这么设计呢?那就再往深层次谈一谈,我们下面就看看库里面的红黑树是怎么设计的。

在这里插入图片描述
可以看到rb_tree是通过Value模版去实例化树的节点的。set传的Value是Key,底层__rb_tree_node类实例化的就是key的树。map传的Value是pair,底层__rb_tree_node类实例化的就是key value的树。这里的__rb_tree_node的就像一个适配器,上层传什么模板的Value,底层就对应实例化不同的节点。

虽然set和map的模板参数一样,都是Key Value。由于Value传的类型不一样,底层红黑树实例化也是不一样的。所以,本质上map和set用的不是同一颗红黑树,而是使用的同一份红黑树的模板。

那为什么还需要Key这个模板呢?在find和erase接口中还需要这个类型。

模拟实现map和set

我们现将红黑树的代码进行一下调整,将原来Key Value 的模板改成库里面的那样。然后类似于库里的map和set先把类模板和类成员基本的框架搭出来。
在这里插入图片描述

引入第三个模板参数来获取T的key

下面调整一下红黑树的插入接口。
在这里插入图片描述

通过上图看,在修改后的代码在比较逻辑上面会有问题。如果是map的话,这里的T就是一个pair,而pair的比较不符合实际用key来比较的需求。解决的方式就是用仿函数来处理,这里的仿函数和在优先级队列那里设计的方式又有区别。这里的仿函数用于将pair中key对象取出来。方便我们在需要用key进行比较的场景使用。

下面需要给红黑树再添加一个模板参数,然后分别在map和set内部实现一个仿函数

在这里插入图片描述

从这里就不得不佩服大佬设计数据结构的思路了。通过学习大佬前辈们在库里面的实现思路,不得不为大佬对于泛型编程水平折服。通过加一个模板,对map设计一个仿函数就可以达到通过T获取Key对象,这里set其实是被map稍稍拖了下后腿,不过问题不大。

迭代器的模拟实现

先以set为例讲解一下迭代器的实现思路,首先是迭代器的设计,这里使用T一个模板参数就能满足map和set的需求了。迭代器基础功能的实现这里不做过多讲解。重点需要理解的是operator++的重载。

在这里插入图片描述
上图中的set在用红黑树的迭代器时,用typename是告诉编译器这是一个类型。

为了让我们简易版本的迭代器能跑起来,还需要实现operator++的重载。根据搜索二叉树的性质可知,当树进行中序遍历时,值是有序的。那么起始位置就是最小的值所在的节点。其实就是从根节点出发,树的最左子树。

那如何访问下一个节点呢?这要分两类情况讨论。
情况一:当前节点右子树存在,就去访问右子树的最小节点。
在这里插入图片描述
情况二 当前节点的右子树为空,去访问孩子是父亲左边的祖先。
在这里插入图片描述
情况二又可以细分下面两种情况,分别是当前节点在父亲节点的左边和右边。若当前节点在父亲的左边,直接将修改当前节点值为当前节点的父节点即可。若当前节点是父亲的右子树,则需要向上遍历,直到找到当前节点是父亲节点的左子数时,修改当前节点的值即可。需要注意的是,当最大的值的节点访问完后,parent为空。所以,可以将end()的返回值设置成空。
在这里插入图片描述

参考代码如下
在这里插入图片描述
下面将operator–实现一下。其实operator–和operator++的实现起来大不差不差,两个大情况正好和operator++的互为镜像。下面就直接提供一份代码以供参考。
在这里插入图片描述

而这里的简易迭代器其实是不符合set的要求的,因为set的key值是不能被修改的。所以我们需要封装一个const迭代器,而库里面set的普通迭代器和const迭代器都是const迭代器。
在这里插入图片描述
这里顺便提一下map的普通 迭代器是如何支持修改Value而禁止修改Key的。map是通过模板T这个模板参数参数的pair<const K, Value>

在这里插入图片描述
将上面的代码进行一下调整,将迭代器的模板参数增加两个,用于适配出const迭代器。然后修改一下set的迭代器部分的内容。
在这里插入图片描述
接下来把map的部分写出来。
在这里插入图片描述
map的普通迭代器要求Key不能被修改,Value可以修改。将pair里的K用const修饰,这样就避免Key在使用普通迭代器的情况下被修改了。

map的operator[]的模拟实现

在前面的map/set使用的文章中介绍map的[]时提到,其实map的operator[]底层使用了insert接口进行实现的。
在这里插入图片描述
下面先将红黑树的insert接口进行一下修改。以符合我们的需求。
在这里插入图片描述
然后修改set的insert接口的时候,问题出现了。
在这里插入图片描述
这里由于set的iterator是const迭代器,insert返回的是普通迭代器。这里类型不匹配了。如何解决?先看一看库是怎么处理的。
在这里插入图片描述
然后,直接借鉴库的写法将接口修改一下。
在这里插入图片描述
根据库的写法修改后依旧还是有类型不匹配的问题。因为set的iterator是const迭代器,而insert的返回值是普通迭代器。要想能够匹配需要提供一个用普通迭代器构造const迭代器的接口。下面就看一看源码是如何实现的。
在这里插入图片描述
库里面的解决方案是提供了一个特殊的构造函数。当iterator被实例化成普通迭代器时,这就是普通迭代器的拷贝构造。当iterator被实例化成const迭代器时,这个函数就会变成支持普通迭代器构造const迭代器的构造函数。这样上面set关于返回值的问题就能够得以解决了。
在这里插入图片描述
解决了这个问题,那就将operator[]手撕出来map/set的封装的关键知识学习就差不多了。operator[]实现思路如下,通过复用insert[]来获取key所在的节点的pair。然后,取出pair的迭代器并使用operator->访问对应的Value即可。
在这里插入图片描述

总结

通过使用红黑树封装map/set,可以加深我们对于容器的理解。通过模拟实现set/map的关键部分,学习到了大佬在设计库的思想。一份红黑树模板可以实例化成两份不一样的容器。由于map和set性质上的差异,相应的红黑树的某些接口如,insert需要进行相应的调整。在迭代器部分设计的特殊的构造函数是非超巧妙的,让set的insert为了兼容map的insert的返回普通迭代器时,用这个普通迭代器构造成const迭代器,符合set的设计。

完整参考代码

//set
#pragma once
#include "RBTree.h"

namespace xyx
{
	template<class K>
	class set
	{	
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		const_iterator begin() const
		{
			return _tree.begin();
		}

		const_iterator end() const
		{
			return _tree.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
			pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> p = _tree.Insert(key);
			return pair<iterator, bool>(p.first, p.second);
		}

	
	private:
		RBTree<K, K, SetKeyOfT> _tree;
	};
}
//map
#pragma once
#include "RBTree.h"


namespace xyx
{
	template<class K, class V>
	class map
	{	
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

		iterator begin() 
		{
			return _tree.begin();
		}

		iterator end() 
		{
			return _tree.end();
		}

		const_iterator begin() const
		{
			return _tree.begin();
		}

		const_iterator end() const
		{
			return _tree.end();
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> p = insert(make_pair(key, V()));
			return p.first->second;
		}

		pair<iterator, bool> insert(const pair<K, V> kv)
		{
			return _tree.Insert(kv);
		}


	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _tree;
	};
}
//RBTree

#pragma once
#include <utility>

enum COLOR
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
    RBTreeNode(const T& data)
        :_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 Ptr, class Ref>
struct __TreeIterator
{
    typedef  RBTreeNode<T> Node;
    typedef __TreeIterator<T, T*, T&> iterator;
    typedef __TreeIterator<T, Ptr, Ref> Self;

    Node* _node;

    __TreeIterator(const iterator& it)
        :_node(it._node)
    {}

    __TreeIterator(Node* root)
        :_node(root)
    {}

    Ref operator*()
    {
        return _node->_data;
    }

    Ptr operator->()
    {
        return &_node->_data;
    }

    bool operator!=(const Self& s) const 
    {
        return _node != s._node;
    }

    bool operator==(const Self& s) const
    {
        return _node == s._node;
    }

    Self& operator--(int)
    {
        Self tmp = *this;
        //左子树不为空,访问左子树最右节点(最大节点)
        if (_node->_left)
        {
            Node* subRight = _node->_left;
            while (subRight->_right)
            {
                subRight = subRight->_right;
            }
            _node = subRight;
        }
        else //左子树为空,访问孩子是父亲右的祖先节点
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_right)
            {
                cur = parent;
                parent = parent->_parent;
            }

            _node = parent;
        }

        return tmp;
    }

    Self& operator--()
    {
        //左子树不为空,访问左子树最右节点(最大节点)
        if (_node->_left)
        {
            Node* subRight = _node->_left;
            while (subRight->_right)
            {
                subRight = subRight->_right;
            }
            _node = subRight;
        }
        else //左子树为空,访问孩子是父亲右的祖先节点
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = parent;
                parent = parent->_parent;
            }

            _node = parent;
        }

        return *this;
    }

    Self& operator++()
    {
        //右子树不为空,访问右子树最左节点(最小节点)
        if (_node->_right)
        {
            Node* subleft = _node->_right;
            while (subleft->_left)
            {
                subleft = subleft->_left;
            }
            _node = subleft;
        }
        else //右子树为空,访问孩子是父亲左的祖先节点
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent)
            {
                if (cur == parent->_left)
                {
                    break;
                }
                else
                {
                    cur = parent;
                    parent = parent->_parent;
                }
            }
            _node = parent;
        }

        return *this;
    }

};

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
    typedef __TreeIterator<T, T*, T&> iterator;
    typedef __TreeIterator<T, const T*, const T&> const_iterator;



    const_iterator begin() const
    {
        //树的最左节点(最小节点)
        Node* leftmin = _root;
        while (leftmin && leftmin->_left)
        {
            leftmin = leftmin->_left;
        }

        return const_iterator(leftmin);
    }

    const_iterator end() const
    {
        return const_iterator(nullptr);
    }

    iterator begin()
    {
        //树的最左节点(最小节点)
        Node* leftmin = _root;
        while (leftmin && leftmin->_left)
        {
            leftmin = leftmin->_left;
        }

        return iterator(leftmin);
    }

    iterator end()
    {
        return iterator(nullptr);
    }

    RBTree()
        :_root(nullptr) 
    {}

    Node* find(const K& key)
    {
        Node* parent = nullptr;
        Node* cur = _root;
        KeyOfT kot;

        while (cur)
        {
            if (kot(cur->data) < key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (kot(cur->data) > key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return cur;
            }
        }

        return nullptr;
    }

    pair <iterator, bool> Insert(const T& data)
    {
        //第一次插入数据
        if (_root == nullptr)
        {
            //根节点只能是黑色
            _root = new Node(data);
            _root->_color = BLACK;
            return make_pair(iterator(_root), true);
        }

        //找到合适的位置插入
        Node* parent = nullptr;
        Node* cur = _root;
        KeyOfT kot;

        while (cur)
        {
            if (kot(cur->_data) < kot(data))
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (kot(cur->_data) > kot(data))
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                //key已经存在
                return make_pair(iterator(cur), false);
            }
        }

        //走到这里表示找到合适的位置插入
        cur = new Node(data);
        Node* newnode = cur;
        cur->_color = RED;//新节点为红色

        if (kot(parent->_data) < kot(data))
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;
        }

        cur->_parent = parent;

        //红黑树的控制逻辑
        //父亲节点的颜色为黑色不需要调整
        //反之,需要调整
        while (parent && parent->_color == RED)
        {
            Node* grandFather = parent->_parent;
            if (parent == grandFather->_left)//考虑父亲在祖父的位置
            {
                Node* uncle = grandFather->_right;
                
                if (uncle && uncle->_color == RED)//uncle 存在且为红
                {
                    //将uncle和parent改成黑色,grandfather改成红色
                    uncle->_color = parent->_color = BLACK;
                    grandFather->_color = RED;

                    //如果祖父就是根节点,将祖父修改成黑色,退出即可
                    if (grandFather == _root)
                    {
                        grandFather->_color = BLACK;
                        break;
                    }
                    else //然后继续向上迭代处理
                    {
                        cur = grandFather;
                        parent = cur->_parent;
                    }
                }
                else //uncle不存在 或者 uncle存在且为黑
                {
                    if (parent->_left == cur) //单旋的场景
                    {
                        RotateR(grandFather);
                        parent->_color = BLACK;
                        grandFather->_color = RED;
                    }
                    else // LR双旋转
                    {
                        RotateL(parent);
                        RotateR(grandFather);
                        cur->_color = BLACK;
                        grandFather->_color = RED;
                    }

                    break; //旋转后直接退出即可
                }
            }
            else //parent == grandFather->_right
            {
                Node* uncle = grandFather->_left;

                if (uncle && uncle->_color == RED) //uncle 存在且为红
                {
                    uncle->_color = parent->_color = BLACK;
                    grandFather->_color = RED;

                    //如果祖父就是根节点,将祖父修改成黑色,退出即可
                    if (grandFather == _root)
                    {
                        grandFather->_color = BLACK;
                        break;
                    }
                    else //然后继续向上迭代处理
                    {
                        cur = grandFather;
                        parent = cur->_parent;
                    }
                }
                else //uncle不存在 或者 uncle存在且为黑
                {
                    if (parent->_right == cur) //单旋的场景
                    {
                        RotateL(grandFather);
                        parent->_color = BLACK;
                        grandFather->_color = RED;
                    }
                    else // RL双旋转
                    {
                        RotateR(parent);
                        RotateL(grandFather);
                        cur->_color = BLACK;
                        grandFather->_color = RED;
                    }

                    break; //旋转后直接退出即可
                }
            }
        }

        return make_pair(iterator(newnode), true);
    }

    void RotateL(Node* parent)
    {
        //_rotateCount++;
        //将curLeft连接到parent的右子树中
        //将parent连接到cur的左子树中
        //修改cur、parent、curLeft的_parent

        Node* cur = parent->_right;
        Node* curLeft = cur->_left;
        parent->_right = curLeft;

        // curLeft可能为空
        if (curLeft)
            curLeft->_parent = parent;

        cur->_left = parent;

        //提前保存parent的_parent避免节点丢失
        Node* ppnode = parent->_parent;
        parent->_parent = cur;


        //边界情况,修改_root
        if (parent == _root)
        {
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
            if (ppnode->_left == parent)
                ppnode->_left = cur;
            else
                ppnode->_right = cur;

            cur->_parent = ppnode;
        }
    }

    void RotateR(Node* parent)
    {
        //++_rotateCount;
        //将cur的右子树连接到parent的左子数
        //让parent连接到cur的右子树
        //修改parent、cur、curRight的_parent

        Node* cur = parent->_left;
        Node* curRight = cur->_right;

        parent->_left = curRight;

        //curRight可能为空
        if (curRight)
            curRight->_parent = parent;

        //避免parent->_parent节点丢失
        Node* ppnode = parent->_parent;

        parent->_parent = cur;
        cur->_right = parent;

        //边界情况
        if (parent == _root)
        {
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
            if (ppnode->_left == parent)
                ppnode->_left = cur;
            else
                ppnode->_right = cur;

            cur->_parent = ppnode;
        }
    }

    bool checkcolor(Node* root, int blacknum, int benchmark)
    {
        //处理边界情况
        if (root == nullptr) 
        {
            //走到NIL节点,基准值与当前路径黑节点数量不一致
            if (blacknum != benchmark)
                return false;

            return true;
        }
        
        //统计当前路径的黑节点数量
        if (root->_color == BLACK)
            ++blacknum;

        //出现连续红节点的情况
        if (root->_color == RED && root->_parent && root->_parent->_color == RED)
        {
            cout << root->_kv.first << "-> 位置出现连续红节点" << endl;
            return false;
        }

        //子问题分治
        return checkcolor(root->_left, blacknum, benchmark) && checkcolor(root->_right, blacknum, benchmark);
    }

    bool IsBalance()
    {
        return IsBalance(_root);
    }
    
    bool IsBalance(Node* root)
    {
        //处理边界情况
        if (root == nullptr)
            return true;
        
        //根节点只能为黑色
        if (_root->_color == RED)
        {
            return false;
        }

        //以最左路径的黑节点的数量为基准值
        int benchmark = 0;
        Node* cur = root;
        while (cur)
        {
            if(cur->_color == BLACK)
                ++benchmark;
            cur = cur->_left;
        }

        return checkcolor(root, 0, benchmark);
    }

    int Height()
    {
        return Height(_root);
    }

    int Height(Node* root)
    {
        //处理边界情况
        if (root == nullptr)
            return 0;

        int leftH = Height(root->_left);
        int rightH = Height(root->_right);

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

private:
	Node* _root;

//public:
//    int _rotateCount = 0;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2210965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第十五章 RabbitMQ延迟消息之延迟插件

目录 一、引言 二、延迟插件安装 2.1. 下载插件 2.2. 安装插件 2.3. 确认插件是否生效 三、核心代码 四、运行效果 五、总结 一、引言 上一章我们讲到通过死信队列组合消息过期时间来实现延迟消息&#xff0c;但相对而言这并不是比较好的方式。它的代码实现相对来说比…

Java->排序

目录 一、排序 1.概念 2.常见的排序算法 二、常见排序算法的实现 1.插入排序 1.1直接插入排序 1.2希尔排序(缩小增量法) 1.3直接插入排序和希尔排序的耗时比较 2.选择排序 2.1直接选择排序 2.2堆排序 2.3直接选择排序与堆排序的耗时比较 3.交换排序 3.1冒泡排序…

你知道C++多少——继承

&#x1f308;个人主页&#xff1a;小新_- &#x1f388;个人座右铭&#xff1a;“成功者不是从不失败的人&#xff0c;而是从不放弃的人&#xff01;”&#x1f388; &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f3c6;所属专栏&#xff1…

蓝桥杯模块三:蜂鸣器和继电器的基本控制

模块训练题目&#xff1a; 一、蜂鸣器电路图 1.电路图 2.电路分析 138译码器控制Y5,Y5控制Y5C&#xff0c;Y5C低电平控制芯片开启P0口控制ULN2003继而控制蜂鸣器端口和继电器端口 二、程序代码 1.138译码器控制端口函数 建立初始化函数选择锁存器 2.实现题目功能 在LED代…

24-10-13-读书笔记(二十五)-《一只特立独行的猪》([中] 王小波)用一生来学习艺术

文章目录 《一只特立独行的猪》&#xff08;[中] 王小波&#xff09;目录阅读笔记记录总结 《一只特立独行的猪》&#xff08;[中] 王小波&#xff09; 十月第五篇&#xff0c;放慢脚步&#xff0c;秋季快要过去了&#xff0c;要步入冬季了&#xff0c;心中也是有些跌宕起伏&am…

Guitar Pro怎么制作伴奏谱,吉他谱制作软件guitar pro教程

在诸多教学吉他谱制作软件中Guitar Pro是一款非常优秀的软件&#xff0c;它是专为吉他和其他弦乐器设计&#xff0c;且能提供乐谱编辑、音轨录制和播放、和弦与音阶库等功能的强大软件。Guitar Pro不仅具有强大的乐谱编辑功能&#xff0c;其用户界面也易于上手&#xff0c;更支…

ThingsBoard规则链节点:Script节点详解

引言 脚本节点简介 用法 含义 应用场景 实际项目运用示例 智能楼宇管理系统 工业自动化生产线 结论 引言 ThingsBoard是一个功能强大的物联网平台&#xff0c;它支持设备管理、数据收集与处理以及实时监控。其核心组件之一是规则引擎&#xff0c;允许用户定义复杂的业务…

vue特效,一片动态星空

vue实现漂亮星空&#xff0c;超级简单 1.创建vue项目&#xff1a; vue create demo 2.注册vuecli : npm i element-ui -S 3.加载依赖 &#xff1a;npm i 4.运行项目 :npm run serve <!DOCTYPE html> <html lang"en"> <head><…

JavaWeb 19 AJAX

"我就是希望你好&#xff0c;就像很多人希望我好一样&#xff0c;特别简单&#xff0c;特别真挚。也不为了什么&#xff0c;就是希望你好" —— 24.10.13 一、什么是AJAX AJAX Asynchronous JavaScript and XML(异步的JavaScript和XML) AJAX不是新的编程语言&…

仿新版QQ的聊天小软件

仿新版QQ的聊天小软件 文章说明核心源码效果展示源码下载 文章说明 新开一个聊天组件的项目的想法主要来源于想学习一下消息队列的使用&#xff0c;后来在书写界面和一些功能模块时&#xff0c;又想到可以抽离出来&#xff0c;分别写几篇文章&#xff0c;主要介绍扫码登陆、消息…

MQ快速入门【详细】个人笔记 讲解通俗易懂

1.同步通讯和异步通讯 同步通讯&#xff1a;如果举个例子来说&#xff0c;同步通讯就像是两个人在打电话&#xff0c;一方说的话&#xff0c;能够立马传给另一方&#xff0c;消息的时效性非常高&#xff0c;但是相对的&#xff0c;只能是给一个人通讯&#xff0c;如果这个时候&…

React (三) 创建安装脚手架,类组件与函数式组件;生命周期;父子通信props;插槽;非父子通信Context

文章目录 一、脚手架的创建与安装1. 认识脚手架2. 安装脚手架3. 创建react项目4. 项目结构 二、从0编写三、组件化开发1. 什么是组件化开发2. 类组件3. render函数4. 函数式组件 四、生命周期1. 挂载Mount2. 更新Update3. 卸载Unmount4. 不常用的生命周期 五、父子组件通信1. 父…

数据结构之队列(python)

华子目录 1.队列存储结构1.1队列基本介绍1.2队列的实现方式 2.顺序队列2.1顺序队列的介绍2.2顺序队列的简单实现2.3代码实现 3.链式队列和基本操作3.1链式队列数据入队3.2链式队列数据出队3.3队列的链式表示和实现 1.队列存储结构 1.1队列基本介绍 队列的两端都"开口&qu…

springcloud之服务集群注册与发现 Eureka

前言 1&#xff1a;对于能提供完整领域服务接口功能的RPC而言&#xff0c;例如&#xff1b;gRPC、Thrift、Dubbo等&#xff0c;服务的注册与发现都是核心功能中非常重要的一环&#xff0c;使得微服务得到统一管理。 2&#xff1a;在分布式领域中有个著名的CAP理论&#xff1b;…

Vue3 + Element plus 实现切换el-radio前二次确认

Vue3 Element plus 实现切换el-radio前二次确认 场景&#xff1a;点击切换el-radio之前判断当前内容是否有改变&#xff0c;如有改变弹窗提示切换el-radio将销毁操作&#xff0c;弹窗二次确认是否切换 问题&#xff1a; el-radio 没有提供类似于beforeUpdate这样的钩子去处理这…

探索极简计算的新边界:从Uxn虚拟机看未来编程生态

越来越多的开发者追求复杂度和功能性的极致,然而,有一个小众的编程社区选择了截然不同的道路——极简主义。Uxn虚拟机便是这一思潮的代表之一。它通过简洁的指令集和有限的硬件资源模拟,试图打造一种可以在多种设备上运行的便携性编程环境。 与主流的重型操作系统和复杂…

HTB:Legacy[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many TCP ports are open on Legacy? 2.What is the 2008 CVE ID for a vulnerability in SMB that allows for remote code execution? 3.What is the name of the Metasploit module that exploits CVE-2008-4250? 4.When expl…

VS+QT 自定义插件变成动态库加载及使用

一、前言 有个界面需要重复使用某个自定义的控件&#xff0c;希望自定义控件能够像动态库文件那样&#xff0c;添加引用lib就能使用&#xff0c;经过多次太坑后&#xff0c;总结如下 二、实现方式 ① 新建项目&#xff0c;选择"Qt Designer Custom Widget" 创建自定…

Springboot从入门到起飞-【day01】

个人主页→VON 收录专栏→Springboot从入门到起飞 一、前言 经过了近两个月的沉淀开始了新专栏的学习&#xff0c;经过深思熟虑还是决定重新学习java&#xff0c;因为基础部分东西太多太乱就不进行逐一的更新了&#xff0c;等到学完了一同进行更新。 二、Springboot简要概述 …

kafka消息队列核心内容及常见问题

目录 1. 使用消息队列的目的&#xff08;优点与缺点&#xff09; 2. 常见各消息队列对比 3. kafka介绍 3.1 kafka简介 3.2 kafka特点 3.3 kafka系统架构 3.4 设置数据可靠性 3.4.1 Topic 分区副本 3.4.2 消息确认机制 4. 常见问题&#xff08;面试题&#xff09; 4.…