【C++】17.map和set的模拟实现

news2024/9/21 20:38:22

1.红黑树中的迭代器

operator++是关键 迭代需要走中序 如何走中序?

_node从左子树的最左结点开始遍历走中序

分两类情况:

  1. 如果右树不为空 那么中序的下一个就是右子树的最左结点

  2. 如果右树为空 那么表示_node所在的子树已经完成 在一个结点的祖先去找

    沿着路径往上孩子是它的左的那个祖先

//迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef __TreeIterator<T, Ref, Ptr> Self;
    Node* _node;

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

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

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

    //++如何走?
    Self& operator++()
    {
        //STL给了头结点 左孩子指向最左结点 右孩子指向最右结点
        //_node为左子树的最左结点 从这开始
        //1.如果右不为空 中序的下一个就是右子树的最左结点
        //2.如果右为空 表示_node所在的子树已经放完成 在一个结点的祖先去找
        //沿着路径往上孩子是它的左的那个祖先
        //右树不为空
        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 && cur == parent->_right)
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }

            _node = parent;
        }
        return *this;
    }

    Self& operator--()
    {
        //与++相反
        //右根左
        //1.如果左不为空 就访问左树的最右结点
        //2.如果左为空 找为右孩子的那个祖先
        return *this;
    }

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

整颗红黑树

#pragma once
#include <iostream>
using namespace std;

enum Colour
{
    BLACK,
    RED,
};

template<class T>
struct RBTreeNode
{
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    
    T _data;

    Colour _col;

    RBTreeNode(const T& data)
        :_left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _data(data)
        , _col(RED)
    {}
};

//迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef __TreeIterator<T, Ref, Ptr> Self;
    Node* _node;

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

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

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

    //++如何走?
    Self& operator++()
    {
        //STL给了头结点 左孩子指向最左结点 右孩子指向最右结点
        //_node为左子树的最左结点 从这开始
        //1.如果右不为空 中序的下一个就是右子树的最左结点
        //2.如果右为空 表示_node所在的子树已经放完成 在一个结点的祖先去找
        //沿着路径往上孩子是它的左的那个祖先
        //右树不为空
        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 && cur == parent->_right)
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }

            _node = parent;
        }
        return *this;
    }

    Self& operator--()
    {
        //与++相反
        //右根左
        //1.如果左不为空 就访问左树的最右结点
        //2.如果左为空 找为右孩子的那个祖先
        return *this;
    }

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

template<class K, class T, class KOfT>
class RBTree
{
    typedef RBTreeNode<T> Node;
public:
    typedef __TreeIterator<T, T&, T*> iterator;
    typedef __TreeIterator<T, const T&, const T*> const_iterator;

    iterator begin()
    {
        Node* cur = _root;
        while (cur && cur->_left)
        {
            cur = cur->_left;
        }

        return iterator(cur);
    }

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

    //插入
    pair<iterator, bool> Insert(const T& data)
    {
        if (_root == nullptr)
        {
            _root = new Node(data);
            _root->_col = BLACK; //根结点必须是黑色
            return make_pair(iterator(_root), true); //插入成功
        }

        KOfT koft;
        //二叉搜索树
        Node* cur = _root;
        Node* parent = nullptr;
        while (cur)
        {
            if (koft(data) < koft(cur->_data))
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (koft(data) > koft(cur->_data))
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                return make_pair(iterator(cur), false);
            }
        }

