实用算法实践-第 4 篇 散列表

4.1    直接寻址表

当关键字的全域比较小时,直接寻址时一种简单而有效的技术。

4.1.1   实例

PKU JudgeOnline, 1200, Crazy Search.

4.1.2   问题描述

给定一个有NC个不同字符的字符串,求出其长度为N的子字符串的个数。例如字符串“daababac”有5个长度为3的子字符串:“daa”、“aab”、“aba”、“bab”、“bac”。

输入保证子字符串的个数不超过16百万个。

4.1.3   输入

34

daababac

4.1.4   输出

5

4.1.5   分析

“输入保证子字符串的个数不超过16百万个”,这个已知条件非常重要。它意味着可以通过。。。。。。。

4.1.6   程序


#include<stdio.h>
#include<string.h>
#define maxNum     16000010
bool hash[maxNum];
char str[maxNum];
int hashSearch(int key)
{
     return key;
}
int hashInsert(int key)
{
     int pos;
     pos = hashSearch(key);
     if(hash[pos]== 0)
     {
         hash[pos] = 1;
         return1;
     }
     return 0;
}
 
int main()
{
     int N;
     int NC;
     int length;
     int count;
     int key;
     int i, j;
     int f[250];
     int num;
     scanf("%d%d",&N,&NC);
    scanf("%s",str);
     memset(hash, 0, sizeof(hash));
     memset(f, 0xFF, sizeof(f));
     length = strlen(str);
     count = 0;
     num = 0;
     for(i = 0;i < length; i++)
     {
         if(f[str[i]]== -1)
         {
              f[str[i]] = num++;
         }
     }
     for(i = 0;i < length - N + 1; i++){
         key = 0;
         for(j =0; j < N; j++){
              key = key * NC + f[str[i + j]];
         }
         count += hashInsert(key);
     }
     printf("%d\n",count);
     return 0;
}

4.2    除法散列表

除法散列发的散列函数为:H(k)= k mod m。其中k为关键字,H(k)将关键字k映射到m个槽的某一个去。

m的选择十分有讲究。可以选作m的值常常是与2的整数幂不太接近的质数。

4.2.1   实例

PKU JudgeOnline, 3349, Snowflake SnowSnowflakes.

4.2.2   问题描述

每个雪花有六个边。如果两个雪花的六个边的长度都一样的话,则它们是一样的。给出一些雪花的六条边,判断其中是不是有两个一样的雪花。

注意:雪花的六条边可能顺时针地给出,也可能逆时针地给出。

4.2.3   输入

2

12 3 4 5 6

4 3 2 1 6 5

4.2.4   输出

Twin snowflakes found.

4.2.5   分析

首先,对与同样的一个雪花,由于存在旋转和镜像的变化,使得给出的六条边的排列不一样。对于给定的一个六条边的各种等效的排列,必须将它映射到同一个唯一的排列。

映射的方法是:将所有等效的排列对应到等效排列中字典序最小的那个排列。

具体的实现方法是:首先找到最短的边,然后判断顺时针和逆时针哪个排列小,取较小的那个。然后继续找到其他最短的边,如果比当前的局部最小排列还小,则修改局部最小排列。如此继续,直到遍历完所有最短的边,所得的局部最小排列就是全局最小的了。

通过上述方法,可以保证的是同一个雪花的任意一种描述都能对应到唯一的一个排列。接下来的做法是将这个排列对应为一个整型关键字。然后使用这个关键字映射到散列中去即可。

采用的具体方法是,将六条边的长度之和作为关键字,并使用除法散列函数。m值取为99991:一个是与2的整数幂不太接近的质数。

4.2.6   程序

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define maxNum     100002
#define MAX   0x7fffffff
bool hashHead[maxNum];
struct snowflake{
     snowflake *next;
     int used;
     int arm[6];
};
snowflakehash[maxNum];
int arm[6];
snowflake*hashSearch(int key)
{
     snowflake *pointer;
     snowflake *father;
     pointer = &hash[key];
     if((*pointer).used== 0){
         returnpointer;
     }
     while(pointer!= NULL){
         if(memcmp(&((*pointer).arm),arm, sizeof(int)* 6) == 0)
         {
              returnpointer;
         }
         father = pointer;
         pointer = (*pointer).next;
     }
     if(pointer== NULL)
     {
         pointer = (snowflake*)malloc(sizeof(snowflake));
         //memset(pointer,0, sizeof(snowflake));
         (*pointer).next = NULL;
         (*pointer).used = 0;
         (*father).next = pointer;
     }
     returnpointer;
}
int hashInsert(int key)
{
     snowflake *pointer;
     pointer = hashSearch(key);
     if((*pointer).used== 0)
     {
         (*pointer).used = 1;
         memcpy(&((*pointer).arm), arm, sizeof(int) * 6);
         return1;
     }
     return 0;
}
#define magicNumber 99991
#define calKey(sum)    sum %magicNumber
#define shift(i, mode) ((i + 6 + mode) % 6)
#define LEFT -1
#define RIGHT 1
struct sequence
{
     int pos;
     intdirection;
};
int temp[6];
void calMin(sequence *max, sequence *s)
{
     int i;
     int maxPos;
     int sPos;
     intmaxDirection;
     intsDirection;
     maxPos = (*max).pos;
     sPos = (*s).pos;
     maxDirection = (*max).direction;
     sDirection = (*s).direction;
     for(i = 0;i < 6; i++){
         if(temp[maxPos]< temp[sPos]){
              return;
         }else if(temp[sPos] < temp[maxPos]){
              (*max).pos = (*s).pos;
              (*max).direction = (*s).direction;
              //*max =*s;
              return;
         }
         maxPos = shift(maxPos, maxDirection);
         sPos = shift(sPos, sDirection);
     }
}
int main()
{
     int n;
     int i;
     int j;
     int min;
     int minPos;
     int pos;
     intdirection;
     int key;
     int sum;
     sequence s;
     sequence minS;
     memset(hash, 0,sizeof(snowflake));
     scanf("%d",&n);
     for(i = 0;i< n; i++)
     {
         min = MAX;
         for(j =0; j < 6; j++){
              scanf("%d",&temp[j]);
              if(min> temp[j])
              {
                   min = temp[j];
                   minPos = j;
              }
         }
         minS.direction = LEFT;
         minS.pos = minPos;
         s.direction = RIGHT;
         s.pos = minPos;
         calMin(&minS, &s);
         for(j =minPos + 1; j < 6; j++){
              if(temp[j]!= min)
              {
                   continue;
              }
              s.direction = LEFT;
              s.pos = j;
              calMin(&minS, &s);
              s.direction = RIGHT;
              s.pos = j;
              calMin(&minS, &s);
         }
         pos = minS.pos;
         direction =  minS.direction;
         sum = 0;
         for(j =0; j < 6; j++){
              arm[j] = temp[pos];
              sum += temp[pos];
              pos = shift(pos, direction);
         }
         key = calKey(sum);
         if(hashInsert(key)== 0)
         {
              cout << "Twin snowflakes found." <<endl;
              return1;
         }
     }
     cout << "Notwo snowflakes are alike." <<endl;
     return 0;
}
本文章欢迎转载,请保留原始博客链接http://blog.csdn.net/fsdev/article

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值