【算法日记】Set 与 Map 经典算法

文章目录

1. 只出现一次的数字(LC136)

只出现一次的数字

题目描述

解题思路

  1. 使用异或运算符,将数组中所有数字都异或。相同的数字异或为0,剩余的就是出现一个的数字。
  2. 利用Set,当set中包含这个数字就移除,不包含就添加。遍历数组后剩余的数字就是出现一次的数字。(效率低,只是练习用)

代码实现

  • 解法一:
java 复制代码
    public int singleNumber(int[] nums) {
        int ret = 0;
        for(int i = 0;i<nums.length;i++){
            ret = ret^nums[i];
        }
        return ret;
    }
  • 解法二:
java 复制代码
 public int singleNumber(int[] nums) {
        Set <Integer> set = new HashSet<>();
        for(int i = 0;i<nums.length;i++){
            if(set.contains(nums[i]))
                set.remove(nums[i]);
            else 
                set.add(nums[i]);
        }
        for(int i = 0;i<nums.length;i++){
            if(set.contains(nums[i]))
                return nums[i];
        }
        return 0;
    }

2. 随机链表的复制(LC138)

随机链表的复制

题目描述

解题思路

  1. 利用Map建立旧节点和新节点的映射关系
  2. 对应关系:
    • cur初始表示旧节点的头结点
    • map.get(cur)表示对应的新节点;
    • cur.next表示旧节点的下一个节点;
    • map.get(cur.next)表示旧节点的下一个节点对应的新节点;
    • map.get(cur).next表示旧节点对应新节点的下一个节点。
  3. next和random关系类似

代码实现

java 复制代码
 class Node {
        int val;
        Node next;
        Node random;

        public Node(int val) {
            this.val = val;
            this.next = null;
            this.random = null;
        }
    }

    public Node copyRandomList(Node head) {
        Map<Node, Node> map = new HashMap<>();
        Node cur = head;
        //新建节点,建立映射关系
        while (cur != null) {
            Node node = new Node(cur.val);
            map.put(cur, node);
            cur = cur.next;
        }
        cur = head;
       
        //新节点复制旧节点的next和random关系
        while (cur != null) {
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(head);
    }

3. 宝石与石头(LC771)

宝石与石头

题目描述

解题思路

利用Set保存jewels中的字符;再利用计数器,遍历stones,如果set中包含该字符,则计数器自增。

代码实现

java 复制代码
 public int numJewelsInStones(String jewels, String stones) {
        Set<Character> set = new HashSet<>();
        int cnt = 0;
        for(int i = 0;i<jewels.length();i++){
            set.add(jewels.charAt(i));
        }
        for(int i = 0;i<stones.length();i++){
            if(set.contains(stones.charAt(i)))
                cnt++;
        }
        return cnt;
    }

4. 坏键盘

坏键盘

题目描述

解题思路

  1. 先把两个字符串都转换为大写字母;
  2. 遍历输出的字符串,利用Set保存输出的所有字符;
  3. 遍历输入字符串,如果set不包含当前字符,则说明这个字符键盘是坏的。坏字符可能输入多次,所以使用brokenSet保存已检测的坏字符。
  4. 如果set和brokenSet都不包含该字符,则输出。

代码实现

java 复制代码
 public void func(String a,String b){
        a = a.toUpperCase();
        b = b.toUpperCase();

        HashSet<Character> set = new HashSet<>();
        for(int i = 0;i<b.length();i++){
            set.add(b.charAt(i));
        }

        HashSet<Character> brokenSet = new HashSet<>();
        for(int i =0;i<a.length();i++){
            char ch = a.charAt(i);
            if(!set.contains(ch) && !brokenSet.contains(ch)){
                brokenSet.add(ch);
                System.out.print(ch);
            }
        }
    }

5. 前K个高频单词(LC692)

前K个高频单词

题目描述

解题思路

  1. 先利用Map建立起单词和频次的映射关系
  2. 题目要求找频次大的单词,因此建立小根堆,重写比较器
    • 每一次堆顶元素比较时,可以理解为"频次小的要出队,相同频次下序列靠后的要出队",因此频次小具有优先级,相同频次下,序列靠后具有优先级
  3. 遍历map,入队
    • 堆元素小于K时,全部入队
    • 堆元素大于等于K时:
      • 频次大于堆顶元素则入队
      • 频次与堆顶元素相同时,序列更小则入队
  4. 由于是小根堆,堆元素出队的顺序是频次由小到大,而题目要求由大到小,所以要调用reverse方法,逆序存放。

代码实现

java 复制代码
 public List<String> topKFrequent(String[] words, int k) {
        //1. 统计频率
        Map<String,Integer> map = new HashMap<>();
        for(String s:words){
            if(map.containsKey(s))
                map.put(s, map.get(s)+1);
            else
                map.put(s,1);

        }

        //2. 建立小根堆 (频次小的优先,相同频次下排序靠后的优先)
        PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(new Comparator<Map.Entry<String,Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                if(o1.getValue().equals(o2.getValue()))
                    return o2.getKey().compareTo(o1.getKey());
                return o1.getValue()-o2.getValue();
            }
        });

        //3. 遍历map
        for(Map.Entry<String,Integer> entry :map.entrySet()){
            //个数不足k时全部入队
            if(minHeap.size()<k)
                minHeap.offer(entry);

                //元素大于等于K时,逐个与堆顶元素比较
            else{
                Map.Entry<String,Integer> top = minHeap.peek();
                //频次大于堆顶元素则入队
                if(top.getValue().compareTo(entry.getValue())<0){
                    minHeap.poll();
                    minHeap.offer(entry);
                }
                //频次相等时,序列靠前入队
                else if(top.getValue().compareTo(entry.getValue())==0){
                    if(top.getKey().compareTo(entry.getKey())>0){
                        minHeap.poll();
                        minHeap.offer(entry);
                    }
                }
            }
        }
        //4. 把字符串放进List中
        List<String> ret = new ArrayList<>();
        while(!minHeap.isEmpty()){
            ret.add(minHeap.poll().getKey());
        }

        //5. 出队的序列是频次由小到大,调用reverse方法逆序排放
        Collections.reverse(ret);
        return ret;
    }
相关推荐
e疗AI产品之路2 小时前
心电分析诊断算法评估方法介绍
算法·心电分析
爱编码的傅同学2 小时前
【今日算法】LeetCode 11.盛水最多的容器 15.三数之和 283.移动0
数据结构·算法·leetcode
啊我不会诶2 小时前
Codeforces Round 1072 (Div. 3)补题
笔记·学习·算法
alphaTao2 小时前
LeetCode 每日一题 2026/1/19-2026/1/25
算法·leetcode
Swift社区2 小时前
LeetCode 382 链表随机节点
算法·leetcode·链表
老鼠只爱大米2 小时前
LeetCode经典算法面试题 #19:删除链表的倒数第N个结点(双指针、栈辅助法等多种实现方案详细解析)
算法·leetcode·链表·双指针·删除链表节点·一趟扫描
chao_7892 小时前
跳跃游戏系列【贪心算法】
python·算法·游戏·贪心算法·贪心
波波0072 小时前
每日一题:.NET 中什么是 LOH(大对象堆)?为什么频繁使用大数组或大字符串可能导致性能问题?如何优化?
java·jvm·算法
独自破碎E2 小时前
动态规划-正则表达式匹配
算法·正则表达式·动态规划