        //将结点插入
        cur = new Node(data);
        Node* newnode = cur;
        if (koft(cur->_data) < koft(parent->_data))
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_right = cur;
            cur->_parent = parent;
        }

        //------------------------
        //新增结点红的 or 黑的
        //破坏b还是c 破坏b较轻 
        //1.只影响一条路径
        //2.还不一定破坏规则
        //所以选红的
        cur->_col = RED;
        //-------------------------------
        //调色
        //第一种情况:cur为红 p为红 g为黑 只要u存在且为红-> p和u变黑 g变红 继续往上处理 如果到根 根要变回黑 
        //ps:需要注意的是 这里只关注颜色 而p g u几个结点在左边或者右边是一样的
        //最后就是为了防止连续的红和保持每条支路黑的结点数量一样
        //-------------------------------------
        //第二种情况:cur为红 p为红 g为黑 u不存在/u为黑 直线
        //1.如果u不存在 那么cur就是新增结点
        //旋转+变色
        //旋转:左单旋 or 右单旋
        //变色:g变红 p变黑
        //--------------------------------------- 
        //2.如果u存在且为黑 那么cur一定不是新增
        //你要保证每条路黑色结点数量一样->cur一定是黑的
        //cur变红的话就是第一种情况
        //---------------------------------------
        //第三种情况:cur为红 p为红 g为黑 u不存在/u为黑 折线
        //旋转:左右双旋 or 右左双旋
        //变色:g变红 cur变黑
        //-------------------------------------

        //parent为红
        while (parent && parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                if (uncle && uncle->_col == RED) //情况1:uncle存在且为红
                {
                    //颜色调整
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    //继续往上处理
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else //情况2+情况3:uncle不存在 + uncle存在且为黑
                {
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);

                        grandfather->_col = RED;
                        parent->_col = BLACK;
                    }
                    else //cur == parent->_right
                    {
                        RotateLR(grandfather);

                        grandfather->_col = RED;
                        cur->_col = BLACK;
                    }
                    break; //子树旋转后,该子树的根变成了黑色,无需继续往上进行处理
                }
            }
            else //parent是grandfather的右孩子
            {
                Node* uncle = grandfather->_left;
                if (uncle && uncle->_col == RED) //情况1:uncle存在且为红
                {
                    //颜色调整
                    uncle->_col = parent->_col = BLACK;
                    grandfather->_col = RED;

                    //继续往上处理
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else //情况2+情况3:uncle不存在 + uncle存在且为黑
                {
                    if (cur == parent->_left)
                    {
                        RotateRL(grandfather);

                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else //cur == parent->_right
                    {
                        RotateL(grandfather);

                        grandfather->_col = RED;
                        parent->_col = BLACK;
                    }
                    break; //子树旋转后,该子树的根变成了黑色,无需继续往上进行处理
                }
            }
        }
        _root->_col = BLACK; //根结点的颜色为黑色(可能被情况一变成了红色,需要变回黑色)
        
        return make_pair(iterator(newnode), true);
    }

    //左单旋
    void RotateL(Node* parent)
    {
        //需要处理subR的parent left
        //需要处理subRL的parent
        //需要处理parent的right parent
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* parentParent = parent->_parent;

        parent->_right = subRL;
        if (subRL)
            subRL->_parent = parent;

        subR->_left = parent;
        parent->_parent = subR;

        if (parentParent == nullptr)
        {
            _root = subR;
            _root->_parent = nullptr;
        }
        else
        {
            if (parent == parentParent->_left)
            {
                parentParent->_left = subR;
            }
            else
            {
                parentParent->_right = subR;
            }
            subR->_parent = parentParent;
        }
    }

    //右单旋
    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* parentParent = parent->_parent;

        parent->_left = subLR;
        if (subLR)
            subLR->_parent = parent;

        subL->_right = parent;
        parent->_parent = subL;

        if (parentParent == nullptr)
        {
            _root = subL;
            _root->_parent = nullptr;
        }
        else
        {
            if (parent == parentParent->_left)
            {
                parentParent->_left = subL;
            }
            else
            {
                parentParent->_right = subL;
            }
            subL->_parent = parentParent;
        }
    }

    //左右双旋
    void RotateLR(Node* parent)
    {
        RotateL(parent->_left);
        RotateR(parent);
    }

    //右左双旋
    void RotateRL(Node* parent)
    {
        RotateR(parent->_right);
        RotateL(parent);
    }

    //中序遍历
    void Inorder()
    {
        _Inorder(_root);
    }


    void _Inorder(Node* root)
    {
        if (root == nullptr)
            return;
        _Inorder(root->_left);
        //cout << root->_kv.first << ":" << root->_kv.second << endl;
        _Inorder(root->_right);
    }

    //判断是否为红黑树
    bool ISRBTree()
    {
        if (_root == nullptr) //空树是红黑树
        {
            return true;
        }
        if (_root->_col == RED)
        {
            cout << "error:根结点为红色" << endl;
            return false;
        }

        //找最左路径作为黑色结点数目的参考值
        Node* cur = _root;
        int BlackCount = 0;
        while (cur)
        {
            if (cur->_col == BLACK)
                BlackCount++;
            cur = cur->_left;
        }

        int count = 0;
        return _ISRBTree(_root, count, BlackCount);
    }

    bool _ISRBTree(Node* root, int count, int BlackCount)
    {
        if (root == nullptr) //该路径已经走完了
        {
            if (count != BlackCount)
            {
                cout << "error:黑色结点的数目不相等" << endl;
                return false;
            }
            return true;
        }

        if (root->_col == RED && root->_parent->_col == RED)
        {
            cout << "error:存在连续的红色结点" << endl;
            return false;
        }
        if (root->_col == BLACK)
        {
            count++;
        }
        return _ISRBTree(root->_left, count, BlackCount) && _ISRBTree(root->_right, count, BlackCount);
    }

    //查找函数
    iterator Find(const K& key)
    {
        KOfT koft;
        Node* cur = _root;
        while (cur)
        {
            if (koft(data) < koft(cur->_data))
            {
                cur = cur->_left;
            }
            else if (koft(data) > koft(cur->_data))
            {
                cur = cur->_right;
            }
            else
            {
                return iterator(cur);
            }
        }

        return iterator(nullptr);
    }

