【C++】map的模拟实现

简介: C++中的`map`是STL中的一种关联容器,存储键值对且键唯一。`map`基于红黑树实现,自动按键排序,支持动态调整、复杂数据类型、丰富的成员函数及双向迭代器。插入、查找等操作保证了对数时间复杂度,适用于需要快速查找和有序存储的场景。

前言:

C++中的map是标准模板库(STL)中的一种关联容器,它存储的是键值对(key-value pairs),其中每个键都是唯一的。

map的特点

  • 值唯一性:map中的每个键只能出现一次,如果尝试插入具有相同键的新元素,则会覆盖原有键对应的值。
  • 自动排序:map中的元素是根据键值自动排序的,默认情况下是按照键的升序排序。
  • 动态调整:插入和删除操作会自动调整内部结构以保持有序性和平衡性。
  • 支持复杂数据类型:map的键和值可以是任意数据类型,包括自定义类型。
  • 迭代器支持:map提供双向迭代器,可以遍历容器中的所有元素。
  • 成员函数丰富:map提供了一系列成员函数,如insert、erase、find、count、begin、end等,用于执行各种操作。
  • 空间和性能开销:由于使用红黑树,map可能会有较大的空间开销,并且相比于哈希表(如unordered_map),插入和删除操作的时间复杂度为对数级别,可能在某些高性能要求的应用中不如哈希表高效

map的实现

map的存储结构

  • 由于set和map是公用一棵树,set是K 、map是KV的结构,为了适应map的结构,set做出一定的改变
  • 内部类是为了取到map内所存储的数据
  • 在set的map中 set所存储的是key而map是pair。这里取到的值是first。
template<class K, class V>
    class map
    {
   
        struct mapofT
        {
   
            const K& operator()(const pair<K, V>& kv)
            {
   
                return kv.first;
            }
        };
        public:
        private:
        RBTree<K, pair<const K, V>, mapofT> _t;
    };

map的插入

插入实现的基本步骤

  • 查找插入位置**:map内部使用红黑树来维护元素的顺序,因此插入操作首先需要在树中找到正确的位置。这通常涉及到比较键值并沿着树进行遍历。

  • 创建节点:一旦找到了插入位置,就会创建一个新的节点来存储要插入的键值对。

  • 插入节点:新创建的节点会被插入到红黑树中的正确位置,这可能涉及到树的旋转和重新着色操作,以保持红黑树的平衡性质。

  • 返回插入结果:insert函数返回一个pair,其中第一个元素是一个迭代器,指向新插入的元素(如果键不存在),或者指向已存在键的元素;第二个元素是一个布尔值,指示插入是否成功(即键是否不存在)。

pair<iterator,bool> insert(const T& data)
{
   
    if (_root == nullptr)
    {
   
        _root = new Node(data);
        _root->_col = BLACK;
        return make_pair(iterator(_root),true);
    }
    Node* parent = nullptr;
    Node* cur = _root;

    keyofT kot;

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

    cur = new Node(data);
    Node* newnode = cur;
    cur->_col = RED;

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

    while (parent && parent->_col == RED)
    {
   
        Node* grandparent = parent->_parent;
        if (parent == grandparent->_left)
        {
   
            Node* uncle = grandparent->_right;
            if (uncle && uncle->_col == RED)
            {
   
                parent->_col = uncle->_col = BLACK;
                grandparent->_col = RED;
                cur = grandparent;
                parent = cur->_parent;
            }
            else//uncle is not or black
            {
   
                if (cur == parent->_left)
                {
   
                    RotateR(grandparent);
                    grandparent->_col = RED;
                    parent->_col = BLACK;
                }
                else
                {
   
                    RotateL(parent);
                    RotateR(grandparent);
                    grandparent->_col = RED;
                    cur->_col = BLACK;
                }
                break;
            }
        }
        else//parent == grandparent->_right
        {
   
            Node* uncle = grandparent->_left;

            if (uncle && uncle->_col == RED)
            {
   
                grandparent->_col = RED;
                parent->_col = uncle->_col = BLACK;
                cur = grandparent;
                parent = cur->_parent;
            }
            else
            {
   
                if (cur == parent->_right)
                {
   
                    RotateL(grandparent);
                    parent->_col = BLACK;
                    grandparent->_col = RED;
                }
                else
                {
   
                    RotateR(parent);
                    RotateL(grandparent);
                    cur->_col = BLACK;
                    grandparent->_col = RED;
                }
            }
            break;
        }
    }
    _root->_col = BLACK;
    return make_pair(iterator(newnode),true );
}

