C++哈希

在之前使用计数排序的时候,是通过对应的下标对该数组下标对应的空间进行标记,但是如果数分布不均匀就会造成浪费空间(比如1,2,3,999)。于是,哈希就来解决这问题。

1.闭散列哈希表

enum state
{
    EMPTY, DELETE, EXIST
};


template<class K, class KV,class Koft>
class HashTable
{
public:
    struct HashNode
    {
        state _state; 
        KV _kv;
    };

public:
    HashTable(size_t capacity = 4)
        : _ht(capacity), _size(0)
    {
        for (size_t i = 0; i < capacity; i++)
        {
            _ht[i]._state = EMPTY;  // 初始化每个位置为空
        }
    }

    void resize(size_t n)
    {
        // 映射关系变了
        // 1. 开辟空间
        // 2. 重新映射
        // 3. 释放空间
        Koft koft;
        int Index = 0;
            vector<HashNode> new_ht;
            new_ht.resize(_size * 2);

            for (int i = 0; i < _ht.size(); i++)
            {
                if (_ht[i]._state == EXIST)
                {
                    Index = koft(_ht[i]._kv) % new_ht.capacity();
                    while (new_ht[Index]._state == EXIST)
                    {
                        Index++;
                        if (Index == new_ht.size())
                        {
                            Index = 0;
                        }
                    }
                }
                new_ht[Index]._state = EXIST;
                new_ht[Index]._kv = _ht[i]._kv;
            }
            _ht.swap(new_ht);

    }

    bool Insert(const KV& kv)
    {     

        // 负载因子
        if (_size * 10 / _ht.capacity() > 7)
        {
            resize(_size*2);
        }


        Koft koft;
        K key = koft(kv);
        int Index = key % _ht.capacity();
        while (_ht[Index]._state==EXIST)      
        {         
            if (koft(_ht[Index]._kv) == koft(kv))
            {
                return false;
            }
            Index++;
            if (Index == _ht.size())
            {
                Index = 0;
            }
        }
        //  空或删除的节点
        _ht[Index]._kv = kv;
        _ht[Index]._state = EXIST;
        _size++;
        return true;
    }

    HashNode* Find(const K& key)
    {
        Koft koft;
        int Index = key % _ht.capacity();
        while (_ht[Index]._state != EMPTY)     // 为删除或存在的进入     如果是删除的状态还要继续往后找
        {
            if (koft(_ht[Index]._kv) == key)
            {
                if (_ht[Index]._state == EXIST)
                {
                    return &_ht[Index];
                }
                else if (_ht[Index]._state == DELETE)
                {
                    return nullptr;
                }
            }

            Index++;
            if (Index == _ht.size())
            {
                Index = 0;
            }
        }
        //  访问到空了,说明查找的元素不可能填在后面了
        return nullptr;
    }


    bool erase(const K& key)
    {
        HashNode* ret = Find(key);
        if (ret)
        {
            ret->_state = DELETE;
            _size--;
            return true;
        }
        else
        {
            return false;
        }
    }


private:
    vector<HashNode> _ht;
    size_t _size;    //   为EXIST的个数 ,        _ht.size() 表示所有是_state状态的空间的个数
};

主要写开散列哈希表

在开散列每一个vector的空间里存的是一个数据和一个next的指针,指向的是同样映射到该位置的数。

在开散列中不需要状态,因为映射到哪个点就放在哪个点。

	template<class KV>
	struct Node
	{
		KV _kv;
		Node* _next;
		Node(const KV& kv=KV())
			:_kv(kv)
			,_next(nullptr)
		{ }
	};

插入方式是进行头插,如上图

Index=key%capacity

			Node* newnode = new Node(kv);
			newnode->_next = _hash[Index];
			_hash[Index] = newnode;
		pair<iterator,bool> Insert(const KV& kv)
		{
			if (_num == _hash.size()) //  负载因子为1时扩容
			{
				resize(_num*2);
			}
			Koft koft;
			const K& key = koft(kv);
			int Index = HashFun(key) % _hash.capacity();
			// 查找是否已经存在
			Node* cur = _hash[Index];
			while (cur != nullptr)
			{
				if (koft(cur->_kv) == key)
				{
					return make_pair(iterator(cur), false);
				}
				cur = cur->_next;
			}
			//开始插入
			Node* newnode = new Node(kv);
			//头插
			newnode->_next = _hash[Index];
			_hash[Index] = newnode;
			_num++;
			return make_pair(iterator(newnode), true);
		}

并不是所有的key都是int 类型,如果是string类型,需要自己构造仿函数获取对应的int类型的值

	template<class K>
	class hash_t
	{
	public:
		const size_t& operator()(const K& key)
		{
			return key;
		}
	};


	template<>
	class hash_t<string>   // 模板特化,如果key是string类型,调用该模板
	{
	public:
		size_t operator()(const string& key)
		{
			int sum = 0;
			for (int i = 0; i < key.size(); i++)
			{
				sum += key[i];  // 用对应的ASCII码
			}
			return sum;
		}
	};
		size_t HashFun(const K& key)
		{
			Hash hash;
			return hash(key);
		}

在使用key的时候,用HashFun(key)就能获得size_t类型的值。

哈希表的框架

	template<class K, class KV, class Koft,class Hash=hash_t<K>> 
