20. C++使用HashTable同时出封装unordered_map和unordered_set

模板参数控制

unordered_set是K模型的容器,而unordered_map是KV模型的容器

  • 为了与原哈希表的模板参数进行区分,这里将哈希表的第二个模板参数的名字改为T。
template<class K, class T>
class HashTable
  • 如果使用的是unordered_set容器,那么传入哈希表的模板参数就是keykey
template<class K>
class unordered_set
{
public:
    //...
private:
    hash_bucket::HashTable<K, K> _ht; //传入底层哈希表的是K和K
};
  • 但如果使用的是unordered_map容器,那么传入哈希表的模板参数就是key以及keyvalue构成的键值对。
template<class K, class V>
class unordered_map
{
public:
    //...
private:
    HashTable<K, pair<K, V>> _ht; //传入底层哈希表的是K以及K和V构成的键值对
};

而哈希结点的模板参数也应该由原来的K、V变为T:

  • 上层容器是unordered_set时,传入的T是键值,哈希结点中存储的就是键值。
  • 上层容器是unordered_map时,传入的T是键值对,哈希结点中存储的就是键值对。

更改模板参数后,哈希结点的定义如下:

//哈希结点的定义
template<class T>
struct HashData
{
    HashData<T>* _next;
    T _data;

    // 构造
    HashData(const T& data)
        :_data(data)
        , _next(nullptr)
    {}
};

哈希表正向迭代器的实现

哈希表的正向迭代器实际上就是对哈希结点指针进行了封装,但是由于在实现++运算符重载时,可能需要在哈希表中去寻找下一个非空哈希桶,因此每一个正向迭代器中都应该存储哈希表的地址。

template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
struct __HTIterator
{
    typedef HashNode<T> Node; //哈希结点的类型
    typedef HashTable<K, T, KeyOfT, HashFunc> HT; //哈希表的类型
    typedef __HTIterator<K, T, KeyOfT, HashFunc> Self; //正向迭代器的类型

    Node* _node; //结点指针
    HT* _pht; //哈希表的地址
    // 构造
    __HTIterator(Node* node,HT* ht)
        :_node(node)
        ,_pht(ht)
    {}
};
接引用和->
T& operator*()
{
    //返回哈希结点中数据的引用
    return _node->_data;
}
T* 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->_next)
    {
        // 当前桶还有节点,走到下一个节点
        _node = _node->_next;
    }
    else
    {
        // 当前桶已经走完了,找下一个桶开始
        KeyOfT kot;
        Hash hs;
        size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
        ++hashi;
        while (hashi < _ht->_tables.size())
        {
            if (_ht->_tables[hashi]) // 如果桶存在
            {
                // 找到了
                _node = _ht->_tables[hashi];
                break;
            }
            else
            {
                // 继续往后找
                ++hashi;
            }
        }

        // 走完了
        if (hashi == _ht->_tables.size())
            _node = nullptr;
    }
    return *this;
}

HashTable.h

#pragma once
#include <vector>
#include <iostream>

static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
    53,         97,         193,       389,       769,
    1543,       3079,       6151,      12289,     24593,
    49157,      98317,      196613,    393241,    786433,
    1572869,    3145739,    6291469,   12582917,  25165843,
    50331653,   100663319,  201326611, 402653189, 805306457,
    1610612741, 3221225473, 4294967291
};

inline unsigned long __stl_next_prime(unsigned long n)
{
    const unsigned long* first = __stl_prime_list;
    const unsigned long* last = __stl_prime_list + __stl_num_primes;
    const unsigned long* pos = std::lower_bound(first, last, n);
    return pos == last ? *(last - 1) : *pos;
}


template<class K>
struct HashFunc
{
    size_t operator()(const K& key)
    {
        return (size_t)key;
    }
};

// 特化
template<>
struct HashFunc<std::string>
{
    size_t operator()(const std::string& key)
    {
        size_t hashi = 0;
        for (auto& e : key)
        {
            hashi *= 131;
            hashi += e;
        }
        return hashi;
    }
};

namespace hash_bucket
{
    template<class T>
    struct HashNode
    {
        T _data;
        HashNode<T>* _next;

        // 构造
        HashNode(const T& data)
            :_data(data)
            ,_next(nullptr)
        {}
    };

    // 需要前置声明一下HashTable
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable;

    template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash = HashFunc<T>>
    struct __HTIterator
    {

        typedef HashNode<T> Node; //哈希结点的类型
        typedef HashTable<K, T, KeyOfT, Hash> HT; // 哈希表的类型
        typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self; //正向迭代器的类型

        Node* _node; //结点指针
        const HT* _ht; //哈希表的地址 // 需要是const