private:
    Node* _root = nullptr;
};

2.MySet和MyMap

如何使用一颗红黑树实现map和set

利用模板 KOfT 传K的时候返回K 传pair<K,V>的时候也返回K

这样就可以同时满足set和map

MySet

#pragma once

#include "RBTree1.h"

namespace szh
{
    template<class K>
    class set
    {
        struct SetKeyOfT
        {
            const K& operator()(const K& k)
            {
                return k;
            }
        };
    public:
        //编译器会找不到 没有实例化 加上typename 告诉编译器这里是类型名称
        typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;

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

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

        pair<iterator, bool> Insert(const K& k)
        {
            return _t.Insert(k);
        }

    private:
        RBTree<K, K, SetKeyOfT> _t;
    };

    void test_set()
    {
        set<int> s;
        s.Insert(3);
        s.Insert(4);
        s.Insert(1);
        s.Insert(2);
        s.Insert(5);
        
        set<int>::iterator it = s.begin();
        while (it != s.end())
        {
            cout << *it << " ";
            ++it;
        }
        cout << endl;

        for (auto k: s)
        {
            cout << k << " ";
        }
        cout << endl;
    }
};

MyMap

#pragma once

#include "RBTree1.h"

namespace szh
{
    template<class K, class V>
    class map
    {
        struct MapKeyOfT
        {
            const K& operator()(const pair<K, V>& kv)
            {
                return kv.first;
            }
        };
    public:
        //编译器会找不到 没有实例化 加上typename 告诉编译器这里是类型名称
        typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;

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

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

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

        //为了实现operator[] 多加一个迭代器返回 返回value
        V& operator[](const K& key)
        {
            pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));//缺省
            return ret.first->second;
            //ret.first拿到迭代器 迭代器->second对应value
        }

    private:
        RBTree<K, pair<K, V>, MapKeyOfT> _t;
    };

    void test_map()
    {
        map<int, int> m;
        m.Insert(make_pair(1, 1));
        m.Insert(make_pair(3, 3));
        m.Insert(make_pair(10, 10));
        m.Insert(make_pair(5, 5));
        m.Insert(make_pair(6, 6));

        map<int, int>::iterator it = m.begin();
        while (it != m.end())
        {
            cout << it->first << ":" << it->second << endl;
            ++it;
        }
        cout << endl;

        //支持范围for
        //for (auto kv : m)
        //{
        //	cout << kv.first << ":" << kv.second << endl;
        //}
        //cout << endl;

        //统计次数
        string strs[] = { "西瓜","樱桃","西瓜","苹果","西瓜","西瓜","西瓜","苹果" };
        map<string, int> countMap;
        for (auto& str : strs)
        {
            countMap[str]++;
            //第一次出现会先插入并同时返回Key所在的Value 然后value++  
            //第二次只返回Key所在的Value 然后value++ 
        }

        for (auto kv : countMap)
        {
            cout << kv.first << ":" << kv.second << endl;
        }
        cout << endl;
    }
}

