• [LeetCode 220.] 存在重复元素 III


    LeetCode 220. 存在重复元素 III

    一道medium题,但是不好想,想不到。

    题目描述

    给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
    如果存在则返回 true,不存在返回 false。

    示例 1:

    输入:nums = [1,2,3,1], k = 3, t = 0
    输出:true

    示例 2:

    输入:nums = [1,0,1,1], k = 1, t = 2
    输出:true

    示例 3:

    输入:nums = [1,5,9,1,5,9], k = 2, t = 3
    输出:false

    提示:

    • 0 <= nums.length <= 2 * 104
    • -231 <= nums[i] <= 231 - 1
    • 0 <= k <= 104
    • 0 <= t <= 231 - 1

    解题思路

    首先,直接暴力查找每个元素前后k个元素进行比较,暴力解法的时间复杂度是 O(nk)。超过这个复杂度的肯定不行。

    看到前后k个元素,第一时间想到了滑动窗口、单调队列,但是不对。单调队列给出的是最值,我们在这里想找的是区间里离当前数最近的值。
    每次都给窗口里k个数排序的话,时间复杂度 O(nklogk) 显然比暴力还慢。有没有办法每次增减元素,不需要全部重新排序一遍的?
    有,std::set 本身有序,插入删除只需要 O(logk) 时间,并且还支持 set<T>:::lower_bound(T) set<T>::upper_bound(T) 这种 O(logk) 的查询操作,这就是我们的所求了。

    参考代码

    注意int类型溢出的问题。

    /*
     * @lc app=leetcode id=220 lang=cpp
     *
     * [220] Contains Duplicate III
     */
    
    // @lc code=start
    class Solution {
    public:
    /*
        bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
            if (k == 0) return false;
    
            deque<pair<int,int>> mins; // <index, value>
            deque<pair<int,int>> maxs; // <index, value>
            int i = 0;
            for (; i <= k; i++) {
                if (nums.size() <= i) break; // guard
                while (!mins.empty() && mins.back().second > nums[i]) {
                    mins.pop_back();
                }
                mins.emplace_back(i, nums[i]);
                if (i != mins.front().first
                    && nums[i] - mins.front().second <= t) {
                        cout << i << nums[i] << endl;
                        return true;
                    }
    
                while (!maxs.empty() && maxs.back().second < nums[i]) {
                    maxs.pop_back();
                }
                maxs.emplace_back(i, nums[i]);
                if (i != maxs.front().first
                    && maxs.front().second - nums[i] <= t) {
                        cout << i << nums[i] << endl;
                        return true;
                    }
            }
            for (; i<nums.size(); i++) {
                if (mins.front().first + k < i) {
                    mins.pop_front(); // 过期
                }
                while (!mins.empty() && mins.back().second > nums[i]) {
                    mins.pop_back(); // 淘汰
                }
                mins.emplace_back(i, nums[i]);
                if (i != mins.front().first
                    && nums[i] - mins.front().second <= t) {
                        cout << i << nums[i] << endl;
                        return true;
                    }
    
                if (maxs.front().first + k < i) {
                    maxs.pop_front(); // 过期
                }
                while (!maxs.empty() && maxs.back().second < nums[i]) {
                    maxs.pop_back(); // 淘汰
                }
                maxs.emplace_back(i, nums[i]);
                if (i != maxs.front().first
                    && maxs.front().second - nums[i] <= t) {
                        cout << i << nums[i] << endl;
                        return true;
                    }
            }
            return false;
        } // WA,滑动窗口是不对的,因为会出现一个合理答案刚出来就被新的最值挤下去的情况,其实我们需要比较的不是一个数和窗口里最值的差值,而是它和窗口里最近原始的差值,这才是问题关键。
        // -> 思路来了,lower_bound / upper_bound
    */
        bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
            if (k == 0) return false;
            // set 不用担心重复元素,有重复元素一定return true了
            set<int64_t> st; // nums[i] - t 会溢出 int 范围
            for (int i=0; i<nums.size(); i++) {
                if (i >= k+1) {
                    st.erase(nums[i-k-1]);
                }
    
                auto lb = st.lower_bound(nums[i]); // 找比x大的
                if (lb != st.end() && *lb - nums[i] <= t) {
                    cout << *lb << ' ' << i << endl;
                    return true;
                }
                auto lb2 = st.lower_bound(int64_t(nums[i]) - t); // 找比x小的
                if (lb2 != st.end() && llabs(*lb2 - nums[i]) <= t) {
                    cout << *lb2 << ' ' << i << endl;
                    return true;
                }
    
                st.insert(nums[i]);
            }
            return false;
        }
    /*
    [2147483640,2147483641]
    1
    100
    expected: true
    
    []
    0
    0
    expected: false
    
    [-2147483640,-2147483641]
    1
    100
    expected: true
    // int 类型溢出
    */
    };
    // @lc code=end
    
  • 相关阅读:
    Android Opencore OpenMAX学习
    pkgconfig 设置
    pkgconfig 设置
    tlplayer for android,使用还是使用gles2渲染的 player
    CINRAD 天气雷达 介绍 总结
    sql 多字段求和并作为查询条件
    新一代多普勒天气雷达简介
    CINRAD雷达产品显示系统使用手册(1)
    CINRAD雷达产品显示系统使用手册(二)
    丽江新一代天气雷达介绍
  • 原文地址:https://www.cnblogs.com/zhcpku/p/15291346.html
Copyright © 2020-2023  润新知