        // 构造
        __HTIterator(Node* node, const HT* ht) // 注意参数
            :_node(node)
            ,_ht(ht)
        {}

        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->_next)
            {
                // 当前桶还有节点,走到下一个节点
                _node = _node->_next;
            }
            else
            {
                // 当前桶已经走完了,找下一个桶开始
                KeyOfT kot;
                Hash hs;
                size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
                ++hashi;
                while (hashi < _ht->_tables.size())
                {
                    if (_ht->_tables[hashi]) // 如果桶存在
                    {
                        // 找到了
                        _node = _ht->_tables[hashi];
                        break;
                    }
                    else
                    {
                        // 继续往后找
                        ++hashi;
                    }
                }

                // 走完了
                if (hashi == _ht->_tables.size())
                    _node = nullptr;
            }
            return *this;
        }
    };


    // 仿函数不能在这里写缺省参数,必须要在上一层加
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable
    {
        KeyOfT kot; // 用于取值
        Hash hs;    // 用于计算
        typedef HashNode<T> Node;
        ////////////////////////
        // 须要进行友元声明__HTIterator中的_tables才能访问,并且声明不能有缺省参数
        template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
        friend struct __HTIterator;
        ///////////////////////////
    public:
        typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
        typedef __HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;
        ////////////////////////////////////////////////////
        // 迭代器
        Iterator Begin()
        {
            // 找第一个不为空的桶里面的第一个节点
            for (size_t i = 0; i < _tables.size(); i++)
            {
                if (_tables[i])
                    return Iterator(_tables[i], this);
            }
            return End();
        }

        Iterator End()
        {
            return Iterator(nullptr, this);
        } 

        ConstIterator Begin() const
        {
            // 找第一个不为空的桶里面的第一个节点
            for (size_t i = 0; i < _tables.size(); i++)
            {
                if (_tables[i])
                    return ConstIterator(_tables[i], this);
            }
            return End();
        }

        ConstIterator End() const 
        {
            return ConstIterator(nullptr, this);
        }

        ////////////////////////////////////////////////////
        HashTable(size_t size = __stl_next_prime(0))
            : _tables(size, nullptr)
            , _n(0)
        {}

        ~HashTable()
        {
            for (size_t i = 0; i < _tables.size(); i++)
            {
                Node* cur = _tables[i];
                while (cur)
                {
                    Node* next = cur->_next;
                    delete cur;
                    cur = next;
                }
                _tables[i] = nullptr;
            }
        }

        std::pair<Iterator, bool>Insert(const T& data)
        {
            // 1、查看哈希表中是否存在该键值的键值对
            Iterator it = Find(kot(data));
            if (it != End())
                return { it , false };

            // 2、判断是否需要调整哈希表的大小
            if (_n == _tables.size())
            {
                // a. 建立新表
                std::vector<Node*> newTables(__stl_next_prime(_tables.size() + 1));

                // b、将原哈希表当中的结点插入到新哈希表
                for (size_t i = 0; i < _tables.size(); i++)
                {
                    Node* cur = _tables[i];
                    while (cur)
                    {
                        // 旧表的节点挪动下来插入到映射的新表位置
                        Node* next = cur->_next;   //记录cur的下一个结点
                        size_t hashi = hs(kot(cur->_data)) % newTables.size(); // 通过哈希函数计算出对应的哈希桶编号index

                        cur->_next = newTables[hashi]; // 节点直接拿下来放到新桶中
                        newTables[hashi] = cur; // 将该结点头插到新哈希表中编号为index的哈希桶中
                        cur = next; // 取原哈希表中该桶的下一个结点
                    }
                    _tables[i] = nullptr;  // 该桶取完后将该桶置空
                }
                _tables.swap(newTables);
            } // 扩容end...

            size_t hashi = hs(kot(data)) % _tables.size();
            Node* newnode = new Node(data);
            // 进行头插
            newnode->_next = _tables[hashi];
            _tables[hashi] = newnode;

            ++_n;
            return { {newnode, this}, true };
        }


        Iterator Find(const K& key)
        {
            if (_tables.size() == 0) // 哈希表大小为0,查找失败
                return End();
            // 通过哈希函数计算出对应的哈希桶编号
            size_t hashi = hs(key) % _tables.size();
            Node* cur = _tables[hashi];
            // 遍历哈希桶
            while (cur)
            {
                if (kot(cur->_data) == key)
                    return Iterator(cur, nullptr);
                cur = cur->_next;
            }
            return End();
        }

        bool Erase(const K& key)
        {
            //1、通过哈希函数计算出对应的哈希桶编号index(除数不能是capacity)
            size_t hashi = hs(kot(key)) % _tables.size();

            //2、在编号为index的哈希桶中寻找待删除结点
            Node* cur = _tables[hashi];
            Node* prev = nullptr;
            while (cur)
            {
                //3、若找到了待删除结点,则删除该结点
                if (kot(cur->_data) == key)
                {
                    //待删除结点是哈希桶中的第一个结点
                    if (prev == nullptr)
                        _tables[hashi] = cur->_next;// 将第一个结点从该哈希桶中移除
                    else// 待删除结点不是哈希桶的第一个结点
                        prev->_next = cur->_next; // 前一个节点的next指向cur的next
                    delete cur;
                    --_n; // 4、删除结点后,将哈希表中的有效元素个数减一
                    return true;
                }
                // 继续往后找
                prev = cur;
                cur = cur->_next;
            }
            return false;
        }

    private:
        std::vector<Node*> _tables;
        size_t _n;
    };
}

