LeetCode面试热题十四(完结篇)

380. O(1) 时间插入、删除和获取随机元素

实现RandomizedSet 类:

  • RandomizedSet() 初始化 RandomizedSet 对象
  • bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
  • bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
  • int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。

你必须实现类的所有函数,并满足每个函数的平均时间复杂度为 O(1) 。

示例:

输入
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", 
"insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]
解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。

提示:

  • -231 <= val <= 231 - 1
  • 最多调用 insert、remove 和 getRandom 函数 2 * 105
  • 在调用getRandom方法时,数据结构中至少存在一个元素。

解题思路:为了能在O(1)内完成insert,romove,考虑使用哈希表,但使用哈希表没有办法实现 getRandom。使用 数组可以实现getRandom,考虑结合哈希表和数组实现。使用hashMap用来存储值和值在数组中的位置(下标),key用来存储值,value用来存储下标。

class RandomizedSet {
    HashMap<Integer,Integer> map;
    List<Integer> list;
    Random random;
    // 初始化哈希表和数组集合
    public RandomizedSet() {
        map = new HashMap<>();
        list = new ArrayList<>();
        random = new Random();
    }
    public boolean insert(int val) {
        if(map.containsKey(val)){
        	// 已经有了,插入失败
            return false;
        }
        // 在哈希表中和数组中分别插入
        map.put(val,list.size());
        list.add(list.size(),val);
        return true;
    }
    public boolean remove(int val) {
        if(!map.containsKey(val)){
        	// 不存在,删除失败
            return false;
        }
        // 为了在数组中O(1)完成删除,将最后一个元素的值放在要删除的位置,删除最后一个位置即可
        int index = map.get(val);        
        int last = list.get(list.size()  -  1);
        list.set(index,last);
        // 更新last的value值
        map.put(last,index);
        map.remove(val);
        list.remove(list.size() - 1);
        return true;
    }
    public int getRandom() {
    	// 使用 random,得到一个0-list.size之间的值,返回
        return list.get(random.nextInt(list.size()));
    }
}

384. 打乱数组

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。
实现 Solution class:

  • Solution(int[] nums) 使用整数数组 nums 初始化对象
  • int[] reset() 重设数组到它的初始状态并返回
  • int[] shuffle() 返回数组随机打乱后的结果

示例:

输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]
解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle();    // 打乱数组 [1,2,3] 并返回结果。
					   // 任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset();      // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle();    // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

提示:

  • 1 <= nums.length <= 200
  • -106 <= nums[i] <= 106
  • nums 中的所有元素都是 唯一的
  • 最多可以调用 5 * 104 次 reset 和 shuffle

解题思路:在类中保存一份原始的数组,在调用reset方法时,直接返回,在shuffle方法中,采用抽奖的思路在数组中随机抽出一个数,抽出后,从奖池删除,然后继续抽奖,直到奖池为空。

class Solution {
    // 存储原始数组
    int[] meta;
    Random random;
    public Solution(int[] nums) {
        meta = Arrays.copyOf(nums,nums.length);
        random = new Random();
    }
    public int[] reset() {
        return meta;
    }
    public int[] shuffle() {
        int len = meta.length;
        int[] tmp = Arrays.copyOf(meta,len);
        int[] shuffle = new int[len];
        for(int i = 0;i < len;i++){
            // 抽奖
            int index = random.nextInt(len - i);
            shuffle[i] = tmp[index];
            // 把这个数从奖池中去除
            tmp[index] = tmp[len - i - 1];
        }
        return shuffle;
    }
}

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

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

示例:

s = "leetcode"
返回 0
s = "loveleetcode"
返回 2

解题思路:哈希表,第一次遍历统计所有字符出现的频数,第二次遍历进行判断频数是否为1。

public int firstUniqChar(String s) {
    int[] nums = new int[26];
    // 统计频数
    for(int i = 0;i < s.length();i++){
        nums[s.charAt(i)-'a']++;
    }
    for(int i = 0;i < s.length();i++){
    	// 判断频数是否为1。
        if(nums[s.charAt(i)-'a'] == 1){
            return i;
        }
    }
    return -1;
}

