[题目解析]哈希表专题:1、8、30、31、88 | 豆包MarsCode AI刷题

57 阅读7分钟

1.找单独的数

题目描述

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。

要求

  1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
  2. 尽量减少额外空间的使用,以体现你的算法优化能力。

测试样例

样例1:

输入:cards = [1, 1, 2, 2, 3, 3, 4, 5, 5] 输出:4 解释:拿到数字 4 的同学是唯一一个没有配对的。

样例2:

输入:cards = [0, 1, 0, 1, 2] 输出:2 解释:数字 2 只出现一次,是独特的卡片。

样例3:

输入:cards = [7, 3, 3, 7, 10] 输出:10 解释:10 是班级中唯一一个不重复的数字卡片。

约束条件

  • 1 ≤ cards.length ≤ 1001
  • 0 ≤ cards[i] ≤ 1000
  • 班级人数为奇数
  • 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次

解决方案

根据班级人数为奇数除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次,我们可以想到用异或来解决该问题。因为两个相同的数进行异或运算的结果为0,那么如果把所有数都异或一遍,最后的结果就是我们要找的数。这个做法的时间复杂度为O(n)O(n),空间复杂度为O(1)O(1),符合题目的要求。

参考代码

public class Main {
    public static int solution(int[] inp) {
        // Edit your code here
        int res = 0;
        for (int x : inp) {
            res ^= x;
        }
        return res;
    }

    public static void main(String[] args) {
        // Add your test cases here
        
        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4);
        System.out.println(solution(new int[]{0, 1, 0, 1, 2}) == 2);
    }
}

8.找出整型数组中占比超过一半的数

问题描述

小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。

测试样例

样例1:

输入:array = [1, 3, 8, 2, 3, 1, 3, 3, 3] 输出:3

样例2:

输入:array = [5, 5, 5, 1, 2, 5, 5] 输出:5

样例3:

输入:array = [9, 9, 9, 9, 8, 9, 8, 8] 输出:9

解决方案

由于这个数字的出现次数超过了数字总数的一半,那么这个数字一定是该数字序列的中位数,故将这个数字序列进行排序取中位数即可。这个做法的时间复杂度为O(nlogn)O(nlogn)。当然,这个题目还有更优时间复杂度的做法,比如可以使用快速选择算法在O(n)O(n)的时间复杂度内得到中位数,或者使用一个指针和对应的计数器记录当前出现次数最多的数以及它的出现次数,遍历该数组序列即可,这个做法的时间复杂度也是O(n)O(n)。由于题目没有具体要求,故采用了一种写法简单的做法。

参考代码

import java.util.Arrays;

public class Main {
    public static int solution(int[] array) {
        // Edit your code here
        Arrays.sort(array);
        return array[array.length / 2];
    }

    public static void main(String[] args) {
        // Add your test cases here

        System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
    }
}

30.组成字符串ku的最大次数

问题描述

给定一个字符串 ss,该字符串中只包含英文大小写字母。你需要计算从字符串中最多能组成多少个字符串 "ku"。每次可以随机从字符串中选一个字符,并且选中的字符不能再使用。字符串中的字符大小写可以忽略,即大写和小写字母视为相同。

例如,输入 "AUBTMKAxfuu",从中最多能组成 1 个 "ku"

测试样例

样例1:

输入:s = "AUBTMKAxfuu" 输出:1

样例2:

输入:s = "KKuuUuUuKKKKkkkkKK" 输出:6

样例3:

输入:s = "abcdefgh" 输出:0

解决方案

由于每次可以随机从字符串中选一个字符,那么不用考虑字符的顺序问题,只需要考虑字符的数量问题。故,可以设两个计数器,分别记录字符k和字符u的出现次数,遍历该字符串即可得到,最后的结果便是两个计数器中的较小值。该做法的时间复杂度为O(n)O(n)

参考代码

