数据结构---hash表

目录

1.基本概念

2.hash冲突

3.hash的简单实现

4. hash表的应用场景

5.hash表和链表扩展的实现​​​​​​​


1.基本概念

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数

2.hash冲突

比如

f(x) = x % 5;          //哈希函数

int hashTble[5];     //哈希表

int value[5];         //存放数据的数组

  1. 先忘value[0]存入 'A' [对应ascii码为65] ,  value[1]存入'F' [对应ascii码为70]
  2. f(65) = 0;  那么hashTable[0] = 0;
  3. f(70) = 0; 那么hashTable[1] = 0;

这就产生了冲突 (hanhTable的0和1是指向的一样的位置)

  • 解决方法:让存入hash表的值不一样

3.hash的简单实现

需要实现以下几个接口:

  • struct hash *hash_create(void);     //创建一个hash表

  • void hash_destroy(struct hash *ht);    //销毁hash表

  • int hash_insert(struct hash *ht, const char* key, void *data); //在hash表ht插入一个元素(key, data)

  • void *hash_lookup(struct hash *ht, const char* key);  //在hash表ht中,通过key得到value的指针

以下摘自linuxptp的hash实现

/**
 * @file hash.c
 * @brief Implements a simple hash table.
 * @note Copyright (C) 2015 Richard Cochran <richardcochran@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include <stdlib.h>
#include <string.h>

#include "hash.h"

#define HASH_TABLE_SIZE 200

struct node {
	char *key;
	void *data;
	struct node *next;
};

struct hash {
	struct node *table[HASH_TABLE_SIZE];
};

static unsigned int hash_function(const char* s)
{
	unsigned int i;

	for (i = 0; *s; s++) {
		i = 131 * i + *s;
	}
	return i % HASH_TABLE_SIZE;
}

//创建hash表
struct hash *hash_create(void)
{
	struct hash *ht = calloc(1, sizeof(*ht));
	return ht;
}

//清除hash表
void hash_destroy(struct hash *ht, void (*func)(void *))
{
	unsigned int i;
	struct node *n, *next, **table = ht->table;

	for (i = 0; i < HASH_TABLE_SIZE; i++) {
		for (n = table[i] ; n; n = next) {
			next = n->next;
			if (func) {
				func(n->data);
			}
			free(n->key);
			free(n);
		}
	}

	free(ht);
}

//向hash表插入元素
int hash_insert(struct hash *ht, const char* key, void *data)
{
	unsigned int h;
	struct node *n, **table = ht->table;
// 通过hash函数,得到key对应的value【value是hash表的位置下标】
	h = hash_function(key);
//避免得到的value值
	for (n = table[h] ; n; n = n->next) {
		if (!strcmp(n->key, key)) {
			/* reject duplicate keys */
			return -1;
		}
	}
//为插入的新元素(链表节点)分配内存
	n = calloc(1, sizeof(*n));
	if (!n) {
		return -1;
	}
//为链表节点赋值
	n->key = strdup(key);
	if (!n->key) {
		free(n);
		return -1;
	}
	n->data = data;
	n->next = table[h];
//将这个新元素的内存地址存入hash表
	table[h] = n;
	return 0;
}
//通过key在hash表ht中得到data
void *hash_lookup(struct hash *ht, const char* key)
{
	unsigned int h;
	struct node *n, **table = ht->table;

	h = hash_function(key);

	for (n = table[h] ; n; n = n->next) {
		if (!strcmp(n->key, key)) {
			return n->data;
		}
	}
	return NULL;
}

比较关键的逻辑就是:hash_insert函数,主要分为几步

  1. 通过hash_function()得到key对应的value,即在hashTable的位置
  2. 把传入的key,data存入一个node
  3. 把hashTable[value]赋值为node的内存地址

通过hash_lookup()找到key对应的data

  1. 先通过hash_function()算出value,即在hashTable的位置
  2. 通过hashTable[value],找到key对应的那个node
  3. 通过node可找到key对应的data
  4. 返回data作为hash_lookup()的返回值
  • hash表的实现

uthash:  uthash简介_Fan Upward的博客-CSDN博客_uthash

4. hash表的应用场景

C语言实现的数据结构之------哈希表_smstong的博客-CSDN博客_c语言实现哈希表

  • 如果需要从链表中找到一个元素,那么就需要遍历链表。
  • 为了解决根据关键字快速找到元素的存放地址,哈希表应运而生。它通过某种算法(哈希函数)直接根据关键字计算出元素的存放地址。
  • 当不同的关键字可能产生出同样的hash值。解决方法可以是链式扩展:即把相同的hash值元素放到一个链表中。

5.hash表和链表扩展的实现

​​​​​​​

 

  • 代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct Element
{
	int key;
	int value;  // 可以是任何结构的数据
} Element;

//数据存入单链表
typedef struct LinkTable
{
	Element data;
    struct LinkTable* next;
}Node;

typedef struct HashTable {
    Node* Head;
    int tablesize;
	int elementsize; 
} HashTable;


// 打印哈希表。
void PrintTable(HashTable *hh)
{
	int ii;
	printf("=========\n");

	for (ii = 0; ii < hh->tablesize; ii++)
	{
		Node *pp = hh->Head[ii].next;
		while (pp)
		{
			printf("[%d-%d] ", pp->data.key, pp->data.value);
			// printf("[%d-%s] ",pp->data.key,pp->data.value);
			pp = pp->next;
		}

		printf("\n");
	}

	printf("=========\n");
}

HashTable* InitHashTable(int size)
{
    HashTable* hh = (HashTable*)malloc(sizeof(HashTable));

    //分配单链表的头结点---size是几就有几个头链表
	hh->Head = malloc(sizeof(Node) * size);
	memset(hh->Head, sizeof(Node)* size, 0x00);

    hh->tablesize = size;
	hh->elementsize = 0;
}

