数据结构与算法_哈希表_线性探测法原理和代码实现

哈希线性探测法的原理:增加元素,查询元素和删除元素。

增加元素
通过哈希函数计算数据存放的位置。
如果该位置空闲,直接存储元素,完成;
如果该位置被占用,从当前位置向后找空闲的位置,存放该元素。
查询元素
通过哈希函数计算数据存放的位置,从该位置取值。
如果该值==要查询的元素值,找到了!
如果该值 != 要查询的元素值(之前往这个位置放元素时,发生哈希冲突了),继续往后遍历找该元素。
判断空的两种情况:
情况1:这个位置一直是空的,没放过元素,那么找到该后置后,就不需要继续往后找了。
情况2:这个位置是空的,以前放过元素,后来被删除了。这种情况下,在查找是要继续往后查找。
所以,表中的每个节点定义为一个结构体:

// 桶的状态  
enum State
{
	STATE_USING,	 // 从未使用过的桶
	STATE_UNUSED,	 // 正在使用的桶
	STATE_DEL        // 元素被删除了的桶 
};

// 桶的类型 
struct Bucket
{
	Bucket(int key = 0,State state= STATE_UNUSED)
		:key_(key)
		, state_ (state)
	{
		
	}
	int key_;     // 存放的元素 
	State state_; // 用的当前状态 
};

// 线性探测哈希表类型
class HashTable
{
public:
	HashTable(int size = primes_[0], double loadFactor_ = 0.75)
		:useBucketNum_(0)
		, loadFactor_(loadFactor_)
		, primeIdx_(0)
	{
		// 把用户传入的size调整到最近的比较大的素数上
		if (size != primes_[0])
		{
			for (; primeIdx_ < PRIME_SIZE; primeIdx_++)
			{
				if (primes_[primeIdx_] > size)
				{
					break;
				}
			}
			// 用户传入的size值过大,已经超过最后一个素数,调整到最后一个素数
			if (primeIdx_ == PRIME_SIZE)
			{
				primeIdx_--;  // 最后素数数组最后一个元素
			}
		}
		tableSize_ = primes_[primeIdx_];
		table_ = new Bucket[tableSize_];
	}

	~HashTable()
	{
		delete[] table_;
		table_ = nullptr;
	}
public:
	// 插入元素 
	bool insert(int key)
	{
		// 考虑扩容 
		double factor = useBucketNum_ * 1.0 / tableSize_;
		cout << "factor:" << factor << endl;
		if (factor > loadFactor_)
		{
			expand();
		}

		// 找到在哈希表中的索引
		int idx = key % tableSize_;  

		 判断
		//if (table_[idx].state_ != STATE_USING)
		//{
		//	table_[idx].state_ = STATE_USING;
		//	table_[idx].key_ = key;
		//	return true;
		//}
		//for (int i = (idx + 1) % tableSize_; i != idx; i=(i+1)%tableSize_)
		//{
		//	// 在遍历过程中,如果某个位置空闲,则插入 
		//	if (table_[i].state_ != STATE_USING)
		//	{
		//		table_[i].state_ = STATE_USING;
		//		table_[i].key_ = key;
		//		return true;
		//	}
		//}

		// 上面代码优化 
		int i = idx;
		do   // 先判断再循环时候,就用do while 
		{
			// 在遍历过程中,如果某个位置空闲,则直接插入 
			if (table_[i].state_ != STATE_USING)
			{
				table_[i].state_ = STATE_USING;
				table_[i].key_ = key;
				useBucketNum_++;
				return true;
			}
			// 否则,i 等于 i 加1,寻找下一个插入的节点。
			i = (i + 1) % tableSize_;

		} while ( i != idx);

		return false;
	}

	// 删除元素
	bool erase(int key)
	{
		int idx = key % tableSize_;

		int i = idx;
		do
		{
			if (table_[i].state_ == STATE_USING && table_[i].key_ == key)
			{
				table_[i].state_ = STATE_DEL;
				useBucketNum_--;
			}
			i = (i + 1) % tableSize_; 
		} while (table_[i].state_ != STATE_UNUSED && i != idx);
		// 删除时候结束条件:往后寻找,直到后边元素状态等于UNUSED截止,并且不等于原始值存放的。
		return true;
	}

	// 查询 
	bool find(int key)
	{
		int idx = key % tableSize_;
		int i = idx;
		do
		{
			if (table_[i].state_ == STATE_USING && table_[i].key_ == key)
			{
				return true;
			}
			i = (i + 1) % tableSize_; // 后移一个位置
		} while (table_[i].state_ != STATE_UNUSED && i != idx);

		return false;
		
	}
private:
	void expand()
	{
		primeIdx_++;
		if (primeIdx_ == PRIME_SIZE)
		{
			throw "HashTable is too large! can not expand!";
		}
		
		Bucket *newTable = new Bucket[primes_[primeIdx_]];
		
		// 扩容后需要重新哈希
		for (int i = 0; i < tableSize_; i++)
		{
			if (table_[i].state_ == STATE_USING)  
			{
				int idx = table_[i].key_ % primes_[primeIdx_];

				int k = idx;
				do
				{
					if (newTable[k].state_ != STATE_USING)
					{
						newTable[k].state_ = STATE_USING;
						newTable[k].key_ = table_[i].key_;
						break;
					}

					k = (k + 1) % primes_[primeIdx_];
				} while (k != idx);
			}
		}
	}
private:
	Bucket * table_;					 // 指向动态开辟的哈希表
	int tableSize_;						// 哈希表当前空间的长度 
	int useBucketNum_;					// 已经使用的桶的个数
	double loadFactor_;					// 哈希表的装载因子
	static const int PRIME_SIZE = 10;	// 素数表大小,C++11后,可在类外直接初始化
	static int primes_[PRIME_SIZE];		// 素数表
	int primeIdx_;						// 当前使用的 素数 下标
};
// 对数组中的静态类型初始化
int HashTable::primes_[PRIME_SIZE] = { 3, 7 , 23 , 47, 97, 251,443 ,911, 1471, 42773 };



int main()
{
	int data = 3;
	HashTable thashtable;
	thashtable.insert(11);
	thashtable.insert(32);
	thashtable.insert(12);
	thashtable.insert(43);

	cout << thashtable.find(32) << endl;
	thashtable.erase(32);
	cout << thashtable.find(32) << endl;
	system("pause");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值