public class Main {
    public static int solution(String s) {
        // write code here
        int num_k = 0, num_u = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == 'k' || c == 'K') {
                num_k++;
            } else if (c == 'u' || c == 'U') {
                num_u++;
            }
        }
        return Math.min(num_k, num_u);
    }

    public static void main(String[] args) {
        System.out.println(solution("AUBTMKAxfuu") == 1);
        System.out.println(solution("KKuuUuUuKKKKkkkkKK") == 6);
        System.out.println(solution("abcdefgh") == 0);
    }
}

31.不同整数的计数问题

问题描述

小R有一个字符串 word,该字符串由数字和小写英文字母组成。小R想用空格替换每一个不是数字的字符。然后,他希望统计在替换后剩下的整数中,不同整数的数目。

例如,给定字符串 "a123bc34d8ef34",替换后形成的字符串是 " 123 34 8 34",剩下的整数是 "123""34""8""34"。不同的整数有三个,即 "123""34""8"

注意,只有当两个整数的不含前导零的十进制表示不同,才认为它们是不同的整数。

测试样例

样例1:

输入:word = "a123bc34d8ef34" 输出:3

样例2:

输入:word = "t1234c23456" 输出:2

样例3:

输入:word = "a1b01c001d4" 输出:2

解决方案

由于这里是统计不同整数的数量,所以可以把所有整数加入到一个HashSet里面,那么HashSet的size就是不同整数的数量。具体做法:设一个变量用来存储当前遍历到的数字(因为数字可能有好几位),然后遍历字符串,如果当前字符是数字,那么进行统计;如果当前字符是字母,那么结束数字的统计,将数字加入到HashSet中。因为上述做法当碰到连续两个字母时会出错(即把0加入到HashSet中),所以可以设立一个标记变量,用来标记当前是否在记录数字。具体细节可以参考代码。上述做法的时间复杂度为O(n)O(n)

参考代码

import java.util.HashSet;

class Main {
    public static int solution(String word) {
        // write code here
        HashSet<Integer> S = new HashSet<>();
        int v = 0;
        boolean flag = false;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (c >= '0' && c <= '9') {
                v = v * 10 + c - '0';
                flag = true;
            } else {
                if (flag) {
                    S.add(v);
                    v = 0;
                    flag = false;
                }
            }
        }
        if (flag) {
            S.add(v);
        }
        return S.size();
    }

    public static void main(String[] args) {
        System.out.println(solution("a123bc34d8ef34") == 3);
        System.out.println(solution("t1234c23456") == 2);
        System.out.println(solution("a1b01c001d4") == 2);
    }
}

88.连续子串和的整除问题

问题描述

小M是一个五年级的小学生,今天他学习了整除的知识,想通过一些练习来巩固自己的理解。他写下了一个长度为 n 的正整数序列 a0,a1,...,an1a_0, a_1, ..., a_{n-1},然后想知道有多少个连续子序列的和能够被一个给定的正整数 b 整除。你能帮小M解决这个问题吗?

测试样例

样例1:

输入:n = 3,b = 3,sequence = [1, 2, 3] 输出:3

样例2:

输入:n = 4,b = 5,sequence = [5, 10, 15, 20] 输出:10

样例3:

输入:n = 5,b = 2,sequence = [1, 2, 3, 4, 5] 输出:6

解决方案

可以以第一个元素的下标为分类标准把连续字符列分成n类,即以0、1、2、...号元素开头,然后分别计算每一类中有多少个连续子序列的和能够被b整除。对于每一类的数量,以该元素为起点,往后遍历即可得到。上述做法的时间复杂度为O(n2)O(n^2)

参考代码

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static int solution(int n, int b, List<Integer> sequence) {
        // Please write your code here
        int res = 0;
        for (int i = 0; i < n; i++) {
            int tot = 0;
            for (int j = i; j < n; j++) {
                tot += sequence.get(j);
                if (tot % b == 0) {
                    res++;
                }
            }
        }
        return res;
    }

    public static void main(String[] args) {
        // You can add more test cases here
        List<Integer> sequence = new ArrayList<>();
        sequence.add(1);
        sequence.add(2);
        sequence.add(3);
        System.out.println(solution(3, 3, sequence) == 3);
    }
}