散列是我们在实际编程中经常用到的一种算法,也就是很多人所说的“哈希算法”,散列表的实现通常叫做散列。
理想的散列表数据结构只不过是一个包含关键字的具体固定大小的数组,我们把表的大小记作 table_size,通常是让表的大小从0到table_size-1变化;
典型情况下,一个关键字就是一个带有相关值的字符串,每个字符串被映射到从0到table_size-1这个范围中的某个数,并且被放到适当的单元中,这个映射叫散列函数;
理想情况下散列函数应该运算简单并且应该保证任何两个不同的关键字映射到不同单元,不过,这是不可能的。
剩下的问题就是要选择一个函数,决定当两个关键字散列到同一个位置的时候(成为冲突),解决冲突的方法以及突和确定散列表的大小。
其实主要的编程细节是解决冲突的消除问题,如果当一个元素被插入时另一个元素已经存在(散列值相同),那么就会产生一个冲突,
解决冲突的方法有几种,其中最有代表性的为分离链接法和开放定址法,本文主要讲解分离链表法,并在最后附上详细代码
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
分离链表法,是将散列到同一个值(同一个位置)的所有元素保留到一个链表中,这些表都有表头,如果空间很紧凑,则更可取的方法是避免使用这些表头。
每个散列表(数组)元素位置即对应一个不固定节点数的链表;
说到链表不得不提的是链表很多种,有头的、无头的、双向的、单向的、循环的、不循环的,这些都可以排列组合成很多种链表,
此外数据插入也分数据前插、数据后插;
但是应用哪一种就应该根据实际情况而定,如果涉及在表头插入、删除时用有头链表比较合适,因为它们都改变了表的起始端,不用头很容易造成表的丢失,
如果你不是很理解头的概念,说直白点,头就是一个空节点;
当查找数据时只涉及向后查找,应用单向链表足矣,但是当涉及向前查找数据时,用双向链表比较合适,优点是加快了程序执行效率,缺点是增加了开销;
当需要快速查找最后一次插入的数据时,数据后插时,用有头双向链表,每次在最后一次插入元素的后面插入数据,这样head->prev即为最后一次插入的数据,
数据前插时,用有头单向链表,每次在头后面插入,这样head->next即为最后一次插入的数据,省去了循环和双向操作。
所以当应用链表时,对于链表的选取至关重要,往往起到事半功倍的效果。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
多说无用,以下是实际代码,其实也是我实际工作中所遇到的问题,只不过是摘出部分有针对性的来进行讲解。
因为项目需要,需要处理一个文件,文件内容就是字符串,一个字符串一行,每行字符串中间没有空格等分割符;
应为应用分离链表法解决冲突,所以数组大小与字符串行数相同,数组应该说明的是--结构体指针数组,每个数组元素是一个结构题指针。
其中散列函数,应用的是elf算法,此算法专门针对字符串,也是经典教材中的经典算法,只不过本人有稍加改动
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define handle_error(msg) do{perror(msg);exit(EXIT_FAILURE);}while(0)
#define MAXCHANSIZE 32
#define MAXHASHSIZE 500
struct hash_st
{
FILE *fp;
char channel[MAXCHANSIZE];
struct hash_st *next; //singal list without head
};
struct hash_st *hash[MAXHASHSIZE]; //定义结构题指针数组,数组元素为指向结构体的指针
/* elf 哈希算法 */
static int elf_hash(const char *key)
{
unsigned long h = 0;
unsigned long g;
while(*key)
{
h = (h<<4) + *key++;
g = h & 0xf0000000L;
if(g) h ^= g >> 24;
h &= ~g;
}
return h%MAXHASHSIZE;
}
/* 初始化哈希表 */
void init_hash()
{
int i;
for(i = 0; i < MAXHASHSIZE; i++)
hash[i] = NULL;
}
/* 插入哈希表 */
void insert_hash(int pos, char *name, FILE *fp)
{
if(hash[pos] == NULL)
{
hash[pos] = (struct hash_st *)malloc(sizeof(struct hash_st));
if(hash[pos] == NULL)
handle_error("malloc()->hash[pos]");
hash[pos]->fp = fp;
strcpy(hash[pos]->channel, name);
hash[pos]->next = NULL;
}
else
{
printf("------------------------------------------------- %d\n", pos);
struct hash_st *new = (struct hash_st *)malloc(sizeof(struct hash_st));
if(new == NULL)
handle_error("malloc()->new");
strcpy(new->channel, name);
new->fp = fp;
new->next = hash[pos]->next;
hash[pos]->next = new;
}
}
/* 打印哈希表 */
void print_hash()
{
int i;
for(i = 0; i < MAXHASHSIZE; i++)
{
struct hash_st *p = hash[i];
while(p != NULL)
{
printf("[%d]---%s\n", i, p->channel);
p = p->next;
}
}
}
/* 搜索哈希表 */
void search_hash()
{
int pos = -1;
char search_name[MAXCHANSIZE];
printf("search_name = ");
scanf("%s", search_name);
pos = elf_hash(search_name);
struct hash_st *p = hash[pos];
while(p != NULL)
{
if(!strcmp(p->channel, search_name))
{
printf("%s in pos %d\n", search_name, pos);
return ;
}
p = p->next;
}
printf("%s not found\n", search_name);
}
/* 释放哈希表 */
void free_hash()
{
int i = 0;
struct hash_st *p1 = NULL;
struct hash_st *p2 = NULL;
for(i = 0; i < MAXHASHSIZE; i++)
{
if(hash[i] != NULL)
{
p1 = hash[i]->next;
while(p1 != NULL)
{
p2 = p1->next;
printf("-------> free [%d]%s\n", i, p1->channel);
free(p1);
p1 = p2;
}
printf("-------> free [%d]%s\n", i, hash[i]->channel);
free(hash[i]);
}
}
}
FILE *fopen_file(char argv[], char mode[])
{
FILE *fp;
fp = fopen(argv, mode);
if(fp == NULL)
handle_error("fopen_file()->fopen");
return fp;
}
int main(int argc, char *argv[])
{
int pos = -1;
FILE *fp_file = NULL;
FILE *fp_channel = NULL;
char name[MAXCHANSIZE] = {'\0'};
if(argc < 2)
{
fprintf(stderr, "%s <file>\n", argv[0]);
exit(EXIT_FAILURE);
}
init_hash();
fp_file = fopen_file(argv[1], "r");
while(fscanf(fp_file, "%s", name) != EOF)
{
fp_channel = fopen_file(name, "w");
pos = elf_hash(name);
printf("name = %s\t\tpos = %d\n", name, pos);
insert_hash(pos, name, fp_channel);
}
print_hash();
search_hash();
free_hash();
return 0;
}