map的查找

  1. 调用find成员函数,传入要查找的键作为参数。
  2. find函数在内部遍历红黑树,使用键值进行比较,寻找匹配的元素。
  3. 如果找到了匹配的键,find返回一个指向该元素的迭代器。
  4. 如果没有找到匹配的键,find返回end()迭代器。
  5. 红黑树的查找类似于,AVL的查找。本质上是一样的。
Node* find(const T& data)
{
   
    keyofT kot;
    Node* cur = _root;
    while (cur)
    {
   
        if (kot(data) > kot(cur->_data))
        {
   
            cur = cur->_right;
        }
        else if (kot(data) < kot(cur->_data))
        {
   
            cur = cur->_left;
        }
        else
        {
   
            return cur;
        }
    }

    return nullptr;
}

map重载[]

  • STL中也是调用insert。
  • 传入K返回V的形式。
V& operator[] (const K& key)
{
   
    pair<iterator, bool> ret = insert(make_pair(key,V()));

    return ret.first->second;
}

map的迭代器

C++中的map是一个关联容器,它存储键值对(key-value pairs),并且根据键(key)来快速检索值(value)。map的迭代器是一种指针,指向map中的元素。map的迭代器有两种类型:const_iterator和iterator。const_iterator用于只读访问,而iterator可以读写。

迭代器的特性

  • map的迭代器遵循双向迭代器的要求,可以向前和向后遍历。
  • 迭代器在map被修改时(例如插入或删除操作)可能会失效。
  • map的迭代器可以通过解引用操作符(*)来访问指向的元素,或者使用箭头操作符(->)来访问元素的成员。

  • set迭代器类似于list的迭代器

迭代器的封装

  • 主要区别在于++--
  • Ptr是T*,Ref是T&,设置这么多的模板参数是为适配出const的迭代器
template<class T,class Ptr,class Ref>
struct TreeIterator
{
   
    typedef RBTreeNode<T> Node;
    typedef TreeIterator<T,Ptr,Ref> self;
    typedef TreeIterator<T, T*, T&> Iterator;
    Node* _node;

    TreeIterator(Node* node)
        :_node(node)
    {
   }
    TreeIterator(const Iterator& it)
        :_node(it._node)
    {
   }

    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;
    }

};

前置++

  • 前置++ 的本质上就是中序的遍历,左子树-根-右子树
  • 如果右子树存在就去右子树的最左节点
  • _node 不是右节点那么,证明子树的左右节点均访问完成,需要访问祖父的节点
self& operator++()
{
   
    if (_node->_right)
    {
   
        Node* cur = _node->_right;
        while (cur->_left)
        {
   
            cur = cur->_left;
        }
        _node = cur;
    }
    else
    {
   
        Node* cur = _node;
        Node* parent = _node->_parent;
        while (parent)
        {
   
            if (cur == parent->_left)
            {
   
                break;
            }
            else
            {
   
                cur = cur->_parent;
                parent = parent->_parent;
            }

        }

        _node = parent;
    }
    return *this;
}

前置--

  • 前置-- 是前置++的镜像的一个顺序的访问,右子树-根-左子树
  • 方法是类似于前置++
self& operator--()
{
   
    if (_node->_left)
    {
   
        Node* cur = _node->_right;
        while (cur->_left)
        {
   
            cur = cur->_right;
        }
        _node = cur;
    }
    else
    {
   
        Node* cur = _node;
        Node* parent = _node->_parent;
        while (parent && parent->_left)
        {
   
            cur = cur->_parent;
            parent = parent->_parent;
        }

        _node = parent;
    }
    return *this;
}

迭代器

C++中的map是一种关联容器,它存储键值对(key-value pairs),并且按照键的顺序自动排序。map的迭代器用于遍历容器中的元素。map提供了多种迭代器成员函数,包括:

  • begin():返回指向map中第一个元素的迭代器。
  • end():返回指向map容器末尾位置的迭代器,这个位置不包含任何元素,用作遍历结束的标志。
  • rbegin():返回指向map最后一个元素的反向迭代器。
  • rend():返回指向map第一个元素的反向迭代器。
  • map的迭代器是const_iterator类型的,这意味着它们提供对元素的只读访问。如果需要修改元素,可以使用iterator类型
  • map的迭代器提供了向前和向后遍历的能力,但不支持随机访问迭代器的所有操作,如算术运算

  • 前面是将迭代器封装,因为正常的++或者--已不可以支持自定义类型的加减

  • 在红黑树的类中实现,在实现set时候只需要调用,在这里面可以认为红黑树是容器的适配器
typedef TreeIterator<T, T*, T&> iterator;

typedef TreeIterator<T,const T*,const T&> const_iterator;

iterator begin()
{
   
    Node* leftmin = _root;
    while (leftmin->_left)
    {
   
        leftmin = leftmin->_left;
    }
    return iterator(leftmin);
}

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

