数据结构—hash表

哈希表,是根据键值对(Key  --- value)进行访问的数据结构。也就是说,它通过把key值映射到表中一个位置来访问记录,以加快查找的速度。
给定表ht,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

总结上面的所说:

        若在Hash表中存放数据的是一个结构体数组,结构体包括了key值、value值和表示数据状态的stat,共三个属性,假如这个数组有n个元素,那么就对应了0~n-1个位置,而要插入的每个元素都有一个key值,通过哈希函数f(key)将转为offset,这个offset的值就在0~n-1之间,然后在数组对应的offset位置上填入key值和value值,并将stat属性置为Valid(有效)

        一个好的hash函数使得每个offset尽量均匀

哈希冲突:

        简单的来说,当哈希函数计算当前key值得到的offset 与 之前一个key值计算得到的offset相等,这就是哈希冲突

解决哈希冲突

        解决哈希冲突的办法主要有两种: 1)  闭散列  2) 开散列

1)闭散列:
        当通过哈希函数计算得到的offset,如果这个位置可以插入,就直接插入,如果这个位置已经被占,那么就顺着往后查找,知道找到第一个可以插入的位置,在进行插入操作。

原理图:

代码如下:

头文件(hash.h)

#pragma once
#include<unistd.h> 

#define HashMaxSize 1000 

typedef enum Stat { 
  Empty, 
  Valid, 
  Invalid // 当前元素被删除了 
} Stat; 

typedef int KeyType; 
typedef int ValType; 

typedef size_t (*HashFunc)(KeyType key); 

typedef struct HashElem { 
  KeyType key; 
  ValType value; 
  Stat stat; // 引入一个 stat 标记来作为是否有效的标记 
} HashElem; 

typedef struct HashTable { 
  HashElem data[HashMaxSize]; 
  size_t size; 
  HashFunc func; 
} HashTable; 

void HashInit(HashTable* ht, HashFunc func); 

int HashInsert(HashTable* ht, KeyType key, ValType value); 

// 输入key, 查找对应key的value. 
int HashFind(HashTable* ht, KeyType key, ValType* value); 

void HashRemove(HashTable* ht, KeyType key); 

int HashEmpty(HashTable* ht); 

size_t HashSize(HashTable* ht); 

void HashDestroy(HashTable* ht); 

头文实现(hash.c)

#include"hash.h"

// 哈虚函数-->对数组的最大值取余 
size_t HashFuncDefault(KeyType key) {
  return key%HashMaxSize;
}


//判断hash表是否是空
// -1 表示非法输入
// 0  表示空
// 1  表示非空 
int HashEmpty(HashTable* ht) {
  if(ht == NULL) {
    return -1;
  }
  if(ht->size == 0) {
    return 0;
  }
  return 1;
} 


//获取hash的大小 
size_t HashSize(HashTable* ht) {
  if(ht == NULL) {
    return 0;
  }
  return ht->size;
}


//hash表的初始化
void HashInit(HashTable* ht, HashFunc hash_func) {
  if(ht == NULL) {
    return;
  }
  ht->func = hash_func; 
  ht->size = 0;
  size_t i;
  for(i=0; i<HashMaxSize; i++) {
    ht->data[i].stat = Empty;
  }
}


//销毁hash表-->将所有元素的状态置为Empty
void HashDestroy(HashTable* ht) {
  if(ht == NULL) {
    return;
  }
  ht->func = NULL;
  ht->size = 0;
  size_t i = 0;
  for(i=0; i<HashMaxSize; i++) {
    ht->data[i].stat = Empty;
  }
}


//向hash表中插入
int HashInsert(HashTable* ht, KeyType key, ValType value) {
  if(ht == NULL) {
    return -1;
  }
  //判断hash表是否能够再插入元素(根据负载因子)
  if(ht->size > HashMaxSize * 0.8) {
    return -1;
  }
  //根据key来计算offset
  size_t offset = ht->func(key);
  //从offset位置开始线性想后面查找,找到第一个为offset的位置插入
  while(1) {
    if(ht->data[offset].stat != Valid) {
      //找到一个合适的位置插入
      ht->data[offset].stat = Valid;
      ht->data[offset].value = value;
      ht->data[offset].key = key;
      ht->size++;
    } else if(ht->data[offset].stat == Valid && ht->data[offset].value == value){
      //找到相同的元素直接返回
      return 0;
    } else {
      offset++;
      if(offset >= HashMaxSize) {
        offset = 0;
      }
    }
    
  }
}


//在中按key值查找元素
int HashFind(HashTable* ht, KeyType key, ValType* value) {
  if(ht == NULL) {
    //非法输入
    return 0;
  }
  if(ht->size == 0) {
    //空Hash表
    return 0;
  }
  //根据key值计算offset
  size_t offset = ht->func(key);
  //从offset开始往后进行查找,没取到一个元素 和key进行比较
  while(1) {
    //如果找到key值相同,且有效的元素,查找成功,并返回value
    if(ht->data[offset].key == key && ht->data[offset].stat == Valid) {
      *value = ht->data[offset].value;
      return 1;
    } 
    //找到的位置为空,则说明要找的位置不存在
    else if(ht->data[offset].stat == Empty) {
      return 0;
    }
    //key值不相等,向后茶轴
    else {
      offset++;
      if(offset > HashMaxSize) {
        offset = 0;
      }
    }
  }
}


