哈希表之拉链法

前段时间理解了一下所谓的哈希表,一直以来在小博印象中哈希表是深奥的,是高大上的,但是接触原理以及看了一份demo之后我就觉得哈希表也就那样吧,接下来我把小博自己的理解尽量用最直白的语句来解释下~~~

---------------------------------------------------------我是分界线,没错,很丑------------------------------------------------------------------

首先什么是哈希表???

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

以上是一段在百度百科中的解释,如果还是不能理解,那么我就抽象的比喻一下。

先看(是根据关键码值(Key value)而直接进行访问的数据结构),这里的关键码值以及数据结构可以用具体的物体代替一下,这里小博将关键码值用学号代替,数据结构用学生代替,学生与学号都是唯一的,可以根据学号直接找到学生。(说了一堆废话~~~~接下去才是重点)。

场景演示一下,当我们需要将数据存入哈希表中的时候,就好像是一堆学生在办理入学,那么这时候有个地中海的大肚子男老师过来了,他对每个上前办理的学生说:你是XXX号,你所在的班级是YYY班,记住咯。然后这名学生就屁颠屁颠的去YYY班了。那么怎么查找呢??想必在学校中被校长点名过的同学应该都想到了,比如XXX号犯错了,他所在的班级是YYY一班,那么校长会说,在YYY一班的XXX号,你出来一下咱们好好喝喝茶~~这样校长就不用为了找XXX号同学而找遍每一个学生了,相对来说就是提升了查找效率。这是哈希表其中的一个特性————查找方便

还有一个比较重要的特性,小博暂时没想到怎么怎么形象的比喻(原谅小博的见识浅薄~~囧)。另一个特性是空间高利用率,各位大老爷可能有几个一下子不能理解这个,请容小博详细说明,都知道需要存数据是需要容器的,数组,结构体等都是容器的一种,那么只要是容器都需要各自的空间(其实不仅仅是容器需要,其他也是一样的,但是相比之下其他占用的比较少罢了,继续正文),比如数组,当申请的时候需要一块连续的空间,而数据结构也是如此,申请的时候需要一块整个数据结构大小的空间,而且每次申请空间的时候,系统内部所为你划分的空间并不是一块挨着一块申请的,因此肯定会有少部分空间无法申请导致浪费,采用数据结构可以灵活的利用这些空间,采用链表将每个结构体联系起来,那么就形成了一个最简单的哈希表。直接上代码理解一下吧~~~~(写这些的时候小博已经神游,断片了,原谅小博的才疏学浅。)

#include <stdio.h>
#include <string>
#include <time.h>

typedef struct ITEM 
{
	int val;
	int index;
	struct ITEM *next;
}Item;

typedef struct LIST
{
	Item *head;
	Item *end;
}List;

#define HASH_VAL 10

void insertItem(List* list, Item* item)
{
	int key = (unsigned int)item->val%HASH_VAL;
	if (!list[key].head)
	{
		list[key].head = item;
		list[key].end = item;
	}
	else
	{
		list[key].end->next = item;
		list[key].end = item;
	}
}

void showList(List* list)
{
	Item* p = NULL;
	for (int i = 0; i < 10; i++)
	{
		printf("%d:", i);
		p = list[i].head;
		if (p)
		{
			do 
			{
				printf("%d(下标:%d)  ", p->val, p->index);
				if (p->next)
					p = p->next;
				else
					break;
			} while (1);
		}
		printf("\n");
	}
}

bool aaa(int* a, int val)
{
	for (int i = 0; i < 10; i++)
	{
		if (a[i] == val)
			return true;
	}
	return false;
}

bool serchItem(List* list, int val)
{
	int key = (unsigned int)val%HASH_VAL;
	Item* p = list[key].head;

	while (p)
	{
		if (p->val == val)
			return true;
		p = p->next;
	}
	return false;
}

int main(int argc, char* argv[])
{
	List list[10];
	Item item[10];
	memset(list, 0, sizeof(List)* 10);
	memset(item, 0, sizeof(Item)* 10);
	int a[10] = {21,11,1,51,5,6,7,8,9,0};

	for (int i = 0; i < 10; i++)
	{
		item[i].index = i;
		item[i].val = a[i];
		insertItem(list, &item[i]);
	}
	showList(list);
	system("pause");
	return 0;
}

  小博演示的拉链法是将数组的特点(方便查找,不易删除)以及链表的特点(方便删除,不易查找)取长补短的一种折中方法。

----------------------------------------------分界线,又出现了-----------------------------------------------------------------

文中好像很多废话,希望没有把各位大老爷给绕晕了,新人小白发表,不足之处请见谅,欢迎大牛指导,其他同道也可交流

 

如果不理解小博的思路,可以参考更详细的原理:http://www.cnblogs.com/tuhooo/p/7092288.html#3934840

posted @ 2018-04-02 10:15  半路出家当和尚  阅读(394)  评论(0编辑  收藏  举报