当前位置: 首页 > article >正文

【数据结构与算法】LeetCode:哈希表

文章目录

  • 哈希表
    • 一般查找 (键查找)
      • 两数之和 (Hot 100)
      • 两个数组的交集
      • 快乐数
      • 环形链表 (Hot 100)
      • 环形链表 II (Hot 100)
      • 和为 K 的子数组 (Hot 100)
    • 计数查找 (值查找)
      • 有效的字母异位词
      • 字符串中的第一个唯一字符
      • 只出现一次的数字 (Hot 100)
      • 多数元素
    • 键、值查找
      • 两个数组的交集 II
    • 其他
      • 字母异位词分组
      • 最长连续序列
      • LRU 缓存 (Hot 100)
      • 复制带随机指针的链表

哈希表

一般查找 (键查找)

两数之和 (Hot 100)

两数之和

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
   
         unordered_map<int, int> map;  // <数值,下标>
         for(int i = 0; i < nums.size(); i++){     // 对于每个数字nums[i]
            auto it = map.find(target - nums[i]);  // 查找target - nums[i]
            if(it != map.end()){ 
                return {it->second, i};  // {target - nums[i]的索引,nums[i]的索引}
            }
            map[nums[i]] = i;   

         }
         return {};
        
    }
};

两个数组的交集

两个数组的交集

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result;
		// num1的元素集合
        unordered_set<int> nums1_set(nums1.begin(), nums1.end());
        for(auto num:nums2){  // 找出nums2在nums1中出现的元素
            if (nums1_set.count(num)){
                result.insert(num);
            }
        }
        return vector<int>(result.begin(), result.end());

    }
};

快乐数

快乐数

class Solution {
public:
   // 计算一个数的各位数字的平方和  
    int getSquareSum(int n) {  
        int sum = 0;  
        while (n > 0) {  
            int digit = n % 10; 
            sum += digit * digit;
            n /= 10;
        }  
        return sum;  
    }  
    
    // 判断一个数是否为快乐数  
    bool isHappy(int n) {  
        std::unordered_set<int> seen;     // 使用集合来存储已经出现过的数  
        while (n != 1 && !seen.count(n)){ // 当 n 不为 1 且 n 没有出现过时  
            seen.insert(n);               
            n = getSquareSum(n);          
        }  
        return n == 1;                    // 如果 n 最终为 1,则为快乐数
    }    
};

环形链表 (Hot 100)

环形链表

class Solution {
public:
    bool hasCycle(ListNode *head) {
        unordered_set<ListNode*> visited;
        ListNode * cur = head;
        while (cur != nullptr) {
            if (visited.count(cur)) {
                return true;
            }
            visited.insert(cur);
            cur = cur->next;
        }
        return false;
    }
};

环形链表 II (Hot 100)

环形链表 II

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode *> visited;
        ListNode* cur = head;
        while (cur != nullptr) {// 遍历链表中的每个节点,并将它记录下来
            if (visited.count(cur)) { // 一旦遇到了此前遍历过的节点,就可以判定链表中存在环。
                return cur;
            }
            visited.insert(cur);
            cur = cur->next;
        }
        return nullptr;
    }
};

和为 K 的子数组 (Hot 100)

和为 K 的子数组

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int ans = 0;
        int pre = 0; // 前缀和,子数组[num[j]..num[i]] 和为k 即 pre[i]−pre[j−1]==k
        unordered_map<int, int> map_pre_num; // 存储每个前缀和出现的次数。
        map_pre_num[0] = 1;

        for (auto& num : nums) {
            pre += num; // 计算前缀和
            // 判断是否已存在pre - k的前缀和,
            // 如果存在,说明之前有map_pre_num[pre - k]个位置到当前位置的连续子数组的和为k
            if (map_pre_num.find(pre - k) != map_pre_num.end()) {
                ans += map_pre_num[pre - k];
            }
            map_pre_num[pre]++;
        }
        return ans;
    }
};

计数查找 (值查找)

有效的字母异位词

有效的字母异位词(计数对象可数)