//在hash表中  按key值删除元素
void HashRemove(HashTable* ht, KeyType key) {
  if(ht == NULL) {
    return;
  }
  if(ht->size == 0) {
    return;
  }
  //根据key值计算offset
  size_t offset = ht->func(key);
  size_t flag = offset;
  while(ht->data[offset].stat != Empty) {
    if(ht->data[offset].stat == Valid && ht->data[offset].key == key) {
      ht->data[offset].stat = Invalid;
      return;
    }  
  offset++;  
  if(offset == flag) {
    return;
  }
  if(offset == HashMaxSize)
    offset = 0;
  }
}

2)开散列

        当计算的到大量的相同的offset值,就在当前位置形成一个链表,把这些元素组织起来,在每一条链表上的key值可能不相同,但是通过哈希哈数计算出的offset一定相同

原理图:


源代码:

头文件(hash.h)

#pragma once 

#include <unistd.h>

#define HashMaxSize 1000 

typedef int KeyType; 
typedef int ValType; 

typedef size_t (*HashFunc)(KeyType key); 

typedef struct HashElem { 
  KeyType key; 
  ValType value; 
  struct HashElem* next; 
} HashElem; 

// 数组的每一个元素是一个不带头结点的链表 
// 对于空链表, 我们使用 NULL 来表示 
typedef struct HashTable { 
  HashElem* data[HashMaxSize]; 
  size_t size; 
  HashFunc hash_func; 
} HashTable; 

void HashInit(HashTable* ht, HashFunc hash_func); 

// 约定哈希表中不能包含 key 相同的值. 
int HashInsert(HashTable* ht, KeyType key, ValType value); 

int HashFind(HashTable* ht, KeyType key, ValType* value); 

void HashRemove(HashTable* ht, KeyType key); 

size_t HashSize(HashTable* ht); 

int HashEmpty(HashTable* ht); 

void HashDestroy(HashTable* ht); 

头文件实现(hash.c)

#include"hash.h"
#include<stdio.h>
#include<stdlib.h>
size_t hash_func(KeyType key) {
  return key % HashMaxSize;
}

void HashInit(HashTable* ht, HashFunc hash_func) {
  if(ht == NULL || hash_func == NULL) {
    return;
  }
  
  int i = 0;
  for(; i<HashMaxSize; i++) {
    ht->data[i] = NULL;
  }
  ht->size = 0;
  ht->hash_func = hash_func;
}

//插入函数返回值
//成功 返回1
//失败 返回-1
int HashInsert(HashTable* ht, KeyType key, ValType value) {
  if(ht == NULL) {
    return -1;
  }
  if(key < 0) {
    return -1;
  } 
  
  size_t offset = ht->hash_func(key);
  HashElem* head = ht->data[offset];
  
  //当前offset位置没有元素的情况
  if(head == NULL) {
    ht->data[offset] = (HashElem*)malloc(sizeof(HashElem));
    ht->data[offset]->key = key;
    ht->data[offset]->value = value;
    ht->data[offset]->next = NULL;
    return 1;
  }
  
  //当前offset有元素的情况
  for(; head->next!=NULL; head=head->next);
  head->next = (HashElem*)malloc(sizeof(HashElem));
  head = head->next; 
  head->key = key;
  head->value = value;
  head->next = NULL;
  return 1;
}

//在hash表中 通过key查找value
int HashFind(HashTable* ht, KeyType key, ValType* value) {
  if(ht == NULL || value == NULL) {
    return -1;
  }
  if(key < 0) {
    return -1;
  }
  
  KeyType offset = ht->hash_func(key);
  HashElem* head = ht->data[offset];
  for(; head != NULL; head = head->next) {
    if(head->key == key) {
      *value = head->value;
      return 1;
    }
  }
  return 0;
}

//在hash表中,通过key值删除一个元素
void HashRemove(HashTable* ht, KeyType key) {
  if(ht == NULL) {
    return;
  }
  if(key < 0) {
    return;
  }

  KeyType offset = ht->hash_func(key);
  HashElem* head = ht->data[offset];
  if(head == NULL) {
    return;
  }
  while(head->next != NULL) {
    if(head->next->key == key) {
      HashElem* cur = head->next;
      head->next = cur->next;
      free(cur);
      return;
    }
    head = head->next;
  }
}



//得到当前hash表已占用的位置的格式
//而不是 hash表中元素的个数
size_t HashSize(HashTable* ht){
  if(ht == NULL) {
    return 0;
  }
  return ht->size;
}

// 判断hash表是否为空
// 输入错位 返回-1
// 为空 返回0
// 不为空 返回1
int HashEmpty(HashTable* ht) {
  if(ht == NULL) {
    return -1;
  }

  if(ht->size == 0) {
    return 0;
  } 
  
  return 1;
}

//递归free
//如果hash表上的一个位置上有很多个元素,则这些元素被一个链表组织起来
//head所指向hash表的一个位置上的链表的头部
void hash_free(HashElem* head) {
  if(head == NULL) {
    return;
  }
  hash_free(head->next);
  free(head);
}
//删除哈希表
void HashDestroy(HashTable* ht) {
  if(ht == NULL) {
    return;
  }
  
  size_t i = 0;
  for(; i<HashMaxSize; i++) {
    if(ht->data[i] != NULL) {
      hash_free(ht->data[i]);
    }
  }  //end of for
  ht->hash_func = NULL;

}

猜你喜欢

转载自blog.csdn.net/luhaowei0066/article/details/80708624
今日推荐