标签:key 键值 最好 adt size 提前 span 别人 case
其实昨晚就已经写得差不多了(也算是没有食言啦),但有几个细节一直没处理好,所以,弄到了今天。
...因为看别人的拉链法实现的散列表代码的时候,一直没看到他有用到链表,很懵逼,可能是没仔细看吧...我以为是我理解错了,然后...我的代码确确实实的让我认为我的是正确的。
这篇主要是基础的数据结构学习,之前看书看了半天懵懵懂懂,然后网上查资料了解,也还是没怎么明白,于是决定还是自己按这些资料的理解亲自写一下,果然什么事都还是要自己“躬行”啊,写的时候就明白了书上说到的一些问题,也明白了散列表这种数据结构的性质,由于该篇仅仅只是对这种数据结构进行一个理解,所以很基础,关于h(x)函数也只是简单的运用了除法散列,然后为了应对冲突,我用的是链接法。
下面说说散列表和我对散列表的理解:
1.什么是散列表?
散列表就是一个关键值映射的地址组成的表,这里的地址是数组的下标...至于映射,学过数学就应该要知道这个概念并不难理解,就是自己设计一个优秀的映射(函数),一个好的映射可以很优秀的避免空间的浪费和冲突的发生,由于我比较菜233,自己设计不出并且也暂时没时间研究一个这样的函数,所以,我用的就是简单的关键值取余,写成数学函数就是 h(key) = key mod N(其中key为关键值,N为散列表的大小),显然,可以看到这个函数设计得并不好,很多关键值都可能被映射到同一个位置上去,然后就发生了冲突,并且还有一些表的空间可能一直都没有值映射到,就造成了空间的浪费。但这也是一个方案,至少让我们有了踏出第一步的勇气去探索更好的方法,或者说还能让初学者更快的、更明了的理解什么是散列表和冲突,基本上这就是什么是散列表了。
2.什么是链接法?
上面多次提到了冲突(也叫碰撞,collision),按照我们的映射的设计,很大可能会出现不同的两个关键值映射到了同一个地址上去,这样的行为就被称为冲突或者碰撞,所以,为了保证数据都可以保存在表中,而且不会被覆盖、丢失等,解决这个问题的一个方法就是链接法(也称拉链法,Chaining),即,将散列到同一个槽(地址)的关键值用链表的方法保存,这样说可能还是想像不出是什么样的情形,那我更细节一点说就是,把所有具有相同地址的数据用链表来保存,并把该链表的头节点保存到散列表中的对应的地址中,这样就可以避免冲突,而且分好了同类,只要输入的关键值被映射到这个地址,那么就遍历这个地址中的链表,添加到尾部(我认为链表可以换成栈或者队列,然后就实现了一个其他的数据结构,比如散列栈、散列队列233...不过不知道用处?),从这里我们也就可以知道,查找、删除和添加的最坏时间复杂度为O(N)了,N是散列表的大小,这种情况就是所有的数据全被散列到同一个地址上了...然后就是一个链表...
好了,说得也差不多了,我再说一下昨晚一直烦恼的几个小问题,最大的问题就是开始写的时候,我的链表是没有头结点的...然后,大家知道没有头结点进行删除是有很大问题的,会删除的是删除对象的后一个节点,其实添加一个头结点就问题都解决了,但...我又感觉添加一个头结点改着麻烦,由于我太菜也没能实现其他的解决办法,最后受不了了,睡觉。。。今天早上还是默默的添加了头结点,问题解决。。。另一个问题就是查找...这个我感觉怎么说呢,也不一定算问题...但又是个让我强迫症犯的问题,问题就是:假如我插入了几个具有相同地址的数据,也就是这个地址有了链表,然后我又删除其中一个以后,再查找删除的这个数据就会导致程序崩溃...因为,查找用的也是相同的散列函数,这样的话就因为这个位置保存的是地址相同的链表,删除一个,这里的链表还是存在,但没有了这个数据,我再查找删除的这个数据时,首先被映射到了这个链表头,然后会在里面找...结果已经被删除了,是不可能找得到的,但偏偏又要返回一个值...这个问题其实要解决也不难,把返回值改一下就好了,但我不想那么做...那么可能就需要再写个判断存在的函数,也感觉麻烦,于是就一直拖着了...然后就想着算了,这问题不解决也罢...嘛,之后再说吧,我有心情了再解决吧 ( ﹁ ﹁ ) ~→~~,还有个问题就是我本来想写一个输出散列表中的所有数据,结果发现自己太菜,接受不了一个地址一个地址的检查是否为NIL,但也没有其他好办法,嫌麻烦(233,什么都解释为嫌麻烦...实际上是不会吧!!!),于是改成了输出某个地址的所有数据。。。可能还有其他地方也有些问题...暂时没发现,主要是测试得并不多~
下面给出我的实现方法...实现方法很多,我的只是个基础例子:
/** * ADT - Hash table(散列表) * * 链接法(chaining) */ #include <stdio.h> #include <stdlib.h> #include <conio.h> #define bool int #define true 1 #define false 0 #define ListSize 10 typedef struct LinkedList { struct LinkedList* next; //当发生冲突时申请额外节点来存放冲突值 int key; //key-value int count; //list个数 }HashChain; //链接法 struct HashList { HashChain* Node; }hashlist[ListSize]; //建立hashlist-LiseSize /* 定义全部函数 */ HashChain* initHashtable(); bool ListEmpty(); bool ListFull(); int GetListCount(); static int Hashmap(); static bool HashCollision(); bool InsertList(); int FindListKey(); bool DeleteList(); /** * 初始化hashlist的每一个位置的节点与个数 */ HashChain* initHashtable(void) { HashChain* table; int i; for(i = 0; i < ListSize; i++) { hashlist[i].Node = NULL; } table = (HashChain*)malloc(sizeof(HashChain)); table->count = 0; table->next = NULL; return table; } /** * 判断hash表是否为空 */ bool ListEmpty(HashChain* table) { return table->count == 0; } /** * 判断hash表是否满 */ bool ListFull(HashChain* table) { return table->count == ListSize; } /** * 当前散列表中存在的数据个数 */ int GetListCount(HashChain* table) { return table->count; } /** * 哈希函数-映射,将value映射为hash table中的一个地址 */ static int Hashmap(int val) { return val % ListSize; } /** * 判断映射地址是否发生了冲突 */ static bool HashCollision(int val) { if(hashlist[Hashmap(val)].Node != NULL ) //检查val的映射地址的第一个节点是否存在,存在则说明冲突。时间复杂度O(1) return false; return true; } /** * 插入数据 */ bool InsertList(HashChain* table, int X) { HashChain* list, * temp; if(ListFull(table)) //如果list满了 { fprintf(stderr, "HashList is full!\n"); return false; //提前退出 } else if(HashCollision(X)) //如果没冲突 { hashlist[Hashmap(X)].Node = (HashChain*)malloc(sizeof(HashChain)); //对该地址申请一个节点,把节点保存到list中 temp = (HashChain*)malloc(sizeof(HashChain)); temp->key = X; temp->next = NULL; hashlist[Hashmap(X)].Node->next = temp; } else if(!HashCollision(X)) //如果冲突 { list = hashlist[Hashmap(X)].Node; //为了防止改变冲突的原地址的节点,把地址赋给一个hash结构变量,让它来完成查找插入的位置的任务 while(list->next != NULL) //链表的线性查找,使得新插入的值插入到该地址中的链表的尾部 { list = list->next; } temp = (HashChain*)malloc(sizeof(HashChain)); temp->key = X; list -> next = temp; temp -> next = NULL; } ++table->count; //计数 return true; } /** * 查找数据 */ int FindListKey(HashChain* table, int X) { HashChain* list; if(ListEmpty(table)) //如果表为空,节省一点查找的时间 { fprintf(stderr, "HashList is empty!\n"); return false; } else if(HashCollision(X)) //如果没有冲突,说明表中不存在该数据 { fprintf(stderr, "Not the data exist!\n"); return false; } else //如果冲突,则从该地址中的链表的第一个节点开始比较查找,查找的时间复杂度为O(n),n为链表的节点个数,最坏情况为O(ListSize),最好情况为O(1) { list = hashlist[Hashmap(X)].Node; while(list->next->key != X) { list = list->next; } return list->next->key; } } /** * 删除数据 */ bool DeleteList(HashChain* table, int X) { HashChain* list, * temp; if(ListEmpty(table)) //如果表为空,节省一点查找的时间 { fprintf(stderr, "HashList is empty!\n"); return false; } else if(HashCollision(X)) //如果没有冲突,说明表中不存在该数据 { fprintf(stderr, "Not the data exist!\n"); return false; } else if(!HashCollision(X)) { list = hashlist[Hashmap(X)].Node; if(list->next == NULL) { temp = list; free(temp); } else { while(list->next != NULL && list->next->key != X) { temp = list; list = list->next; } temp = list->next; list->next = list->next->next; free(temp); } temp = NULL; //防止野指针 } --table->count; return true; } int main(void) { HashChain* table, * PrintKey; int val; char c; table = initHashtable(); puts("1) 添加 2) 删除"); puts("3) 查找 4) 关键值个数"); puts("5) 显示指定地址中的所有关键值"); puts("6) 退出"); while((c = getch()) != ‘6‘) { switch(c) { case ‘1‘ : printf("\n添加数据:"); scanf("%d", &val); InsertList(table, val); break; case ‘2‘ : printf("\n删除数据:"); scanf("%d", &val); DeleteList(table, val); break; case ‘3‘ : printf("\n查找数据:"); scanf("%d", &val); if(FindListKey(table, val)) printf("您查找的数据为:%d\n", FindListKey(table, val)); break; case ‘4‘ : printf("\n当前关键值个数为: %d\n", GetListCount(table)); break; case ‘5‘ : printf("输入关键值:"); scanf("%d", &val); printf("显示数据:"); PrintKey = hashlist[Hashmap(val)].Node; while(PrintKey->next != NULL) { PrintKey = PrintKey->next; printf("%d ", PrintKey->key); } printf("\n"); break; } } free(table); table = NULL; //防止野指针 return 0; }
等自己学到更多姿势了,把其他解决冲突的办法和更好的映射方法再补充上。。。
最后,自己感谢一下自己的学习和谢谢观看~~
标签:key 键值 最好 adt size 提前 span 别人 case
原文地址:http://www.cnblogs.com/darkchii/p/7640557.html