【LeetCode】Map与Set相关笔试题

本文介绍了五个使用Map与Set解决的LeetCode笔试题目,包括找到只出现一次的数字、复制带随机指针的链表、宝石与石头问题、处理坏键盘打字问题以及找出前K个高频单词。解题过程中强调了Map和Set在存储、比较和排序上的关键作用,如使用TreeMap配合自定义比较器、利用toCharArray转换字符串以及Character.toUpperCase进行大小写转换等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、只出现一次的数字

只出现一次的数字链接题目描述
解题思路及代码

/**
     * 思路:
     * 方法一:
     * key-value模型,数字是key,出现次数是val
     * 将数组放入map中
     * 用map找到val为1的key
     *
     * 方法二:
     * 两个相同的数异或后结果为0
     * 任意数与0异或还是自己
     * 所有数异或结果就是只出现一次的数
     */
public class SingleNumber {
     public int singleNumber1(int[] nums) {
        Map<Integer,Integer> map = new TreeMap<>();
        for (int n : nums){
            int count = map.getOrDefault(n,0);
            map.put(n,count+1);
        }
        for (Map.Entry<Integer,Integer> e : map.entrySet()){
            int n = e.getKey();
            int c = e.getValue();
            if (c == 1){
                return n;
            }
        }
        return -1;
    }

    public int singleNumber2(int[] nums) {
        int r = 0;
        for (int n : nums){
            r = r ^ n;
        }
        return r;
    }
}

总结:

  1. 任意数异或自己本身,结果为0;
  2. 任意数异或0,结果不变;

2、复制带随机指针的链表

复制带随机指针的链表链接

题目描述
举例
解题思路及代码

 /**
     * 思路:
     * 方法一:
     * 按照链表的方式复制
     * 难点:(Random)指向找到新链表的节点(Random)指向
     * 1.将旧链表结点复制一份
     * 2.将新旧链表连起来
     * 3.根据旧链表的Random找新链表的Random
     * 4.结束后将新旧链表分开
     *
     * 方法二:
     * 通过Map保存新旧链表的映射关系
     * 旧链表中的结点作为key,新链表中的结点作为value;
     * 新链表node,旧链表cur
     * node.random = map.get(cur.random)
     * 解释:cur.random是key,get(cur.random)是key对应的value。
     */
public class CopyList {
   public Node copyRandomList(Node head){
        Node cur = head;
        while (cur != null){
            Node node = new Node();
            node.val = cur.val;

            node.next = cur.next;
            cur.next = node;
            cur = node.next;
        }
        cur = head;
        //复制random
        while (cur != null){
            if (cur.random != null) {
                cur.next.random = cur.random.next;
            }else {
                cur.next.random = null;
            }
            cur = cur.next.next;
        }
        cur = head;
        if (head == null){
            return null;
        }
        Node newHead = head.next;
        //拆新旧链表
        while (cur != null){
          Node node = cur.next;
          cur.next = node.next;
          if (node.next != null) {
              node.next = node.next.next;
          }
          cur = cur.next;
        }
        return newHead;
    }

    // 因为Map的key内部比较大小,得有Comparable或者比较器
    // Node作为引用数据类型里面都没有,所以无法比较
    // 写一个比较器
    private static class NodeComparator implements Comparator<Node>{

        @Override
        public int compare(Node o1, Node o2) {
            return o1.val - o2.val;
        }
    }
    public Node copyRandomList2(Node head){
        //要传入比较器
        Map<Node,Node> map = new TreeMap<>(new NodeComparator());
        Node newHead = null;
        Node newLast = null;
        Node cur = head;
        while (cur != null){
            Node node = new Node();
            node.val = cur.val;

            //node尾插到newHead
            if (newHead == null){
                newHead = node;
            }else {
                newLast.next = node;
            }
            newLast = node;

            map.put(cur,node);

            cur = cur.next;
        }

总结:
1.引用类型的的比较要使用Comparable或者Comparator
2.使用Map或Set时,若key的类型是引用数据类型,则需要写一个比较器传进去才能进行key的比较,例如:Map<Node,Node> map = new TreeMap<>(new NodeComparator());
3.如果使用自定义类型作为TreeMap的K类型
1)要不自定义类型是Comparable
2)否则,需要在构造方法中传入一个比较器

3、宝石与石头

宝石与石头链接
题目描述
解题思路及代码

  /**
     * 思路:
     * 将我拥有的石头放到set中去
     * 遍历宝石J集合,
     * 若set中包含了J中的宝石,count++
     */
