数据结构笔记(六)——散列(Hash Table)之开放定址法(3)

前一篇讲了分离链接法,它的实现简单易懂,但是分离链接法需要指针,需要实现链表,给新单元分配地址也需要时间,因此速度有所下降。这一节的开放定址法不用链表来解决冲突,而是当遇到冲突时,尝试选择其他位置,直到找到一个空位置。h_i(X)=(hash(X)+F(i)) mod TableSize,且F(0)=0.函数F为冲突解决方法。可以看到,数据将会被全部放入表中(不会有一个额外的链表用来存储数据),装填因子λ应该低一点(<0.5),这样出现冲突的可能会稍低一点。

对于F(i)的选择有很多种,比如F(i)=i就是线性探测法,F(i)=i^2就是平方探测法。

线性探测法就是冲突函数为线性的探测方法。通过计算公式h_i(X)=(hash(X)+i) mod TableSize可以看出,我们从hash函数计算的位置处开始逐个遍历查看是否有空位置,只要还有空位置,我们就可以找到放置X的地方,但当表差不多满了的时候,找空位置的代价就是N了。甚至当表相对比较空的时候,占据的单元也会形成一个区块(称为一次聚集),我们想要找到下一个空位置就先要跳过(查找过)这些聚集的地方。可以证明,不成功的查找和成功的查找需要的探测次数分别为1/2(1+1/(1-λ)^2)和1/2(1+1/(1-λ)),分析表明,如果表有多于一半被填满的话,探测的次数就太多了。λ=0.5时,插入的平均探测次数为2.5,成功的插入需要1.5次,λ=0.75时平均就需要8.5次,所以在用线性探测法时,应使λ<0.5.

                    线性探测法,hash(x)=X mod 10,F(i)=i,依次插入89,18,49,58,69

平方探测法是冲突函数为二次函数的探测方法。同样,当表中被填充的位置超过一半时,其性能也会迅速下降,由于平方探测是以1,4,9,16为间隔探测空位置的,有时即使表中有空位,我们却无法找到也无法插入某个新元素。但是当表中至少一半为空,且表的大小为素数时,我们可以保证总能插入一个新元素。哪怕比表的一半多一个的位置被填满,我们就无法保证可以成功插入了。

                 平方探测法,hash(x)=X mod 10,F(i)=i*i,依次插入89,18,49,58,69

另外,我们想删除某个元素也不是很容易了,如果我们删除89,那么当我们查找49的时候,第一个探测的位置是9,这个位置为空,程序将会返回未查找到这个结果,所以我们需要懒惰删除,额外添加一个标记,标记89为删除了的而不删掉它,这样可以使各个程序正常运行。

代码:

HashAdr.h

#pragma once
#include<iostream>
#define MINSIZE 5
using namespace std;
typedef int ElementType;
typedef unsigned int Index;
typedef Index Position;
struct HashAdr;
typedef HashAdr *HashTb;
HashTb initialize(int size);
void insert(HashTb ht, ElementType e);
Position find(HashTb ht, ElementType e);
void deleteX(HashTb ht, ElementType e);
void destory(HashTb ht);

enum kind{Legitimate,Empty,Deleted};
struct Cell 
{
	ElementType element;
	enum kind info;
};
struct HashAdr 
{
	Cell* table;
	int table_size;
};
int nextPrime(int x);
int hashFunc(int x, int table_size);

HashAdr.cpp