class Solution {
public:
    bool isAnagram(string s, string t) {
    	// 使用哈希表来统计两个字符串各个字符出现次数的差值。
    	// 如果差值都为0,则为字母异位词 
    	// map需要更多的开销,因此优先使用数组
        vector<int>vec(26,0);
        for(auto c: s) vec[c - 'a']++;
        for(auto c: t) vec[c - 'a']--;
        // 查找数量不同的字母,有则表示s和t不是字母异位词
        for (auto t: vec){  
            if(t!=0) return false;
        }
        return true;
    }
};

字符串中的第一个唯一字符

字符串中的第一个唯一字符(计数对象可数):

class Solution {
public:
    int firstUniqChar(string s) {
        std::unordered_map<char, int> map; 
        // 统计每个字符的出现次数  
        for (char c : s) map[c]++;
        // 遍历字符串,找到第一个只出现一次的字符  
        for (int i = 0; i < s.size(); i++) {  
            if (map[s[i]] == 1) return i;   
        }  
    
        return -1; 
    }

};

只出现一次的数字 (Hot 100)

只出现一次的数字(计数对象不可数):

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int, int> map;

        // 统计每个字符的出现次数  
        for (int num : nums) {  
        	map[num]++;  
    	}  
   
        // 查找只出现一次的元素
        for(auto item: map){
            if(item.second == 1) return item.first;
        }
        return NULL;
    }
};

多数元素

多数元素

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        unordered_map<int, int> counts;
        int ans = 0, max_count = 0;
        for (int num: nums) {
           counts[num]++;
            if (counts[num] > max_count) { // 查找最多的数
                ans = num;
                max_count = counts[num];
            }
        }
        return ans;
    }
};

键、值查找

两个数组的交集 II

两个数组的交集 II

class Solution {
public:
	vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {  
	    unordered_map<int, int> map;  
	    vector<int> result;  // 元素可重复
	  
	    // 记录nums1每个元素出现的次数  
	    for (int num : nums1) map[num]++;   

	    // 遍历 nums2,如果元素在哈希表中且计数大于零,则添加到结果集中并减少计数  
	    // 第一个条件(键):检查 num 是否存在于哈希表 map 中
	    // 第二个条件(值):确保只将在 nums1 中出现过(即计数大于零)的元素添加到结果集中,
	    for (int num : nums2) {  
	        if (map.find(num) != map.end() && map[num] > 0) {  
	            result.push_back(num);  
	            map[num]--;  // 确保各个元素添加的次数不超过它们在 nums1 中出现次数
	        }  
	    }  
	  
	    return result;  
	}  
};

其他

字母异位词分组

字母异位词分组

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> mp;
        //  不同词排序后的字母集是相同的,设为键。字母组成相同的词设为值。
        for (string& str: strs) {
            string key = str;
            sort(key.begin(), key.end()); // 字母集
            mp[key].emplace_back(str);    // <字母集,词>
        }
        vector<vector<string>> ans;
        for (auto it: mp) {
            ans.emplace_back(it.second);
        }
        return ans;
    }
};

最长连续序列

最长连续序列

class Solution{
public:
    int longestConsecutive(vector<int>& nums){
        unordered_set<int> set;
        for(auto num:nums) set.insert(num);

        int max_length = 0;
        for(auto num: set){
            if(!set.count(num - 1)){ // 最长连续序列的开头值(最小值)x
                int cur_length = 1; 
                // 以x为起点,不断尝试匹配 x+1,x+2,⋯ 是否存在
                while(set.count(num+1)){
                    num++;
                    cur_length++;
                } 
                max_length = max(max_length, cur_length);
            }
        }
        return max_length;
    }
};

LRU 缓存 (Hot 100)

LRU 缓存

struct DlinkedNode{
    int key, value;
    DlinkedNode* prev;
    DlinkedNode* next;
    DlinkedNode(int _key = 0, int _value = 0 ): key(_key),value(_value), prev(nullptr),next(nullptr){};
};
class LRUCache {
private:
    unordered_map<int, DlinkedNode*> cache;
    DlinkedNode* head; // 伪头
    DlinkedNode* tail; // 伪尾
    int size = 0;     // 当前缓存的节点数量
    int capacity;     // 容量
public:
    LRUCache(int _capacity):capacity(_capacity) {
        // 伪头部和伪尾部节点
        head = new DlinkedNode();
        tail = new DlinkedNode();
        head->next = tail;
        tail->prev = head;
    }
    ~LRUCache() {  
        // 析构函数,清理资源  
        DlinkedNode* curr = head->next;  
        while (curr != tail) {  
            DlinkedNode* temp = curr;  
            curr = curr->next;  
            delete temp;  
        }  
        delete head;  
        delete tail; 
    } 

