LeetCode 219 Contains Duplicate II

题目

Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the absolute difference between i and j is at most k.

Example 1:

Input: nums = [1,2,3,1], k = 3
Output: true

Example 2:

Input: nums = [1,0,1,1], k = 1
Output: true

Example 3:

Input: nums = [1,2,3,1,2,3], k = 2
Output: false


解法思路(一)

  • 外层循环游标 i[0, nums.length - 1) 间遍历,内层循环在 [i + 1, k] 间遍历,暴力解法,当然可以,只不过时间复杂度是 O(N*K);
O(N) 时间复杂度的解法
  • 在遍历开始前,把滑动窗口中 k 个元素先存入一个查找表,由于窗口中的元素有可能是重复的,所以使用 HashMap 统计窗口中每个元素出现的频次;
  • 当游标 i 在遍历 [0, nums.length - 1) 的时候,只需要 O(1) 的时间就可以在 nums[i] 对应的窗口中判断出是否有与其相等的元素;

解法实现(一)

时间复杂度
  • O(N);
空间复杂度
  • O(K);
关键词

滑动窗口 哈希表 HashMap 元素可能重复的查询表 窗口滑动的边界判断

实现细节
  • k >= nums.length 这个边界的判断,nums.length 指向数组最后一个元素的后面,是数组外的位置,如果 k 的范围超过数组的边界,那么滑动窗口就缩小为最长到数组的边界 nums.length - 1
  • 至于 k 的精确语义,根据题目中给的例子可以确定;
package leetcode._219;

import java.util.HashMap;

public class Solution219_1 {

    public boolean containsNearbyDuplicate(int[] nums, int k) {

        HashMap windowK = new HashMap();

        for (int i = 1; i <= (k >= nums.length ? nums.length - 1 : k); i++) {
            if (windowK.containsKey(nums[i])) {
                windowK.put(nums[i], windowK.get(nums[i]) + 1);
            } else {
                windowK.put(nums[i], 1);
            }
        }

        for (int i = 0; i < nums.length - 1; i++) {
            if (windowK.containsKey(nums[i])) {
                return true;
            } else if(i + k + 1 < nums.length){
                if (windowK.containsKey(nums[i + k + 1])) {
                    windowK.put(nums[i + k + 1], windowK.get(nums[i + k + 1]) + 1);
                } else {
                    windowK.put(nums[i + k + 1], 1);
                }
            }
            if (windowK.containsKey(nums[i+1])) {
                windowK.put(nums[i + 1], windowK.get(nums[i + 1]) - 1);
                if (windowK.get(nums[i + 1]) == 0) {
                    windowK.remove(nums[i + 1]);
                }
            }
        }

        return false;
    }

    public static void main(String[] args) {
//        int[] arr = {1, 2, 3, 1}; int k = 3;
//        int[] arr = {1, 0, 1, 1}; int k = 1;
//        int[] arr = {1, 2, 3, 1, 2, 3}; int k = 2;
        int[] arr = {1}; int k = 1;
//        int[] arr = {99, 99}; int k = 2;
        boolean result = (new Solution219_1()).containsNearbyDuplicate(arr, k);
        System.out.println(result);
    }

}

解法思路(二)

  • 解法思路(一)是“找自己”的元素“追着”窗口跑,解法思路(二)是“找自己”的元素“领着”窗口跑;

解法实现(二)

时间复杂度
  • O(n);
空间复杂度
  • O(k);
关键字

哈希表 HashSet

k 的几何意义
  • “找自己”的元素最多在距离自己第 k 个的位置“找到自己”;
  • 滑动窗口的长度是 k
关于滑动窗口
  • 滑动窗口的长度是 k
  • 滑动窗口的起点是“找自己”的后一个元素;
  • 滑动窗口的终点是距“找自己”第 k 远的元素;
  • 如果滑动窗口被撑到最长,也没能在其中找到“自己”,那么就要缩短滑动窗口,让距“找自己”第 k 个元素“出离”滑动窗口,并将“找自己”的元素纳入滑动窗口,与滑动窗口右邻接的元素成为新的“找自己”的元素;
关于滑动窗口中的元素为什么可以用 HashSet 盛装
  • 滑动窗口的长度最大为 k,滑动窗口被撑到最大只有当滑动窗口中没有相等的元素,如果滑动窗口在没有被撑到最大的时候,出现了一个滑动窗口中有的元素想进入滑动窗口,那它是进不去的,因为它已经找到了“自己”,算法运行结束;
package leetcode._219;

import java.util.HashSet;

// 219. Contains Duplicate II
// https://leetcode.com/problems/contains-duplicate-ii/description/
// 时间复杂度: O(n)
// 空间复杂度: O(k)
public class Solution219_2 {

    public boolean containsNearbyDuplicate(int[] nums, int k) {

        if(nums == null || nums.length <= 1)
            return false;

        if(k <= 0)
            return false;

        HashSet record = new HashSet();
        for(int i = 0 ; i < nums.length; i ++){
            if(record.contains(nums[i]))
                return true;

            record.add(nums[i]);
            if(record.size() == k + 1)
                record.remove(nums[i-k]);
        }

        return false;
    }

    private static void printBool(boolean b){
        System.out.println(b ? "True" : "False");
    }

    public static void main(String[] args) {

        int[] nums = {1, 2, 1};
        int k = 1;
        printBool((new Solution219_2()).containsNearbyDuplicate(nums, k));
    }

}

返回 LeetCode [Java] 目录

你可能感兴趣的:(LeetCode 219 Contains Duplicate II)