395. 至少有 K 个重复字符的最长子串

给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

示例:

输入:s = "ababbc", k = 2
输出:5
解释:最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。

提示:

  • 1 <= s.length <= 104
  • s 仅由小写英文字母组成
  • 1 <= k <= 105

解题思路:找出所有在s串中出现次数不足k次的字符,将 s串分割为若干子串,然后对子串继续分割,直到所有子串中的字符出现次数都大于等于k次,记录子串的长度。结果为所有符合条件子串的最大长度。

public int longestSubstring(String s, int k) {
    int n = s.length();
    // 递归对s串进行切割统计最大长度
    return dfs(s, 0, n - 1, k);
}
public int dfs(String s, int l, int r, int k) {
    int[] cnt = new int[26];
    // 统计区间内所有字符出现的次数
    for (int i = l; i <= r; i++) {
        cnt[s.charAt(i) - 'a']++;
    }
    // 找出出现次数小于k的字符,进行串切割
    char split = 0;
    for (int i = 0; i < 26; i++) {
        if (cnt[i] > 0 && cnt[i] < k) {
            split = (char) (i + 'a');
            break;
        }
    }
    if (split == 0) {
    	// 没有出现次数小于k的字符。返回子串的长度
        return r - l + 1;
    }
    int i = l;
    int ret = 0;
    // 子串切割
    while (i <= r) {
    	// 去除头部不合格的字符
        while (i <= r && s.charAt(i) == split) {
            i++;
        }
        if (i > r) {
            break;
        }
        // 找到子串起始位置
        int start = i;
        // 找出子串终止位置
        while (i <= r && s.charAt(i) != split) {
            i++;
        }
        // 将子串继续切割
        int length = dfs(s, start, i - 1, k);
        // 记录最终结果
        ret = Math.max(ret, length);
    }
    return ret;
}

412. Fizz Buzz

给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:

  • answer[i] == “FizzBuzz” 如果 i 同时是 3 和 5 的倍数。
  • answer[i] == “Fizz” 如果 i 是 3 的倍数。
  • answer[i] == “Buzz” 如果 i 是 5 的倍数。
  • answer[i] == i (以字符串形式)如果上述条件全不满足。
输入:n = 15
输出:["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz",
	  "11","Fizz","13","14","FizzBuzz"]

提示:
1 <= n <= 104

解题思路:从1到n遍历,逐个判断,将结果加入集合。

public List<String> fizzBuzz(int n) {
    List<String> list = new ArrayList<>();
    for(int i = 1;i <= n;i++){
        if(i%15 == 0){
            list.add("FizzBuzz");
        }else if(i%3 == 0){
            list.add("Fizz");
        }else if(i%5 == 0){
            list.add("Buzz");
        }else{
            list.add(String.valueOf(i));
        }
    }
    return list;
}

454. 四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例 :

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0]+nums2[0] + nums3[0]+nums4[1]=1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1]+nums2[1] + nums3[0]+nums4[0]=2 + (-1) + (-1) + 0 = 0

解题思路:将四个数组分为两个部分,将nums1与nums2所有元素两两相加,结果放入哈希表中。计算nums3与nums4所有元素两两相加,在哈希表中寻找相加结果的负值,如果有,就找到了一组四元组。

public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
	// 使用hashMap,因为相加的值可能重复,同时记录次数
    HashMap<Integer,Integer> map = new HashMap<>();
    int len = nums1.length;
    for(int i = 0;i < len;i++){
        for(int j = 0;j < len;j++){
        	// 将nums1和nums2的元素两两相加结果放入哈希表
            map.put(nums1[i]+nums2[j],map.getOrDefault(nums1[i]+nums2[j],0) + 1);
        }
    }
    int ans = 0;
    // 计算另一组的结果,在哈希表中寻找。
    for(int i = 0;i < len;i++){
        for(int j = 0;j < len;j++){
            ans += map.getOrDefault(-nums3[i] - nums4[j],0);
        }
    }
    return ans;
}

此系列更新完结,共 136个题,是 LeetCode 精选 TOP 面试题中的所有题的题解(不包括会员题)
LeetCode 精选 TOP 面试题链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值