再散列和可扩散列

再散列和可扩散列,第1张

再散列:

当使用开放定址法时,所有的关键字都存在数组中,但是数组的大小是固定的,而数据量可能会逐渐增加,当数组中存放的关键字大于一半时,即λ>0.5时,对其进行插入、查找等 *** 作的耗时就会增加,这会降低程序的性能。那么这时候,我们建立一个新的大小是原表大小两倍(大小为素数)的散列表,将原表中的数据重新散列到新表中,并继续使用新表。但是由于再散列需要开辟新的空间,这是一个耗时的 *** 作,所以只有当再散列只是程序的一小部分时,才不会对程序的性能造成太大影响。当再散列应用在交互中时,新空间的开辟以及原数据的转移可能会使用户感到明显的延迟。

再散列有三种方式:

1.当装填因子0.5" src="https://latex.codecogs.com/gif.latex?%5Clambda%20%3E0.5" />时再散列

2.当一次插入失败时再散列

3.在超过指定的装填因子时再散列

在一次插入失败之前,表的装填因子就有可能过大,以至于前面的一些 *** 作已经变得非常低效,并且在之前的分析中,当时,散列表还有着很好的性能,所以使用方式1或是3是比较好的。

//散列:再散列
//再散列即当需要的时候,重新建立一个散列表,并将原来的散列表数据拷贝过来
#include 
#include 
#include 

#define empty -1

typedef struct hashlist {
	int size;
	int* val;
}hl;

int nextprime(int doublesize) {
	int flag = 1;
	for (int i = doublesize + 1;; i++) {
		flag = 1;
		for (int j = 2; j <= sqrt(i); j++) {
			if (i % j == 0) {
				flag = 0;
				break;
			}
		}
		if (flag == 1) {
			return i;
		}
	}
}

hl* CreatHashlist() {
	hl* p = (hl*)malloc(sizeof(hl));
	if (p == NULL) {
		printf("内存不足\n");
		return NULL;
	}
	int sizet = 10;
	p->size = sizet;
	p->val = (int*)malloc(sizeof(int) * p->size);
	for (int i = 0; i < p->size; i++) {
		p->val[i] = empty;
	}
	return p;
}

hl* rehash(hl* p) {//再散列,实际上就是重新建立一个二倍大小的散列表,并将数据拷贝过来,只是需要重新设置散列函数,在这里使用了相同的散列函数
	int oldsize = p->size;
	int* olddata = p->val;
	int newsize = nextprime(2 * oldsize);
	p->size = newsize;
	p->val = (int*)malloc(sizeof(int) * p->size);
	for (int i = 0; i < oldsize; i++) {
		p->val[i] = olddata[i];
	}
	for (int i = oldsize; i < newsize; i++) {
		p->val[i] = empty;
	}
	return p;
}

int hash(hl* p, int i) {//
	return i % p->size;
}

int hash1(hl* p, int i, int j) {
	return hash(p, i) + (j * (i+1)) % p->size;
}

void insert(hl* p, int x) {
	for (int j = 0;; j++) {
		int index = hash1(p, x, j);
		if (p->val[index] == empty) {
			p->val[index] = x;
			break;
		}
	}
}

int* find(hl* p,int x) {//找到x第一次出现的地址
	for (int j = 0;; j++) {
		int index = hash1(p, x, j);
		if (p->val[index] == empty || p->val[index] == x) {
			return p->val+index;
		}
	}
}

void delete(hl* p, int x) {
	//对于开放定址法,删除最好使用懒惰删除,只需要给要删除元素做一个记号
	//这只需要使用find找到该元素即可,正常的删除会使表中出现空位置,可能会导致find失败
}

int main() {
	hl* phl = CreatHashlist();
	for (int i = 0; i < 10; i++) {//插入0-10
		insert(phl, i);
	}

	phl = rehash(phl);

	for (int i = 43; i <= 53; i++) {
		insert(phl, i);
	}

	printf("%d\n", *find(phl, 45));

	return 0;
}
可扩散列:

在数据量过大以至于无法放入主存中时,数据就需要存储在外存中(硬盘等)。如果使用之前的方式来实现散列,假设一次find需要探测散列中的三个元素,那么当数据放入外存中时,一次find就需要访问3次硬盘,但访问硬盘是一个开销很大的 *** 作,这时候为了提高程序的性能,借鉴了B树的结构,定义了可扩散列。可扩散列允许使用两次硬盘访问来执行find *** 作。除叶子节点外的节点都用来存放索引信息,当目录较小时,可以直接放在主存中,那么只需要根据目录找到相应位置,只需要一次硬盘访问就能找到对应元素,当目录过大不能放入主存时,就需要将一部分目录放入外存中,这时候就需要两次访问来执行find。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/3002544.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存