#include "stdafx.h"
#include "HashAdr.h"
HashTb initialize(int size)
{
	if (size<MINSIZE)
	{
		cerr << "table size is too small";
		return nullptr;
	}
	HashTb ht = (HashTb)malloc (sizeof(HashAdr));
	if (ht==nullptr)
	{
		cerr << "out of space";
		return nullptr;
	}
	ht->table_size = nextPrime(size);
	ht->table = (Cell*)malloc(sizeof(Cell)*ht->table_size);
	if (ht->table==nullptr)
	{
		free(ht);
		cerr << "out of space";
		return nullptr;
	}
	for (int i=0;i<ht->table_size;++i)
	{
		ht->table[i].info = Empty;
	}
	return ht;
}
void insert(HashTb ht, ElementType e)
{
	Position pos = find(ht, e);
	if (ht->table[pos].info!=Legitimate)
	{
		ht->table[pos].element = e;
		ht->table[pos].info = Legitimate;
	}
}
Position find(HashTb ht, ElementType e)
{
	Position pos = hashFunc(e,ht->table_size);
	int i = 0;
	while (ht->table[pos].info != Empty&&ht->table[pos].element!=e)//判断为空在前
	{
		pos += (2 * ++i - 1);//f(i)-f(i-1)=2i-1
		if (pos>=ht->table_size)
		{
			pos -= ht->table_size;
		}
	}
	return pos;
}
void deleteX(HashTb ht, ElementType e)
{
	Position pos = find(ht, e);
	if (ht->table[pos].info==Legitimate)
	{
		ht->table[pos].info = Deleted;
	}
}
void destory(HashTb ht)
{
	if (ht!=nullptr)
	{
		if (ht->table != nullptr)
		{
			free(ht->table);
		}
		free(ht);
	}
	

}
int nextPrime(int x)
{
	if (x % 2 == 0)
	{
		x++;//变成奇数
	}
	for (;; x += 2)//偶数不是素数
	{
		bool flag = true;
		for (int i = 3; i*i <= x; i += 2)
		{
			if (x%i == 0)
			{
				flag = false;
				break;
			}
		}
		if (flag)
		{
			return x;
		}
	}
	return 0;
}
int hashFunc(int x, int table_size)
{
	return x%table_size;
}

test.cpp

// HashTable.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "HashAdr.h"
#include<vld.h>
int main()
{
	HashTb ht = initialize(26);
	int input;
	cout << "input: " << endl;
	cin >> input;
	while (input!=-1)
	{
		insert(ht, input);
		cin >> input;
	}
	cout << "table size:" << ht->table_size << endl;
	if (find(ht,33))
	{
		cout << "yes" << endl;
		deleteX(ht, 33);
	}
	else
	{
		cout << "no" << endl;
	}
	destory(ht);
    return 0;
}

结果:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
散列开放定址法是一种解决哈希冲突的方法,它的时间复杂度和空间复杂度如下: 时间复杂度: - 最好情况下,哈希表中没有冲突,查找、插入和删除操作的时间复杂度都是O(1)。 - 最坏情况下,哈希表中所有的关键字都映射到了同一个地址上,查找、插入和删除操作的时间复杂度都是O(n)。 - 平均情况下,查找、插入和删除操作的时间复杂度都是O(1)。 空间复杂度: - 散列开放定址法的空间复杂度取决于哈希表的大小和存储的元素个数,即O(m+n),其中m为哈希表的大小,n为存储的元素个数。 下面是一个使用散列开放定址法解决哈希冲突的Python代码示例: ```python class HashTable: def __init__(self, size): self.size = size self.keys = [None] * self.size self.values = [None] * self.size def put(self, key, value): hash_value = self.hash_function(key) if self.keys[hash_value] is None: self.keys[hash_value] = key self.values[hash_value] = value else: if self.keys[hash_value] == key: self.values[hash_value] = value else: next_slot = self.rehash(hash_value) while self.keys[next_slot] is not None and self.keys[next_slot] != key: next_slot = self.rehash(next_slot) if self.keys[next_slot] is None: self.keys[next_slot] = key self.values[next_slot] = value else: self.values[next_slot] = value def get(self, key): start_slot = self.hash_function(key) value = None stop = False found = False position = start_slot while self.keys[position] is not None and not found and not stop: if self.keys[position] == key: found = True value = self.values[position] else: position = self.rehash(position) if position == start_slot: stop = True return value def hash_function(self, key): return key % self.size def rehash(self, old_hash): return (old_hash + 1) % self.size ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值