C语言写一个散列表

目录
  • 一、快速理解散列表
  • 二、散列函数
  • 三、防撞

一、快速理解散列表

散列表,就是下标可以为字母的数组。

假设现有一个数组int a[100],想查找其中第40个元素,则直接输入a[40]就可以了,时间复杂度为O ( 1 ) O(1)O(1)。

问题在于,当下标不是数字,而是一个字符串的时候,可能需要一个超大的空间才能将所有下标妥善地存放在特定的位置。例如,若以大小写字母作为下标索引,那么一位就需要预留52个空间,10位就需要52的10次方 这么大的空间,根本没有设备可以满足。

好在,52的10次方这么庞大的数字也超出了正常人的使用范围,无论多长的索引,我们能用上的值也绝对是有限的。

例如,现有下面三个字符串作为下标

key1 = "microcold";
key2 = "tinycold";
key3 = "microcool";

其实只需要选取头、尾两个字母,就能很好地区分这三个字符串,即

def hash(key):
    return key[0]+key[-1]

但这种算法对索引字符的要求非常高,至少头尾不能重复。所以,现在需要能把超长字符串映射成特定短字符串而且尽量避免重复的算法。

二、散列函数

最简单的散列函数就是求余,将输入字符串按位转为整数之后求余。由于在字符串可能会转成非常大的整数,故需了解余数的性质

(a+b)%c=(a%c+b %c)% c

相应地有:

(a*b)%c=((a%c)*(b %c))% c

用C语言实现如下:

#include <stdio.h>
#define MAXHASH 100

//快速取幂法,a*b^n%c
int  PowerMod (int a, int b, int n, int c) 
{  
    int  ans = 1; 
    b = b % c; 
    while (n > 0) {  
        if(n % 2 == 1) 
            ans = (ans * b) % c; 
        n = n / 2;       //b >>= 1;
        b = (b * b) % c; 
    } 
    return (a*ans)%c; 
} 

int hash(char* key, int n){
    int addr = 0;
    for(int i = 0; i < n; i++){
        addr += PowerMod(key[i], 128, i, MAXHASH);
    }
    return addr%MAXHASH;
}

int main(){
    char* str;
    int i;
    while(1){
        gets(str);
        i = 0;
        while(str[i++]!='\0'){}
        printf("%d\n",hash(str,i));
    }
    return 0;
}

测试如下:

>gcc hash.c
>a.exe
asdf
21
microcold
81
tinycold
12
microcool
5
minicool
81
minicold
73

三、防撞

尽管minicool和microcold撞车了,但通过100以内的位数,去表示52的9次方 的样本,也算是不错的表现了。

为了不发生撞车,则需更改数组中的元素类型——至少得是个结构体。而防止撞车的方法很简单,如果发生撞车,那我就不散列了,直接发配到一个指定的数组中。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXHASH 100
typedef struct HASHNODE{
    char *key;
    int next;
} *hashNode;

struct HASHNODE* hashTable[MAXHASH];
struct HASHNODE* crashTable[MAXHASH];     //存储撞击之后的值
int numCrash=0;                   //已有的撞击值

void initTable(){
    for(int i=0; i < MAXHASH; i++){
        hashTable[i] = (hashNode)malloc(sizeof(struct HASHNODE));
        hashTable[i]->key = NULL;
        hashTable[i]->next = -1;
        crashTable[i] = (hashNode)malloc(sizeof(struct HASHNODE));
        crashTable[i]->key = NULL;
        hashTable[i]->next = -1;
    }
}

void insertCrash(char* str, int index, int n){
    if(index == numCrash){
        crashTable[numCrash]->key = (char*)malloc(sizeof(char)*n);
        strcpy(crashTable[numCrash++]->key, str);  //此时新增一个节点
    }
    else {
        if(crashTable[index]->next==-1)
            crashTable[index]->next = numCrash;
        insertCrash(str, hashTable[index]->next, n);
    }
}

//n为字符串长度
void insertHash(char* str, int index,int n){
    if(hashTable[index]->key==NULL){
        hashTable[index]->key = (char*)malloc(sizeof(char)*n);
        strcpy(hashTable[index]->key, str);
    }else{
        if(hashTable[index]->next==-1)
            hashTable[index]->next = numCrash;
        insertCrash(str, hashTable[index]->next, n);
    }
}