MyUnorderedSet.h

#pragma once

#include "HashTable.h"

namespace lsl
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		// 用第一个也可以
		//typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::ConstIterator iterator;

		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::ConstIterator const_iterator;

		// 使用这个也可以
		/*
		using iterator = typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Iterator;
		using const_iterator = typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::ConstIterator;
		*/

		iterator begin()
		{
			return _ht.Begin();
		}

		iterator end()
		{
			return _ht.End();
		} 

		const_iterator begin() const
		{
			return _ht.Begin();
		}

		const_iterator end() const
		{
			return _ht.End();
		}

		std::pair<iterator, bool> insert(const K& key)
		{
			return _ht.Insert(key);
		}

	private:
		hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
	};

    /////////////////测试/////////////
	// const迭代器
	void print(const unordered_set<int>& s)
	{
		unordered_set<int>::const_iterator it = s.begin();
		while (it != s.end())
		{
			//*it = 1;
			std::cout << *it << " ";
			++it;
		}
		std::cout << std::endl;
	}

	void unordered_set_Test()
	{
		unordered_set<int> s;
		s.insert(1);
		s.insert(5);
		s.insert(10);
		s.insert(508);
		s.insert(333);
		s.insert(82);

		for (auto& e : s)
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;

		print(s);

		// set不可以修改
		unordered_set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			//*it = 1;
			std::cout << *it << " ";
			++it;
		}
		std::cout << std::endl;
	}
}

MyUnorderedMap.h

#pragma once

#include "HashTable.h"

namespace lsl
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const std::pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, std::pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, std::pair<const K, V>, MapKeyOfT, Hash>::ConstIterator const_iterator;

		// 使用这个也可以
		/*
		using iterator = typename hash_bucket::HashTable<K, std::pair<const K, V>, MapKeyOfT, Hash>::Iterator;
		using const_iterator = typename hash_bucket::HashTable<K, std::pair<const K, V>, MapKeyOfT, Hash>::ConstIterator;
		*/ 

		iterator begin()
		{
			return _ht.Begin();
		}

		iterator end()
		{
			return _ht.End();
		}

		const_iterator begin() const
		{
			return _ht.Begin();
		}

		const_iterator end() const
		{
			return _ht.End();
		}

		std::pair<iterator, bool> insert(const std::pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

		V& operator[](const K& key)
		{
			std::pair<iterator, bool> ret = _ht.Insert({ key, V() });
			return ret.first->second;
		}

	private:
		hash_bucket::HashTable<K, std::pair<const K, V>, MapKeyOfT, Hash> _ht;
	};

	/////////////////测试/////////////
	// const迭代器
	void print(const unordered_map<std::string, std::string>& s)
	{
		unordered_map<std::string, std::string>::const_iterator it = s.begin();
		while (it != s.end())
		{
			//*it = 1;
			std::cout << it->first << " " << it->second << std::endl;
			++it;
		}
		std::cout << std::endl;
	}

	void unordered_map_Test()
	{
		unordered_map<std::string, std::string> s;
		s.insert({ "string", "字符串" });
		s.insert({ "left", "左" });

		unordered_map<std::string, std::string>::iterator it = s.begin();
		while (it != s.end())
		{
			std::cout << it->first << ":" << it->second << std::endl;
			++it;
		}

		print(s);

		s["sort"];
		s["left"] = "左边+剩余";
		s["right"] = "右边";

		// pair中的first不能被修改
		unordered_map<std::string, std::string>::iterator it2 = s.begin();
		while (it2 != s.end())
		{
			//it2->first += 'x';
			//it2->second += 'x';
			std::cout << it2->first << ":" << it2->second << std::endl;
			++it2;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小林子AND

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值