自定义实现 Trie、HashSet、LRU

这篇博客介绍了C++中自定义实现的三种数据结构:使用链地址法解决冲突的自定义HashSet,通过vector和链表实现;详细解析了自定义LRU缓存的实现过程;以及如何设计Trie树,强调了Trie树节点的指针数组特性用于存储字符串。
摘要由CSDN通过智能技术生成

C++中的hash table

  • C++ STL中有四种关联式容器, map ,set, multimap, multiset; 他们底层是RB树, 查找的时间复杂度是O(logN)
  • 他们都有对应的hash table版本, unordered_map, unordered_set, unordered_multimap, unordered_multiset. 他们底层的实现是hash table, 查找时间复杂度O(1).
    • 解决冲突的方法是 链地址法
    • 链地址法 (用一个链表存储所有冲突的记录), 开放地址法 (有冲突, 以当前位置为基准根据探查序列再寻址), 再哈希法

自定义HashSet

  • 一个vector, 每个容器都维持着一个链表, 初始化NULL
  • vector的下标就是对应的hash_key
  • vector存放的是每个链表的头节点, 每个函数都需要单独判断.
    • 删除操作, 需要双指针, 记录删除结点的前一个结点
class MyHashSet
{
public:
    struct ListNode
    {
        int val;
        ListNode* next;
        ListNode(int v): val(v), next(NULL){}
    };

    int range;
    vector<ListNode*> bucket_v;
    
    MyHashSet()
    {
        this->range = 769;
        bucket_v.assign(this->range, 0);
    }
    
    void add(int key)
    {
        int hash_key = key % this->range;
        ListNode* node = new ListNode(key);
        // 单独判断是否有头节点, 增加key的时候, 如果存在则不添加
        if (bucket_v[hash_key] == NULL) bucket_v[hash_key] = node;
        else
        {
            ListNode* temp = bucket_v[hash_key];
            if (temp->val == key) return;
            while(temp->next != NULL)
            {
                temp = temp->next;
                if (temp->val == key) return ;
            }
            temp->next = node;
        }
    }
    
    void remove(int key)
    {
        int hash_key = key % this->range;
        if (bucket_v[hash_key] == NULL) return ;
        else
        {
            ListNode* pre = bucket_v[hash_key];
            if (pre->val == key)
            {
                bucket_v[hash_key] = pre->next;
                return ;
            }
            ListNode* cur = pre->next;
            while(cur != NULL)
            {
                if (cur->val == key)
                {
                    pre->next = cur->next;
                    return ;
                }
                pre = cur;
                cur = cur->next;
            }
        }
    }
    
    bool contains(int key)
    {
        int hash_key = key % this->range;
        ListNode* cur = bucket_v[hash_key];
        while(cur != NULL)
        {
            if (cur->val == key)
                return true;
            cur = cur->next;
        }
        return false;
    }
};

自定义实现LRU

/* 最近最少使用缓存
 * 如何区别最少使用 : 顺序存储, 越久未使用越在末尾, 使用的时候调到开头
 * 因为插入, 删除需要O(1), 所以用链表存储
 * 查询也需要O(1), 哈希表
 * 两者结合 哈希链表
 */
class LRUCache
{
public:
    unordered_map<int, list<pair<int, int>>::iterator> mp;
    list<pair<int, int>> cache;
    int capacity;
    
    LRUCache(int val)
    {
        this->capacity = val;
    }
    
    int get(int key)
    {
        // 查找hash表看存不存在
        auto mp_iter = mp.find(key);
        if (mp_iter == mp.end())
            return -1;
        auto list_iter = mp_iter->second;
        // 因为查看, 也算是使用, 要把该结点移到开头, 也就是重新插入
        int val = list_iter->second;
        put(key, val);
        return val;
    }
    
    void put(int key, int val)
    {
        auto mp_iter = mp.find(key);
        // 如果存在, 则删除
        if (mp_iter != mp.end())
        {
            auto list_iter = mp_iter->second;
            // 删除hash表里的记录
            mp.erase(key);
            // 使用iterator删除list中的元素
            cache.erase(list_iter);
        }
        pair<int, int> p = make_pair(key, val);
        // 插入list, 更新map
        cache.push_front(p);
        mp[key] = cache.begin();
        if (cache.size() > capacity)
        {
            // 删除listc最后一个, 删除map对应元素
            pair<int, int> temp = cache.back();
            cache.pop_back();
            mp.erase(temp.first);
        }
    }
};

自定义实现Trie

  • Trie树的结点不同于一般树的结点, 一般树的结点有一个确认值; 但是Trie树结点是一个指针数组, 数组的下标依次对应26个字母. 当对应下标的指针不为空时, 表明这个结点是该元素.
  • 判断结尾时, 实际上是结尾字母所在结点的下一个结点为true
struct TrieNode
{
    bool is_end;
    TrieNode* links[26];
    TrieNode()
    {
        is_end = false;
        memset(links, 0, sizeof(links));
    }
};

class Trie
{
public:
    TrieNode* root;
    Trie()
    {
        root = new TrieNode();
    }
    
    void insert(string word)
    {
        TrieNode* node = root;
        for(char c : word)
        {
            if (node->links[c - 'a'] == NULL)
                node->links[c - 'a'] = new TrieNode();
            node = node->links[c - 'a'];
        }
        node->is_end = true;  // 此时node已经是结尾字母的下一个结点
    }
    
    bool search(string word)
    {
        TrieNode* node = root;
        for(char c : word)
        {
            if (node->links[c - 'a'] == NULL)
                return false;
            node = node->links[c - 'a'];
        }
        return node->is_end;
    }
    
    bool startsWith(string prefix)
    {
        TrieNode* node = root;
        for(char c : prefix)
        {
            if (node->links[c - 'a'] == NULL)
                return false;
            node = node->links[c - 'a'];
        }
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值