const_iterator begin()const 
{
   
    Node* leftmin = _root;
    while (leftmin->_left)
    {
   
        leftmin = leftmin->_left;
    }
    return const_iterator(leftmin);
}

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

源码

map

#pragma once

#include"RBTree.h"
#include<iostream>
using namespace std;

namespace Map
{
   
    template<class K, class V>
    class map
    {
   
        struct mapofT
        {
   
            const K& operator()(const pair<K, V>& kv)
            {
   
                return kv.first;
            }
        };
    public:


        typedef typename RBTree<K, pair<const K, V>, mapofT>::iterator iterator;
        typedef typename RBTree<K, pair<const K, V>, mapofT>::const_iterator const_iterator;

        V& operator[] (const K& key)
        {
   
            pair<iterator, bool> ret = insert(make_pair(key,V()));

            return ret.first->second;
        }

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

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

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

        const_iterator end()const
        {
   
            return _t.end();
        }
    private:
        RBTree<K, pair<const K, V>, mapofT> _t;
    };
}

红黑树

#pragma once

#include<iostream>
#include<assert.h>
#include<utility>

using namespace std;

    enum Colour
    {
   
        RED,
        BLACK
    };
    template< class T>
    class RBTreeNode
    {
   
    public:
        RBTreeNode<T>* _left;
        RBTreeNode<T>* _right;
        RBTreeNode<T>* _parent;
        T _data;
        Colour _col;

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

    template<class T,class Ptr,class Ref>
    struct TreeIterator
    {
   
        typedef RBTreeNode<T> Node;
        typedef TreeIterator<T,Ptr,Ref> self;
        typedef TreeIterator<T, T*, T&> Iterator;
        Node* _node;

        TreeIterator(Node* node)
            :_node(node)
        {
   }
        TreeIterator(const Iterator& it)
            :_node(it._node)
        {
   }

        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--()
        {
   
            if (_node->_left)
            {
   
                Node* cur = _node->_right;
                while (cur->_left)
                {
   
                    cur = cur->_right;
                }
                _node = cur;
            }
            else
            {
   
                Node* cur = _node;
                Node* parent = _node->_parent;
                while (parent && parent->_left)
                {
   
                    cur = cur->_parent;
                    parent = parent->_parent;
                }

                _node = parent;
            }
            return *this;
        }

        self& operator++()
        {
   
            if (_node->_right)
            {
   
                Node* cur = _node->_right;
                while (cur->_left)
                {
   
                    cur = cur->_left;
                }
                _node = cur;
            }
            else
            {
   
                Node* cur = _node;
                Node* parent = _node->_parent;
                while (parent)
                {
   
                    if (cur == parent->_left)
                    {
   
                        break;
                    }
                    else
                    {
   
                        cur = 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;

        iterator begin()
        {
   
            Node* leftmin = _root;
            while (leftmin->_left)
            {
   
                leftmin = leftmin->_left;
            }
            return iterator(leftmin);
        }

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

        const_iterator begin()const 
        {
   
            Node* leftmin = _root;
            while (leftmin->_left)
            {
   
                leftmin = leftmin->_left;
            }
            return const_iterator(leftmin);
        }

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


        Node* find(const T& data)
        {
   
            keyofT kot;
            Node* cur = _root;
            while (cur)
            {
   
                if (kot(data) > kot(cur->_data))
                {
   
                    cur = cur->_right;
                }
                else if (kot(data) < kot(cur->_data))
                {
   
                    cur = cur->_left;
                }
                else
                {
   
                    return cur;
                }
            }

            return nullptr;
        }

        pair<iterator,bool> insert(const T& data)
        {
   
            if (_root == nullptr)
            {
   
                _root = new Node(data);
                _root->_col = BLACK;
                return make_pair(iterator(_root),true);
            }
            Node* parent = nullptr;
            Node* cur = _root;

            keyofT kot;

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

            cur = new Node(data);
            Node* newnode = cur;
            cur->_col = RED;

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

            while (parent && parent->_col == RED)
            {
   
                Node* grandparent = parent->_parent;
                if (parent == grandparent->_left)
                {
   
                    Node* uncle = grandparent->_right;
                    if (uncle && uncle->_col == RED)
                    {
   
                        parent->_col = uncle->_col = BLACK;
                        grandparent->_col = RED;
                        cur = grandparent;
                        parent = cur->_parent;
                    }
                    else//uncle is not or black
                    {
   
                        if (cur == parent->_left)
                        {
   
                            RotateR(grandparent);
                            grandparent->_col = RED;
                            parent->_col = BLACK;
                        }
                        else
                        {
   
                            RotateL(parent);
                            RotateR(grandparent);
                            grandparent->_col = RED;
                            cur->_col = BLACK;
                        }
                        break;
                    }
                }
                else//parent == grandparent->_right
                {
   
                    Node* uncle = grandparent->_left;

                    if (uncle && uncle->_col == RED)
                    {
   
                        grandparent->_col = RED;
                        parent->_col = uncle->_col = BLACK;
                        cur = grandparent;
                        parent = cur->_parent;
                    }
                    else
                    {
   
                        if (cur == parent->_right)
                        {
   
                            RotateL(grandparent);
                            parent->_col = BLACK;
                            grandparent->_col = RED;
                        }
                        else
                        {
   
                            RotateR(parent);
                            RotateL(grandparent);
                            cur->_col = BLACK;
                            grandparent->_col = RED;
                        }
                    }
                    break;
                }
            }
            _root->_col = BLACK;
            return make_pair(iterator(newnode),true );
        }

        void RotateR(Node* parent)
        {
   
            Node* cur = parent->_left;
            Node* curright = cur->_right;

            parent->_left = curright;

            if (curright)
            {
   
                curright->_parent = parent;
            }

            cur->_right = parent;

            Node* ppnode = parent->_parent;

            parent->_parent = cur;

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

                cur->_parent = ppnode;
            }

        }
        void RotateL(Node* parent)
        {
   

            Node* cur = parent->_right;

            Node* curleft = cur->_left;

            parent->_right = curleft;

            if (curleft)
            {
   
                curleft->_parent = parent;
            }

            cur->_left = parent;

            Node* ppnode = parent->_parent;

            parent->_parent = cur;

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

                cur->_parent = ppnode;
            }
        }


        bool is_balance()
        {
   
            return is_balance(_root);
        }

        bool Checknum(Node* root, int blacknum, int stdnum)
        {
   
            if (root == nullptr)
            {
   
                if (blacknum != stdnum)
                {
   
                    return false;
                }
                return true;
            }

            if (root->_col == BLACK)
            {
   
                ++blacknum;
            }

            return Checknum(root->_left, blacknum, stdnum)
                && Checknum(root->_right, blacknum, stdnum);
        }
        bool is_balance(Node* root)
        {
   
            if (root == nullptr)
            {
   
                return true;
            }

            if (root->_col == RED)
            {
   
                return false;
            }
            int stdnum = 0;
            Node* cur = root;
            while (cur)
            {
   
                if (cur->_col == BLACK)
                {
   
                    ++stdnum;
                }
                cur = cur->_left;
            }

            return Checknum(root, 0, stdnum);
        }

    private:
        Node* _root = nullptr;
    };
目录
相关文章
|
1月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
30 3
【C++】map、set基本用法
|
1月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
22 5
|
5月前
|
C++ 容器
【C++】map和set封装
【C++】map和set封装
44 2
|
5月前
|
存储 C++ 容器
【C++】map和set深度讲解(下)
【C++】map和set深度讲解(下)
69 2
|
5月前
|
存储 自然语言处理 Java
【C++】map和set深度讲解(上)
【C++】map和set深度讲解(上)
54 2
|
5月前
|
存储 算法 C++
C++一分钟之-扁平化映射与unordered_map
【7月更文挑战第5天】C++的STL `unordered_map`是键值对的快速查找容器,基于哈希表。常见问题包括哈希函数选择、键类型限制、内存管理和迭代顺序不确定性。要避免问题,需优化哈希函数,确保自定义类型支持哈希和比较操作,合理管理内存,不依赖迭代顺序。提供的代码示例展示了如何为自定义类型定义哈希函数并操作`unordered_map`。正确使用能提升代码效率。
60 0
C++一分钟之-扁平化映射与unordered_map
|
5月前
|
存储 C++ 索引
|
5月前
|
存储 C++
C++的list-map链表与映射表
```markdown C++ 中的`list`和`map`提供链表和映射表功能。`list`是双向链表,支持头尾插入删除(`push_front/push_back/pop_front/pop_back`),迭代器遍历及任意位置插入删除。`map`是键值对集合,自动按键排序,支持直接通过键来添加、修改和删除元素。两者均能使用范围for循环遍历,`map`的`count`函数用于统计键值出现次数。 ```
|
6月前
|
存储 自然语言处理 C++
【C++航海王:追寻罗杰的编程之路】set|map|multiset|multimap简单介绍
【C++航海王:追寻罗杰的编程之路】set|map|multiset|multimap简单介绍
48 0
【C++航海王:追寻罗杰的编程之路】set|map|multiset|multimap简单介绍
|
5月前
|
存储 C++ 容器
【C++】开散列实现unordered_map与unordered_set的封装
【C++】开散列实现unordered_map与unordered_set的封装
61 0