先说下哈希表,它又叫散列表,是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
我们找对应值的话,只需到映射位置就可以,效率非常之高。
对应算法代码
Hash.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int KeyType;
typedef int ValuType;
typedef int DataType;
typedef enum Status
{
Exist,
Delete,
Empty
}Status;
typedef struct HashNode
{
KeyType key;
ValuType value;
Status status;
}HashNode;
typedef struct HashTable
{
HashNode*node;
size_t size;
size_t N;
}HashTable;
void HashInit(HashTable*table);//哈希表初始化
int HashNodeInsert(HashTable*table, KeyType key, ValuType value);//插入节点
void HashNodeRemove(HashTable*table, KeyType key);//删除节点
HashNode* HashNodeFind(HashTable*table, KeyType key);//查找节点
void HashPrintf(HashTable*table);
void HashTableDestory(HashTable*table);//销毁
Hash.c
#include"Hash.h"
void HashInit(HashTable*table)//哈希表初始化
{
table->N = 13;
table->size = 0;
table->node = (HashNode*)malloc(sizeof(HashNode) * (table->N));
assert(table->node);
for (size_t i = 0; i < table->N; i++)
{
table->node[i].status = Empty;
}
}
int HashNodeInsert(HashTable*table, KeyType key, ValuType value)//插入节点
{
assert(table);
//负载因子小于0.5,插入;大于则扩容
if ((table->size * 10 )/ table->N >= 5)
{
//malloc,把之前的重新插入
HashTable newTable;
newTable.node= (HashNode*)malloc(sizeof(HashNode) * 2 * table->N);
assert(newTable.node);
newTable.N = 2 * table->N;
newTable.size = 0;
for (size_t i = 0; i < newTable.N; i++)
{
newTable.node[i].status = Empty;
}
for (size_t i = 0; i < table->N; i++)
{
if(table->node[i].status==Exist)
HashNodeInsert(&newTable, table->node[i].key, table->node[i].value);
}
free(table->node);
table->node = newTable.node;
table->N = newTable.N;
table->size = newTable.size;
return 0;
}
else//空间足够,不用扩容,直接查找位置插入
{
size_t position = key % (table->N);
if (table->node[position].status == Empty)//key模N的位置是空,直接插入
{
table->node[position].key= key;
table->node[position].value = value;
table->node[position].status = Exist;
(table->size)++;
return 0;
}
else//位置不是空,向后查找空位置
{
//插入节点存在,value++
if (HashNodeFind(table, key) != NULL)
{
++(HashNodeFind(table, key)->value);
return 0;
}
++position;
if (position == table->N)
position = 0;
while (table->node[position].status == Exist)
{
position++;
if (position == table->N)
position = 0;
}
table->node[position].key = key;
table->node[position].value = value;
(table->size)++;
table->node[position].status = Exist;
return 0;
}
}
}
void HashNodeRemove(HashTable*table, KeyType key)//删除节点
{
assert(table);
if (HashNodeFind(table,key) == NULL)
return;
else
{
HashNode*cur = HashNodeFind(table, key);
cur->key = 0;
cur->status = Delete;
table->size--;
}
}
HashNode* HashNodeFind(HashTable*table,KeyType key)//查找节点
{
assert(table);
size_t i = key%table->N;
size_t time = 0;//计算查找次数
while (time < table->N)
{
if ((table->node[i].status==Exist)&&(table->node[i].key == key))
return &(table->node[i]);
i++;
if (i == table->N)
i = 0;
time++;
}
return NULL;
}
void HashTableDestory(HashTable*table)//销毁
{
assert(table);
free(table->node);
table->node = NULL;
}
void HashPrintf(HashTable*table)
{
assert(table);
for (size_t i = 0; i < table->N; i++)
{
if (table->node[i].status == Exist)
printf("Node[%-2d] = %-2d ,times: %-4d ,status: Exist\n", i, table->node[i].key, table->node[i].value);
else if (table->node[i].status == Delete)
printf("Node[%-2d] = %-2d ,times: %-4d ,status: Delete\n", i, table->node[i].key, table->node[i].value);
}
printf("\n");
}
Test.c
#include"Hash.h"
Test()
{
HashTable table;
HashInit(&table);
HashNodeInsert(&table, 8, 1);
HashNodeInsert(&table, 555, 1);
HashNodeInsert(&table, 9, 1);
HashNodeInsert(&table, 100, 1);
HashNodeInsert(&table, 100, 1);
HashNodeInsert(&table, 19, 1);
HashNodeInsert(&table, 20 ,1);
HashNodeInsert(&table, 7, 1);
HashNodeInsert(&table, 122, 1);
HashNodeInsert(&table, 122, 1);
HashNodeInsert(&table, 789, 1);
HashPrintf(&table);
HashNodeRemove(&table, 8);
HashNodeRemove(&table, 122);
HashNodeRemove(&table, 19);
HashNodeRemove(&table, 555);
HashPrintf(&table);
HashTableDestory(&table);//销毁
}
int main()
{
Test();
return 0;
}
例题:
给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
解题思路:100G大小的log file分成1000份,因为IP是字符串,将字符串转成对应的整数,然后用哈希表存放这些整数key和对应的次数value。筛选出每份出现次数最多的IP,然后再对这1000个进行比较,找到出现最多的IP。
至于如何找到tok K的IP,可以先建一个K个节点的小堆(父节点小于孩子节点)。因此,最小数数是堆顶节点,最大数保存在右子树的最右节点,用剩下的数和对顶元素进行比较。如果比队顶元素大,则用该数字替换掉堆顶元素,然后进行一次向下排序。经过多次比较,就可找到top K的元素。
评论区留给热心的读者,欢迎提问。