void printHash(){
    for(int i = 0; i < MAXHASH; i++){
        if(hashTable[i]->key!=NULL)
            printf("hashTable[%d]:%s\n",i,hashTable[i]->key);
        if(crashTable[i]->key!=NULL)
            printf("crashTable[%d]:%s\n",i,crashTable[i]->key);
    }
}

int  PowerMod (int a, int b, int n, int c) 
{  
    int  ans = 1; 
    b = b % c; 
    while (n > 0) {  
        if(n % 2 == 1) 
            ans = (ans * b) % c; 
        n = n / 2;       //b >>= 1;
        b = (b * b) % c; 
    } 
    return (a*ans)%c; 
} 

int hash(char* key, int n){
    int addr = 0;
    for(int i = 0; i < n; i++){
        addr += PowerMod(key[i], 128, i, MAXHASH);
    }
    return addr%MAXHASH;
}

int main(){
    initTable();
    char* str;
    int i;
    while(1){
        gets(str);
        if(strcmp(str,"exit")==0) break;
        i = 0;
        while(str[i++]!='\0'){}
        insertHash(str,hash(str,i),i);
        printf("%d\n",hash(str,i));
    }
    printHash();
    return 0;
}

最后得到:

>gcc hash.c
>a.exe
asdf
21
hellworld
84
microcold
81
minicool
81
tinycool
20
tinycold
12
weixiaoleng
11
exit
crashTable[0]:minicool
hashTable[11]:weixiaoleng
hashTable[12]:tinycold
hashTable[20]:tinycool
hashTable[21]:asdf
hashTable[81]:microcold
hashTable[84]:hellworld

可见一方面的确散列了,另一方面也的确防撞了。

