c语言实现哈希表

哈希表大家都在数据结构中学习过,应该是查找最快的一种数据结构了,最好的情况下可以达到线性的时间复杂度。

我们使用c语言实现开放地址的哈希表,也就是给定一个key值计算他的哈希值,如果冲突的话那么就在下一个槽位(slot)进行查找。

键key的状态码如果为 VT_UNDEFINED 的话那么就是这个槽位没有被占用或者已经被删除了

值value的状态码有VT_TRUE和VT_FALSE两种,只要这个槽位已经被占用过了,那么value的状态码就是VT_TRUE

所以在进行线性探测的时候

如果key的状态码为VT_UNDEFINED且value的状态码为VT_TRUE的话就表示这个槽位信息被伪删除了,但是探测链没有断,仍然可以继续探测

如果key的状态码为VT_UNDEFINED且value的状态码为VT_FALSE的话就表示这个槽位没有被占用,当前处于空闲状态

下面是obj_map.h的代码

#ifndef _OBJECT_MAP_H
#define _OBJECT_MAP_H
#include "header_obj.h"

#define MAP_LOAD_PERCENT 0.8

typedef struct{
    Value key;
    Value value;
} Entry;    //key->value对

typedef struct{
    ObjHeader objHeader;
    uint32_t capacity;  //总的Entry数量
    uint32_t count;     //已使用的Entry数量
    Entry* entries;     //Entry数组
} ObjMap;

ObjMap* newObjMap(VM* vm);

void mapSet(VM* vm, ObjMap* objMap, Value key, Value value);
Value mapGet(ObjMap* objMap, Value key);
void clearMap(VM* vm, ObjMap* objMap);
Value removeKey(VM* vm, ObjMap* objMap, Value key);
#endif

接下来是obj_map.c的代码

//######part1 p128######
#include "obj_map.h"
#include "class.h"
#include "vm.h"
#include "obj_string.h"
#include "obj_range.h"

//创建新map对象
ObjMap* newObjMap(VM* vm){
    ObjMap* objMap = ALLOCATE(vm, ObjMap);
    initObjHeader(vm, &objMap->objHeader, OT_MAP, vm->mapClass);
    objMap->capacity = objMap->count = 0;
    objMap->entries = NULL;
    return objMap;
}

//计算数字的哈希码
static uint32_t hashNum(double num){
    Bits64 bits64;
    bits64.num = num;
    return bits64.bits32[0] ^ bits64.bits32[1];     //前32与后32异或取hash码
}

//计算对象的哈希码
static uint32_t hashObj(ObjHeader* objHeader){
    switch(objHeader->type){
        case OT_CLASS:  //计算class的哈希值
            return hashString(((Class*)objHeader)->name->value.start,
                ((Class*)objHeader)->name->value.length);

        case OT_RANGE:  //计算range对象哈希码
            ObjRange* objRange = (ObjRange*)objHeader;
            return hashNum(objRange->from) ^ hashNum(objRange->to);
        case OT_STRING: //对于字符串,直接返回其hashCode
            return ((ObjString*)objHeader)->hashCode;
        default:
            RUN_ERROR("the hashable are objstring, objrange and class.");
    }
    return 0;
}

//根据value的类型调用相应的哈希函数
static uint32_t hashValue(Value value){
    switch(value.type){
        case VT_FALSE:
            return 0;
        case VT_NULL:
            return 1;
        case VT_NUM:
            return hashNum(value.num);
        case VT_TRUE:
            return 1;
        case VT_OBJ:
            return hashObj(value.objHeader);
        default:
            RUN_ERROR("unsupport type hashed!");
    }
    return 0;
}

//######part2 p129######
//在entries中添加entry,如果是新的key则返回true
static bool addEntry(Entry* entries, uint32_t capacity, Value key, Value value){
    uint32_t index = hashValue(key) % capacity;

    //通过开放探测法去找可用的slot
    while(true){
        //找到空闲slot,说明目前没有此key,直接赋值返回
        if(entries[index].key.type == VT_UNDEFINED){
            entries[index].key = key;
            entries[index].value = value;
            return true;    //新的key就返回true
        }else if(valueIsEqual(entries[index].key, key)){    //已经存在此key,更新值就行
            entries[index].value = value;
            return false;   //未添加新的key就返回false
        }
        //开放探测定址,直接探测下一个slot
        index = (index + 1) % capacity;
    }
}