//  K是key的类型,KV是pair<K,V> ,V是value的类型, Koft是仿函数返回key,Hash是获取size_t类型数据 
	class OpenHash
	{
		typedef HashIterator<K,KV, Koft,Hash> iterator;
	public:
		friend class iterator;  //  为了让iterator访问_hash,确定下标
		typedef Node<KV> Node;

		OpenHash()
			:_hash(3)  // size()==3
			,_num(0)
		{ }


		// 在初始化和扩容都保证了size==capacity
		void resize(size_t n){}



		~OpenHash()
		{
			clear();
		}


		void clear()
		{
			for (int i = 0; i < _hash.size(); i++)
			{
				Node* next = nullptr;
				Node* cur = _hash[i];
				while (cur)
				{
					next = cur->_next;
					delete cur;
					cur = next;
				}
			}
		}

		size_t HashFun(const K& key){}

		iterator begin(){}

		iterator end(){}

		pair<iterator,bool> Insert(const KV& kv){}

		Node* Erase(const K& key){}

		Node* Find(const K& key){}


	private:
		vector<Node*> _hash;
		size_t _num;
	};
}

resize()函数

		void resize(size_t n)
		{
			vector<Node*> new_hash;
			new_hash.resize(n,nullptr);  // size()==n    //  用resize(),capacity()等于size()
			for (int i = 0; i < _hash.size(); i++)
			{
				Koft koft;
				Node* cur = _hash[i];
				while (cur != nullptr)  //要进行深拷贝,否则将原空间销毁,vector[i]的next指针就是野指针
				{
					const K& key = koft(cur->_kv);
					int Index = HashFun(key) % new_hash.capacity();
					Node* newNode = new Node(cur->_kv);
					newNode->_next = new_hash[Index];
					new_hash[Index] = newNode;			
					cur = cur->_next;
				}
			}
			_hash.swap(new_hash);
		}	

迭代器

主要是operator++(),从第一个不为空的节点开始,每++就访问next指针,当next==nullptr,就要

找下一个不为nullptr的vector[index],可以通过当前的key找到对应的下标,再对下标index++,找到vector[index]!=nullptr。所有在迭代器的实现时,需要我们将vector也传到迭代器类中,不如直接将哈希表指针this(现成的)传给迭代器类中。

迭代器,包装成一个_kv的地址(表面)。

	template<class K,class KV,class Koft,class Hash>
//  K是key的类型,KV是pair<K,V> ,V是value的类型, Koft是仿函数返回key,Hash是获取size_t类型数据
	class HashIterator
	{
		typedef HashIterator<K,KV, Koft,Hash> iterator;
		typedef OpenHash<K, KV, Koft,Hash> OH;
		typedef Node<KV> Node;
	public:
		HashIterator(Node* node = nullptr,OH* ptr=nullptr)  //  ptr哈希表指针
			:_node(node)
			,_ptr(ptr)
		{}

		KV& operator*()
		{
			return _node->_kv;
		}

		KV* operator->()
		{
			return &_node->_kv;   //  iterator it->first(表面使用)==_node->_kv.first(底层原理)
		}

		iterator operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
				return *this;
			}
			else
			{
				Koft koft;
				const K& key = koft(_node->_kv);
				int Index = _ptr->HashFun(key) % _ptr->_hash.size();
				Index++;
					for (; Index < _ptr->_hash.size(); Index++)
					{
						Node* cur = _ptr->_hash[Index];
						if (cur)
						{
							_node = cur;
							return *this;
						}
					}
					_node = nullptr;
					return *this;  
					//return iterator(nullptr, _ptr);
					//会进入死循环,因为_ptr指针变化了,永远不等于end(),为什么,因为这里返回的是一个临时拷贝,_ptr发生了浅拷贝,end()的ptr与临时的ptr不一样
					
			}
		}

		bool operator!=(const iterator& it)
		{
			return _node != it._node;
		}

// 只有访问到_hash.size()并且没找到非空数据,返回nullptr,与end()对应
		iterator begin()
		{
			for (int i = 0; i < _hash.size(); i++)
			{
				Node* cur = _hash[i];
				if (cur)
				{
					return iterator(cur,this);
				}
			}
			return end();
		}

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

Earse(), Find()

		Node* Erase(const K& key)
		{
			int Index = HashFun(key) % _hash.capacity();
			Node* cur = _hash[Index];
			Koft koft;
			Node* prev = nullptr;
			while (cur != nullptr)
			{
				if (koft(cur->_kv) == key)
				{
					if (cur == _hash[Index])
					{
						_hash[Index] = cur->_next;
						delete cur;
						return _hash[Index];
					}
					else
					{
						prev->_next = cur->_next;
						delete cur;
						return prev->_next;
					}
				}
				prev = cur;
				cur = cur->_next;
			}
			return nullptr;
		}

		Node* Find(const K& key)
		{
			Koft koft;
			int Index = HashFun(key) % _hash.capacity();
			Node* cur = _hash[Index];
			while (cur)
			{
				if (koft(cur->_kv) == key)
				{
					return cur;
				}
				cur = cur->_next;
			}
			return nullptr;
		}

map.h

template<class K,class V,class Hash=hash_t<K>>
class unordered_MAP
{
public:

	struct Koft
	{
	    const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};

	typedef typename OpenHash<K, pair<K, V>, Koft,Hash>::Node Node;
     //    因为Node也是OperHash从外面引进的,需要加typename
	typedef HashIterator<K, pair<K,V>, Koft, Hash> iterator;
	pair<iterator,bool> Insert(const pair<K, V>& kv)
	{
		return _map.Insert(kv);
	}


	Node* Erase(const K& key)
	{
		return _map.Erase(key);
	}

	Node* Find(const K& key)
	{
		return _map.Find(key);
	}

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

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

private:
	OpenHash<K, pair<K, V>, Koft,Hash> _map;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值