public class JewelsInStones {
  public int numJewelsInStones(String J, String S) {
        //把所有宝石放到Set集合中
       Set<Character> jewels = new TreeSet<>();
       for (char j : J.toCharArray()){
           jewels.add(j);
       }
       //遍历所有的石头
        int count = 0;
        for (char s : S.toCharArray()){
           if (jewels.contains(s)){
               count++;
           }
        }
        return count;
    }
}

总结:
toCharArray()方法是将字符串转为字符数组;

4、坏键盘打字

坏键盘打字链接
题目描述
示例
解题思路及代码

 /**
     * 思路:
     * 将输入的错误字符串放到一个set中
     * 将要打印的坏掉的键放到一个Set类型的written中
     * 若set中不包含正确字符且written中也不包含
     * 则打印它的大写
     */
public class BrokenKeyboard {
   public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String right = scanner.nextLine();
        String wrong = scanner.nextLine();

        Set<Character> set = new TreeSet<>();
        for (char c : wrong.toCharArray()){
            set.add(c);
        }

        Set<Character> written = new TreeSet<>();
        for (char c : right.toCharArray()){
            if (! set.contains(c)){
                //字符转成大写
                c = Character.toUpperCase(c);
                /*
                if (c >= 'a' && c <= 'z'){
                    c += ('A'-'a');
                }
                */
                if (! written.contains(c)){
                    System.out.print(c);
                    written.add(c);
                }
            }
        }
        System.out.println();
    }
}

总结:
1.Character.toUpperCase()方法是将字母转为大写
2.将字母转为大写还可以:
c += ‘A’ - ‘a’;

5、前K个高频单词

前K个高频单词
题目描述举例
解题思路及代码

 /**
     * 思路:
     * 即出前k个单词,出现次数优先于字母顺序
     * 先统计每个单词出现的次数
     * 单词与次数形成映射关系,相同次数的单词放在一个数组中
     * 对次数进行排序,从大到小
     * 按次数取出k个单词
     */
public class TopKFrequent {
    //统计单词出现次数
    public static Map<String,Integer> calcCount(String[] words){
        Map<String,Integer> map = new HashMap<>();
        for (String w : words){
            int count =  map.getOrDefault(w,0);
            map.put(w,count+1);
        }
        return map;
    }
    //反转,得到每个次数下有多少个单词
    public static Map<Integer,List<String>> calcCountOfWords(Map<String,Integer> map){
        Map<Integer,List<String>> ret = new HashMap<>();
        for (Map.Entry<String,Integer> e : map.entrySet()){
            String word = e.getKey();
            int count = e.getValue();
            if (!ret.containsKey(count)){
                ret.put(count,new ArrayList<>());
            }
            ret.get(count).add(word);
        }
        return ret;
    }
    //对次数进行排序
    public static class IntegerComparator implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }
    public List<String> topKFrequent(String[] words, int k) {
        //获取每个单词出现次数
        Map<String,Integer> wordsCount = calcCount(words);
        //获取出现次数与单词的映射
        Map<Integer,List<String>> countToWordList = calcCountOfWords(wordsCount);
        //获取所有次数的集合
        Set<Integer> counts = countToWordList.keySet();
        //用比较器将次数从大到小排
        // 1.将counts放到一个数组中
        // 2.利用Collections接口的sort方法,进行排序(但为自然顺序、即升序)
        // 3.写一个比较器将count调整为降序
        List<Integer> countList = new ArrayList<>(counts);
        Collections.sort(countList,new IntegerComparator());
        /*
        Collections.sort(countList, new Comparator<Integer>() {
            //匿名类
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        */
        //取单词
        List<String> result = new ArrayList<>();
        int n = 0;
        int i = 0;
        while (n < k) {
            int count = countList.get(i);
            i++;
            List<String> ws = countToWordList.get(count);
            Collections.sort(ws);
            if (ws.size() <= k - n) {
                // 如果候选人数量小于需要人数,全部录取
                result.addAll(ws);
                n += ws.size();
            } else {
                // 否则,只录取需要的人数
                result.addAll(ws.subList(0, k - n));
                n += (k - n);
            }
        }
        return result;
    }
}

总结:
1.Collections.sort()默认是升序排序
2.当一些类只使用一次时可以用匿名类的形式。

 Collections.sort(countList, new Comparator<Integer>() {
            //匿名类
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值