范围for以及计数都可以使用

3.测试

#include "MyMap.h"
#include "MySet.h"

int main()
{
    szh::test_map();
    cout << endl;
    szh::test_set();
    return 0;
}

  

【C++】17.map和set的模拟实现 完

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

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

相关文章

【Linux】网络基础(网络层与链路层)

网络层与链路层典型协议网络层IP 地址地址管理网络号的划分&#xff08;五种&#xff09;特殊的网络以及IP地址路由选择链路层MAC 地址ARP 协议MTU 最大传输单元其他典型协议&#xff1a;ICMP、DNS 、NAT技术ICMP协议DNS 协议NAT & NAPT 技术代理网络层 功能&#xff1a;负…

算法设计与分析阶段考总结

前言&#xff1a;基本是为了我自己看的一些我容易忘记的东西&#xff0c;为考试作准备把 第一章 算法中的基本概念 程序设计数据结构算法 算法特性 1.有穷性 2.确定性 3.可行性 4.输出 5.输入 算法复杂性分析 算法复杂性依赖于&#xff1a;问题规模N&#xff0c;输入I&#xff…

TCP 的可靠传输

目录 可靠传输有啥用确认应答超时重传总结 可靠传输有啥用 我们知道相比于 UDP, TCP 的传输是可靠的, 啥意思呢? 就是 UDP 发送的数据, 它自己不知道发送的数据对方是否接收到. 而 TCP 发送的数据, 它知道对方是否接收到, 也就是说对方会给个应答. 假设一个场景 : 甲要付款给…

全网最详细,Jmeter性能测试-性能基础详解,接口关联与编写Java脚本(三)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 接口关联 接口关联…

Vector - CAPL - CAN x 总线信息获取(续1)

继续.... 目录 OverLoadFrameCount -- 过载帧数量 代码示例 OverLoadFrameRate -- 过载帧速率 代码示例 StandardFrameCount -- 标准帧数量 代码示例 StandardFrameRate -- 标准帧速率 代码示例 StandardRemoteFrameCount -- 标准远程帧数量 代码示例 StandardRemote…

Java注解编译期处理AbstractProcessor详解

文章目录概述注解处理器注解处理流程AbstractProcessorgetSupportedOptions()getSupportedAnnotationTypesgetSupportedSourceVersioninit初始化process 处理方法如何注册注解处理器如何调试编译期代码Maven相关配置(指定生效的Processor)注意事项自定义注解处理器范例范例一&a…

多线程小知识

多线程 多线程环境使用哈希表HashMapHashtableConcurrentHashMap 死锁死锁是什么两者之间的死锁多人之间的死锁 死锁产生的四个必要条件:互斥使用不可抢占请求和保持循环等待 不同的锁ReentrantLockSynchronized和ReentrantLock之间的区别:具体选择Synchronized加锁工作过程偏向…

using namespace std 是什么意思——C++命名空间

目录 namespace命名空间局部域和全局域namespace展开命名空间指定命名空间 命名空间的嵌套不同文件中的同名命名空间 using namespace std 是什么意思 我们先看一段C代码&#xff1a; #include <stdio.h> #include <stdlib.h>int rand 0;int main() {printf(&quo…

6.2 统计量与抽样分布

学习目标&#xff1a; 我的理解: 统计量是从一个样本中计算得到的数值&#xff0c;用于描述样本的某种特征或性质。统计量可以用来推断总体的特征或性质&#xff0c;因为样本是总体的一部分。 通常&#xff0c;统计量是通过对样本中的数据进行计算得到的&#xff0c;例如平均…

3年轻人20万开店日营业额79.2元,年轻人开店还能赚到钱吗?

最近&#xff0c;河南郑州的蔡先生和2个朋友合伙开一家汉堡店&#xff0c;生意很惨淡&#xff0c;日营业额79.2元。 蔡先生表示&#xff0c;他们开店失败的最大原因就是没有做好前期准备工作&#xff0c;产品没有吸引人的特色&#xff0c;导致正式营业后吸引不到客户。 蔡先生用…