unsigned int hash(HashTable *hh, int key)
{
	return key % hh->tablesize;  // 对表长取余。
}

Node* lookup(HashTable* hh, int key)
{
    // 根据key算出hashvalue
	int hashvalue = hash(hh, key);

	// 遍历单链表看是否有key
	Node* tmp = hh->Head[hashvalue].next;
    while (tmp != NULL) {
		if (tmp->data.key == key) 
		{
			printf("has key [%d]\n", key);
			return tmp; 
		}
		tmp = tmp->next;
	}
	return NULL;


}

void Insert(HashTable* hh, Element* lt)
{
    // find exist in hashtable?
    Node* result = lookup(hh, lt->key);
    if (result) {
		return;
	}
    // 根据key算出hashvalue
	int hashvalue = hash(hh, lt->key);
	printf("hashvalue = [%d]\n", hashvalue);

    // 更新链表结点的数据
	Node* nd = malloc(sizeof(Node));
	nd->data.key = lt->key;
	nd->data.value = lt->value;

    //根据hashvalue得到在链表的头结点数组的下标位置, 并插入nd结点
	nd->next = hh->Head[hashvalue].next;
    hh->Head[hashvalue].next = nd;

	hh->elementsize++;
}

void delete(HashTable* hh, int key)
{
    // find exist in hashtable?
    // Node* result = lookup(hh, key);
    // if (!result) {
	// 	printf("no this node\n");
	// 	return;
	// }

    // 根据key算出hashvalue
	int hashvalue = hash(hh, key);

	// 遍历单链表看是否有key
	Node* tmp = hh->Head[hashvalue].next;
	Node *last = tmp;
	Node *next = NULL;
    while (tmp != NULL) {
		if (tmp->data.key == key) 
		{
			printf("delete key [%d]\n", key);
			free(tmp);
			tmp = NULL;

			last->next = next;
			hh->elementsize--;
			break;
		}
        // update last and next node
		last = tmp;
		tmp = tmp->next;
		if (tmp) { next = tmp->next;}
	}


}

void freehashtable(HashTable *hh)
{
	if (hh == NULL)	return;
    
	//删除每个节点的链表
    for (int i = 0; i < hh->tablesize; i++)
	{
		printf("hash pos[%d]\n", i);
		Node* tmp = hh->Head[i].next;
		Node* tmp2;
		while (tmp != NULL) {
           printf("key %d val = %d\n", tmp->data.key, tmp->data.value);
		   tmp2 = tmp->next;
		   free(tmp);
		   tmp = tmp2;

		   hh->elementsize--;
		}
		PrintTable(hh);
		hh->Head[i].next = NULL;
	}
	printf("hash count[%d]\n", hh->elementsize);

}

int main()
{
	// 初始化哈希表。
	HashTable *hh = InitHashTable(10);


	Element ee;

	{
		ee.key = 10; ee.value = 110; Insert(hh, &ee);
		ee.key = 10; ee.value = 110; Insert(hh, &ee);
		printf("count=%d\n", hh->elementsize);
		PrintTable(hh);    // 打印哈希表 
	}

	{
		ee.key = 12; ee.value = 112; Insert(hh, &ee);
		PrintTable(hh);    // 打印哈希表 

		delete(hh, 12);     // 删除哈希表中关键字为12的数据元素。
		PrintTable(hh);    // 打印哈希表 
	}

	// 插入数据元素,关键字从10到20。
	ee.key = 10; ee.value = 110; Insert(hh, &ee);
	ee.key = 11; ee.value = 111; Insert(hh, &ee);
	ee.key = 13; ee.value = 113; Insert(hh, &ee);
	ee.key = 14; ee.value = 114; Insert(hh, &ee);
	ee.key = 15; ee.value = 115; Insert(hh, &ee);
	ee.key = 16; ee.value = 116; Insert(hh, &ee);
	ee.key = 17; ee.value = 117; Insert(hh, &ee);
	ee.key = 18; ee.value = 118; Insert(hh, &ee);
	ee.key = 19; ee.value = 119; Insert(hh, &ee);

	// // 插入数据元素,关键字从20到30。
	ee.key = 20; ee.value = 120; Insert(hh, &ee);
	ee.key = 21; ee.value = 121; Insert(hh, &ee);
	ee.key = 22; ee.value = 122; Insert(hh, &ee);
	ee.key = 23; ee.value = 123; Insert(hh, &ee);
	ee.key = 24; ee.value = 124; Insert(hh, &ee);
	ee.key = 25; ee.value = 125; Insert(hh, &ee);
	ee.key = 26; ee.value = 126; Insert(hh, &ee);
	ee.key = 27; ee.value = 127; Insert(hh, &ee);
	ee.key = 28; ee.value = 128; Insert(hh, &ee);
	ee.key = 29; ee.value = 129; Insert(hh, &ee);

	// // 插入数据元素,关键字从30到32。
	ee.key = 30; ee.value = 130; Insert(hh, &ee);
	ee.key = 31; ee.value = 131; Insert(hh, &ee);
	ee.key = 32; ee.value = 132; Insert(hh, &ee);


	// delete(hh, 12);     // 删除哈希表中关键字为12的数据元素。

	printf("count=%d\n", hh->elementsize);
	PrintTable(hh);    // 打印哈希表 

	// 在哈希表中查找关键字18。
	Node *pp = lookup(hh, 18);
	if (pp == 0) printf("LookUp(18) failed.\n");
	else printf("key=18,value=%d.\n", pp->data.value);

	freehashtable(hh);  // 销毁哈希表 

	return 0;
}

斯顿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值