http://hi.baidu.com/%C2%F8%CD%B7%CA%C7%B0%FC%D7%D3/blog/item/e14e59d6105c552307088b92.html
怎么快速查找我要的资料呢
Description
招生考试,初试结束后,校方会贴出一张大榜单,上面是通过初试获得复试资格的考生名单。考生和家长一大早都来看榜,但是一面墙那么大的名单,要看很久才能看完。怎么能快一些呢?一般来说,名单按姓氏笔画或拼音字母排序,是一张有序表,这就让我们想到了基于有序表的二分查找算法 binarysearch, 而我们学计算机的知道还有一种叫做 哈希 的方法 hash。
这个问题就是要求 Acmer 实现在名单中快速查找某一姓名的算法。
Input
输入数据分为名单和询问两部分。
首先是名单部分,第一行是一个正整数 n (n<=500000), 表示名单中的人数。下面 n 行, 每一行有一个由大写字母A-Z和小写字母a-z组成的字符串,代表名单中的姓名。
然后是询问部分,第一行是一个正整数 m (m<=10000), 表示询问的次数。下面 m 行, 每一行有一个由大写字母A-Z和小写字母a-z组成的字符串,代表要查询的姓名。
输入中的每个字符串长度不超过10。
Output
对于每一次查询,如果要查询的字符串出现在名单中输出 "YES", 否则输出 "NO"。(注意不要加引号)
每一次查询占一行。
Sample Input
5
abc
edfg
x
a
Mike
3
Mike
bc
EDFG
Sample Output
YES
NO
NO
Hint
二分查找,hash 只是作为提示,并不要求一定要用它们来解题。
如果使用 map, set 等等, 很有可能超时,对于 Java 也是如此。
建议使用 scanf(), printf(), 有助于提高效率,对于 Java 任选输入方式。
注意,区分大小写,而且可能有相同的名字。
----------------------------------------------------------------------------------------------------------------------------------
上面是BOJ的1003题,最简单的方法是调用C下库函数 qsort 和 bsearch (stdlib.h),即首先对所有字符串快速排序,然后二分查找。提交后,Memory:6616K Time:831MS。这么写很逆天。
但既然题目点到了hash,对咱种初学者来说最好还是动动手实现下比较好。下面就贴出用哈希表解决这道题的代码:
#include#include #include #include #define HashTableSize 650000 typedef struct Node HashNode; struct Node { char * str; HashNode * next; }; //ELF Hash Function unsigned int ELFHash(const char * str) { unsigned int hash = 0; unsigned int x = 0; while (*str) { hash = (hash << 4) + (*str++); if ((x = hash & 0xF0000000L ) != 0) { hash ^= ( x >> 24); hash &= ~x; } } return hash&0x7FFFFFFF; } void HashInsert(char * str, HashNode ** HashTable) { int key; HashNode * tempA, * tempB; key = ELFHash(str) % HashTableSize; if (HashTable[key] == NULL) { HashTable[key] = (HashNode *)malloc(sizeof(HashNode)); HashTable[key]->str = str; HashTable[key]->next = NULL; return; } tempA = HashTable[key]; while (tempA != NULL) { if (!strcmp(tempA->str, str)) return; tempB = tempA; tempA = tempA->next; } tempA = (HashNode *)malloc(sizeof(HashNode)); tempA->str = str; tempA->next = NULL; tempB->next = tempA; return; } HashNode* HashSearch(const char * str, HashNode ** HashTable) { int key = ELFHash(str) % HashTableSize; HashNode * temp; if (HashTable == NULL || HashTable[key] == NULL) return NULL; temp = HashTable[key]; while (temp != NULL) { if (!strcmp(temp->str, str)) return temp; temp = temp->next; } return NULL; } int HashDelete(char * str, HashNode ** HashTable) { HashNode * tempA, *tempB = NULL; int key = ELFHash(str) % HashTableSize; if (HashTable == NULL) return 0; tempA = HashTable[key]; if (strcmp(tempA->str, str) == 0) { HashTable[key] = tempA->next; free(tempA); return 1; } tempB = tempA->next; while (tempB->next) { if (strcmp(tempB->str, str) == 0) { tempA->next = tempB->next; free(tempB); return 1; } tempA = tempA->next; } return 0; } void HashDestory(HashNode *** HashTable) { int i; HashNode * temp; for (i = 0;i < HashTableSize;i++) { while ((*HashTable)[i] != NULL) { temp = (*HashTable)[i]; (*HashTable)[i] = (*HashTable)[i]->next; free(temp); } } free(*HashTable); *HashTable = NULL; } HashNode ** HashInit(int size) { HashNode ** HashTable = (HashNode **)calloc(size, sizeof(HashNode *)); return HashTable; } void HashDisplay(HashNode ** HashTable) { int i; HashNode * temp; if (HashTable) { for (i = 0;i < HashTableSize;i++) { printf("%d: ", i); temp = HashTable[i]; while (temp) { printf("%s", temp->str); if (temp = temp->next) printf("-->"); } printf("/n"); } } } void main() { char str[500000][11]; char cmp[11]; HashNode ** HashTable = NULL; int m, n, i; HashTable = HashInit(HashTableSize); scanf("%d", &n); for (i = 0;i < n;i++) { scanf("%s", str[i]); HashInsert(str[i], HashTable); } scanf("%d", &m); for (i = 0;i < m;i++) { scanf("%s", cmp); if (HashSearch(cmp, HashTable)) printf("YES/n"); else printf("NO/n"); } HashDestory(&HashTable); }
提交后:Memory:9612K Time:375MS
采用的是链地址法处理冲突,散列函数为ELFHash。
Hash表属于空间换时间的一种方法,明显可以看到时间减少了一半多。不过上面的程序空间上应该还可以再优化。