Python 实现海康机器人工业相机 MV-CU060-10GM 的实时显示视频流及拍照功能

一、背景介绍 1、最近项目中需要给客户对接海康机器人工业相机 MV-CU060-10GM&#xff1b; 2、客户要求通过部署的管理平台&#xff0c;可以在页面上实现如下功能&#xff1a; 1&#xff09;相机视频流开始预览&#xff1b; 2&#xff09;相机视频流停止预览&#xff1b; 3&am…

三月份跳槽了,历经阿里测开岗4轮面试,不出意外,被刷了...

大多数情况下&#xff0c;测试员的个人技能成长速度&#xff0c;远远大于公司规模或业务的成长速度。所以&#xff0c;跳槽成为了这个行业里最常见的一个词汇。 前几天&#xff0c;我看到有朋友留言说&#xff0c;他在面试阿里的测试开发工程师的时候&#xff0c;灵魂拷问三小…

NIO非阻塞式网络通信实例

一、概述 1、NIO有三大核心部分&#xff1a;Channel(通道)&#xff0c;Buufer(缓存区)&#xff0c;Selector(选择器) Buffer缓存区 缓冲区本质上是一块可以写入数据&#xff0c;然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象&#xff0c;并提供了 一组方法&am…

❤Linux文件、目录与磁盘格式总结❤

文章目录 Linux文件、目录与磁盘格式总结Linux文件权限chgrpchmodchown文件种类Linux文件扩展名 Linux文件与目录管理常用的目录解释目录树绝对路径与相对路径cd(切换目录)pwd(显示当前目录)mkdir(建立一个新目录)rmdir(删除空目录)执行文件路径变量$PATHls(文件与目录的查看)c…

青藤首提“业安融合”理念,正式发布先进云安全方案CNAPP

4月18日&#xff0c;以“云时代&#xff0c;安全变了”为主题的2023年云安全高峰论坛在北京举行。会上&#xff0c;青藤首次提出“业安融合”理念&#xff0c;正式发布先进云安全方案CNAPP。 中国全面进入云和数字化时代 当前&#xff0c;全球已进入数字经济时代&#xff0c;…

物联感知产品如何助力企业实现智能化转型?

如果您的企业想要实现设备全生命周期管理、设备信息管理、设备监控和数据处理等功能&#xff0c;想要进行智能化数字转型&#xff0c;不妨了解一下物联感知。 物联感知产品是利用物联网技术&#xff0c;实现对物理世界的感知、联接和智能化。物联感知产品一般由四个部分组成&a…

python中的pyc文件了解一下?

基本说明 pyc 文件是 Python 编译过的字节码文件。当你运行一个 Python 程序时&#xff0c;Python 解释器首先将源代码&#xff08;通常是 .py 文件&#xff09;编译成字节码。这个字节码是一种低级的、与平台无关的代码&#xff0c;它可以被 Python 虚拟机&#xff08;Python…

TCP的连接管理机制(三次握手与四次挥手)

目录为啥要三次握手与四次挥手三次握手syn 与 ack三次握手具体流程四次挥手注意为啥要三次握手与四次挥手 相比于UDP, TCP是有连接的, 这个连接就体现在这了. 三次握手就是TCP建立连接, 四次挥手就是TCP断开连接. 三次握手 握手是指通信双方进行网络交互. 三次握手就相当于…

记录上传文件异常 /tmp/tomcat... (No space left on device)

一&#xff0c;问题描述 用postman调用上传接口&#xff0c;基本每两次调用会有一次报错&#xff0c;如下 {"timestamp": "2023-04-11T03:00:15.4690000","status": 500,"error": "Internal Server Error","exceptio…

【活动】想对大学的自己说……

写在前面&#xff1a; 时间过得真的很快&#xff0c;眨眼间我们已经走过了这么多年的大学时光。回想起来&#xff0c;我们曾经有过无数的欢笑和泪水&#xff0c;有过无数的成功和挫败&#xff0c;但是这些经历都让我们变得更加坚强和成熟。如果现在有机会回到大学时光&#xff…