到此这篇关于C语言写一个散列表的文章就介绍到这了,更多相关C语言写散列表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言实现散列表(哈希Hash表)实例详解

    C语言实现散列表(哈希Hash表) 实例代码: //散列表查找算法(Hash) #include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define SUCCESS 1 #define UNSUCCESS 0 #define HASHSIZE 7 #define NULLKEY -32768 typedef int Status; type

  • 详解散列表算法与其相关的C语言实现

    散列表(也叫哈希表)是一种查找算法,与链表.树等算法不同的是,散列表算法在查找时不需要进行一系列和关键字(关键字是数据元素中某个数据项的值,用以标识一个数据元素)的比较操作. 散列表算法希望能尽量做到不经过任何比较,通过一次存取就能得到所查找的数据元素,因而必须要在数据元素的存储位置和它的关键字(可用key表示)之间建立一个确定的对应关系,使每个关键字和散列表中一个唯一的存储位置相对应.因此在查找时,只要根据这个对应关系找到给定关键字在散列表中的位置即可.这种对应关系被称为散列函数(可用h(ke

  • C语言写一个散列表

    目录 一.快速理解散列表 二.散列函数 三.防撞 一.快速理解散列表 散列表,就是下标可以为字母的数组. 假设现有一个数组int a[100],想查找其中第40个元素,则直接输入a[40]就可以了,时间复杂度为O ( 1 ) O(1)O(1). 问题在于,当下标不是数字,而是一个字符串的时候,可能需要一个超大的空间才能将所有下标妥善地存放在特定的位置.例如,若以大小写字母作为下标索引,那么一位就需要预留52个空间,10位就需要52的10次方 这么大的空间,根本没有设备可以满足. 好在,52的10

  • 如何在Java中实现一个散列表

    目录 前言: 优化1 优化2 优化3 如何实现 总结 前言: 假设现在有一篇很长的文档,如果希望统计文档中每个单词在文档中出现了多少次,应该怎么做呢? 很简单! 我们可以建一个HashMap,以String类型为Key,Int类型为Value: 遍历文档中的每个单词 word ,找到键值对中key为 word 的项,并对相关的value进行自增操作. 如果该key= word 的项在 HashMap中不存在,我们就插入一个(word,1)的项表示新增. 这样每组键值对表示的就是某个单词对应的数量

  • 利用Java和c语言写一个计算器

    目录 一.java计算器 1.类名计算器 2.类名sum 3.示例图 4.代码 4.1计算器类 4.2sum类下 二.C语言下的计算器 2.代码 一.java计算器 步骤: 1.建包 2.导入输入包 3.创建类 4.类名调用 类名调用的方法:类名标识符=new 类名 意思是用类名创建一个标识符,通过new类名即可 通过这种方法就可以是处于本类或者其他类的可以调用 为了更方便阅读,我建了两个类 1.类名计算器 2.类名sum 3.示例图 4.代码 4.1计算器类 package com.haha;

  • 使用Go语言写一个Http Server的实现

    目录 调试 功能1 功能2 功能3 功能4 Http Server 代码 go.mod: module goStudy1 go 1.17 main.go: package main import ( "fmt" "os" "strconv" //"github.com/thinkeridea/go-extend/exnet" "io" "log" "net/http"

  •  JavaScript 数据结构之散列表的创建(2)

    目录 一.处理散列值冲突 1.分离链接 2.put 方法 3.get 方法 前言: 上一篇我们介绍了什么是散列表,并且用通俗的语言解析了散列表的存储结构,最后动手实现了一个散列表,相信大家对散列表已经不陌生了. 如果还不清楚散列表,请先阅读上一篇文章:JavaScript 数据结构之散列表的创建(1) 上篇末尾我们遗留了一个问题,就是将字符串转化为散列值后可能出现重复.当以散列值(hash 值)为 key 存储数据时,就会有覆盖已有数据的风险.本篇我们看如何处理散列值冲突的问题,并实现更完美的散

  • Java数据结构之散列表(动力节点Java学院整理)

    基本概念 散列表(Hash table,也叫哈希表),是根据关键字(key value)而直接进行访问的数据结构. 说的具体点就是它通过吧key值映射到表中的一个位置来访问记录,从而加快查找的速度. 实现key值映射的函数就叫做散列函数 存放记录的数组就就叫做散列表 实现散列表的过程通常就称为散列(hashing),也就是常说的hash 散列 这里的散列的概念不仅限于数据结构了,在计算机科学领域中,散列-哈希是一种对信息的处理方法,通过某种特定的函数/算法(散列函数/hash()方法)将要检索的

  • Ruby中的数组和散列表的使用详解

    Ruby的数组(arrays)和散列表(hashes)是被索引的收集(indexed collections). 两者都存储对象的集合,通过键(key)来访问.数组的键是整数.而散列表支持以任何对象作为它的键.数组和散列表会按需调整大小来保存新的元素.访问数组元素是高效的,但是散列表提供了灵活性.任何具体的数组或散列表可以保存不同类型的对象. 使用数组字面量(array literal)--即方括号之间放一组元素--可以创建和初始化新的数组对象.有了数组对象,在方括号之间提供索引便可以访问单个元

  • 散列表的原理与Java实现方法详解

    本文实例讲述了散列表的原理与Java实现方法.分享给大家供大家参考,具体如下: 概述 符号表是一种用于存储键值对(key-value pair)的数据结构,我们平常经常使用的数组也可以看做是一个特殊的符号表,数组中的"键"即为数组索引,值为相应的数组元素.也就是说,当符号表中所有的键都是较小的整数时,我们可以使用数组来实现符号表,将数组的索引作为键,而索引处的数组元素即为键对应的值,但是这一表示仅限于所有的键都是比较小的整数时,否则可能会使用一个非常大的数组.散列表是对以上策略的一种&

  • 用Python写一个无界面的2048小游戏

    以前游戏2048火的时候,正好用其他的语言编写了一个,现在学习python,正好想起来,便决定用python写一个2048,由于没学过python里面的界面编程,所以写了一个极其简单的无界面2048.游戏2048的原理和实现都不难,正好可以拿来练手,要是不知道这游戏的话,可以去网上查一下,或者下载一个到手机来玩一下,我就不在说其原理.我知道不放图的话大家一点兴趣都没,下面首先放一张游戏成型图,然后我们在来讲如何一步步用最基础的知识来实现. 一.生成4*4的矩阵 游戏的第一步便是生成一个4*4的矩

随机推荐