    void put(int key, int value) {
	    // 如果关键字 key 不存在,则向缓存中插入该组 key-value 。
	    // 如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。
        if(!cache.count(key)){
            DlinkedNode* node = new DlinkedNode(key, value);
            cache[key] = node;
            addToHead(node);
            size++;
            if(size > capacity) removeTail();
        }else{ // 如果关键字 key 已经存在,则变更其数据值 value,并移动到头部
            DlinkedNode* node = cache[key];
            node->value = value;
            addToHead(node, true);
        }
    }
    int get(int key) {
        if(!cache.count(key)) return -1; // key不存在
         // 如果 key 存在,先通过哈希表定位,再移到头部
        DlinkedNode* node = cache[key];
        addToHead(node, true);// 尾部是最久未使用
        return node->value;
        
    }
    void addToHead(DlinkedNode* node, bool exit = false){
        if(exit){
            node->prev->next = node->next;
            node->next->prev = node->prev;
        }
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    void removeTail(){
        DlinkedNode* node = tail->prev;
        node->prev->next = node->next;
        node->next->prev = node->prev;
        // 删除哈希表中对应的项
        cache.erase(node->key);
        delete node;
        size--;
    }
};

复制带随机指针的链表

复制带随机指针的链表

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        Node* cur = head;
        unordered_map<Node*, Node*> map; // <原节点,复制的新节点>
        // 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        while(cur != nullptr) {
            Node* new_node =  new Node(cur->val);
            map[cur] = new_node; // 原节点 -> 新节点
            cur = cur->next;
        }
        cur = head;
        // 构建新链表的 next 和 random 指向
        while(cur != nullptr) {
            // 值为新节点,键为旧节点
            // 当前旧节点对应的新节点的next 指向 当前旧节点的next指向的节点对应的新节点
            map[cur]->next = map[cur->next];
             // 当前旧节点对应的新节点的random 指向 当前旧节点的random指向的节点对应的新节点
            map[cur]->random = map[cur->random];
            cur = cur->next;
        }
        // 返回新链表的头节点
        return map[head];
    }
};

http://www.kler.cn/news/314557.html

相关文章:

  • Alinx MPSoC驱动开发第17章I2C实验修改设备树后petalinux编译报错
  • 分布式Id生成策略-美团Leaf
  • 使用python对图像批量水平变换和垂直变换
  • 深度学习参数管理
  • MySQL-DDL/DML(数据定义/操作语言)
  • GIS开发之如何使用OpenLayers,Leaflet,Mapbox,Cesium,ArcGIS, Turf.js 与 D3.js
  • 【Webpack--00802】配置Babel语法兼容
  • 【图像检索】基于Gabor特征的图像检索,matlab实现
  • Python面试宝典第50题:分割等和子集
  • Vscode、插件历史版本下载
  • [数据结构与算法·C++] 笔记 1.4 算法复杂性分析
  • [附源码]SpringBoot+VUE+Java实现人脸识别系统
  • 实战指南:深度剖析Servlet+JSP+JDBC技术栈下的用户CRUD操作
  • 探秘 Web Bluetooth API:连接蓝牙设备的新利器
  • 828华为云征文|Flexus X实例GitLab部署构建流水线-私人一体化代码仓库~
  • AWS账号可以共用吗?
  • vue 中互相不关联的两个组件怎么进行通信(数据传输)
  • MFC获取网页的html文本
  • 视频V4改进
  • 锐捷 睿易路由器存在RCE漏洞
  • 会声会影2025视频剪辑教学
  • 开源集成开发环境搭建之VSCode安装部署教程
  • MySQL:基本查询操作
  • java计算机毕设课设—土地档案管理系统(附源码、文章、相关截图、部署视频)
  • 基于Java的SSM(Spring、Spring MVC、MyBatis)框架构建的远程诊断系统
  • 论文阅读 - MDFEND: Multi-domain Fake News Detection
  • 探索iPhone一键删除重复照片的方法
  • Kafka 为什么这么快?
  • 某乐指数爬虫逆向分析
  • Qemu开发ARM篇-2、uboot交叉编译