//使对象objMap的容量调整到newCapacity
static void resizeMap(VM* vm, ObjMap* objMap, uint32_t newCapacity){
    //1.建立一个新的entry数组
    Entry* newEntries = ALLOCATE_ARRAY(vm, Entry, newCapacity);
    uint32_t idx = 0;
    while(idx < newCapacity){
        newEntries[idx].key = VT_TO_VALUE(VT_UNDEFINED);
        newEntries[idx].value = VT_TO_VALUE(VT_FALSE);
        idx++;
    }

    //2.再遍历老数组,把有值的部分插入到新数组
    if(objMap->capacity > 0){
        Entry* entryArr = objMap->entries;
        idx = 0;
        while(idx < objMap->capacity){
            //该slot有值
            if(entryArr[idx].key.type != VT_UNDEFINED){
                addEntry(newEntries, newCapacity, 
                        entryArr[idx].key, entryArr[idx].value);
            }
            idx++;
        }
    }

    //3.将老entry数组空间回收
    DEALLOCATE_ARRAY(vm, objMap->entries, objMap->count);
    objMap->entries = newEntries;   //更新指针为新的entry数组
    objMap->capacity = newCapacity; //更新容量
}

//######part3 p131######
//在objMap中查找key对应的entry
static Entry* findEntry(ObjMap* objMap, Value key){
    //如果objMap为空则返回NULL
    if(objMap->capacity == 0){
        return NULL;
    }

    //以下开放定址探测
    //用哈希值对容量取模计算槽位slot
    uint32_t index = hashValue(key) % objMap->capacity;
    Entry* entry;
    while(true){
        entry = &objMap->entries[index];

        //若该slot中的key恰好是要求的key则返回
        if(valueIsEqual(entry->key, key)){
            return entry;
        }

        //key为VT_UNDEFINED且value为VT_TRUE表示探测链未断,可以继续探测(代表从哈希表中删除掉的元素)
        //key为VT_UNDEFINED且value为VT_FALSE表示探测链断了,探测结束(代表哈希表中这个slot没有放入过元素)
        if(VALUE_IS_UNDEFINED(entry->key) && VALUE_IS_FALSE(entry->value)){
            return NULL;    //没有找到
        }

        //如果探测链没断就继续向下探测
        index = (index + 1) % objMap->capacity; 
    }
}

//在objMap中实现key与value的关联,objMap[key]=value
void mapSet(VM* vm, ObjMap* objMap, Value key, Value value){
    //当容量利用率达到80%时扩容
    if(objMap->count + 1 > objMap->capacity * MAP_LOAD_PERCENT){
        uint32_t newCapacity = objMap->capacity * CAPACITY_GROW_FACTOR;
        if(newCapacity < MIN_CAPACITY){
            newCapacity = MIN_CAPACITY;
        }
        resizeMap(vm, objMap, newCapacity); //扩容
    }

    //若创建了新的key则使objMap->count加1
    if(addEntry(objMap->entries, objMap->capacity, key, value)){
        objMap->count++;
    }
}

//从map中查找key对应的value map[key]
Value mapGet(ObjMap* objMap, Value key){
    Entry* entry = findEntry(objMap, key);
    if(entry == NULL){
        return VT_TO_VALUE(VT_UNDEFINED);
    }
    return entry->value;
}

//######part4 p133######
//回收objMap.entries所占的空间
void clearMap(VM* vm, ObjMap* objMap){
    DEALLOCATE_ARRAY(vm, objMap->entries, objMap->count);   //回收内存
    objMap->entries = NULL;         //初始化键值对
    objMap->capacity = objMap->count = 0;   //初始化容量和已使用量
}

//删除objMap中的key,返回map[key]
Value removeKey(VM* vm, ObjMap* objMap, Value key){
    Entry* entry = findEntry(objMap, key);

    if(entry == NULL){
        return VT_TO_VALUE(VT_NULL);
    }

    //设置开放定址的伪删除
    Value value = entry->value;
    entry->key = VT_TO_VALUE(VT_UNDEFINED);
    entry->value = VT_TO_VALUE(VT_TRUE);    //值为真,伪删除

    objMap->count--;
    if(objMap->count == 0){ //若删除后map为空就回收该空间
        clearMap(vm, objMap);
    } else if(objMap->count < objMap->capacity / (CAPACITY_GROW_FACTOR) * MAP_LOAD_PERCENT){
        //若map利用率太低就缩小map空间
        uint32_t newCapacity = objMap->capacity * CAPACITY_GROW_FACTOR;
        if(newCapacity < MIN_CAPACITY){
            newCapacity = MIN_CAPACITY;
        }
        resizeMap(vm, objMap, newCapacity);
    }

    return value;
}

代码来源 《基于c语言自制编程语言》 作者:郑钢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值