• leetcode 查找算法(三)


    接着leetcode 查找算法(二)写的


    滑动数组

    219 存在重复元素II

    题目描述

    给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

    示例 1:

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

    示例 2:

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

    示例 3:

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

    解题思路

    两种方法:1、暴力法 最容易想到 但是效率低

    2、采用滑动窗口k 用集合set存放key值,用K的大小限定set.size() 达到更快的查询速度

    方法一

    public boolean containsNearbyDuplicate(int[] nums, int k) {
        // 1 异常处理
        if (nums.length < 2) return false;
    
        // 2
        int n = nums.length;
        int count = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (Math.abs(i - j)  <= k && nums[i] == nums[j]) {
                    count++;
                }
            }
        }
    
        return count > 0;
    }
    

    复杂度分析

    • 时间复杂度:O(n2)
    • 空间复杂度:O(1)

    方法二

    public boolean containsNearbyDuplicate2(int[] nums, int k) {
        if (nums.length < 2) return false;
    
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (set.contains(nums[i])) return true;
            set.add(nums[i]);
            if (set.size() > k) {
                set.remove(nums[i - k]);
            }
        }
    
        return false;
    }
    

    复杂度分析

    • 时间复杂度:O(n)
    • 空间复杂度​:O(k) -- 存储set临时空间

    220. 存在重复元素 III

    题目描述

    在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。

    如果存在则返回 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

    解题思路

    方法一:暴力算法 通过 耗时703 ms,在所有 Java 提交中击败了5.07%的用户
    方法二:散列表 + 桶

    方法一

    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (nums.length < 2) return false;
    
        int count = 0;
        long tt = t;   // 不转换为long类型 会溢出
        int n = nums.length;
    
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (Math.abs(i - j) <= k && Math.abs(((long)nums[i] - (long)nums[j])) <= tt)
                    count++;
            }
        }
        return count > 0;
    }
    

    复杂度分析

    • 时间复杂度:O(n2)
    • 空间复杂度:O(1)

    方法二

    算法思想:参考讨论版

    private int sz;//桶大小
    
    public boolean containsNearbyAlmostDuplicate2(int[] nums, int k, int t) {
        if(t < 0)    return false; //绝对值不可能为负
        sz = t + 1;
        Map<Integer, Long> map = new HashMap<>(); //哈希表,桶号映射到值,值用Long防止溢出
        for(int i = 0; i < nums.length; i++)
        {
            int key = getID(nums[i]); //得到桶号
            if(map.containsKey(key)) // 同一个桶,差的绝对值必然满足要求
                return true;
            if(map.containsKey(key - 1) && Math.abs(map.get(key - 1) - nums[i]) <= t) //检查相邻桶
                return true;
            if(map.containsKey(key + 1) && Math.abs(map.get(key + 1) - nums[i]) <= t)
                return true;
            map.put(key, (long)nums[i]); //将该值放入桶中
            if(i >= k)    map.remove(getID(nums[i - k]));//为了始终满足下标的差的绝对值要求
        }
        return false;
    }
    //计算桶号
    private int getID(int num) {
        return (num < 0) ? num / sz - 1 : num / sz; //防止0号桶同时存在正负数干扰判断
    }
    

    复杂度分析

    • 时间复杂度:O(n)
    • 空间复杂度:O(n) -- map存储nums[i]元素,获取桶号

    二分查找

    35 搜索插入位置

    题目描述

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    你可以假设数组中无重复元素。

    示例 1:

    输入: [1,3,5,6], 5
    输出: 2

    示例 2:

    输入: [1,3,5,6], 2
    输出: 1

    示例 3:

    输入: [1,3,5,6], 7
    输出: 4

    示例 4:

    输入: [1,3,5,6], 0
    输出: 0

    解题思路

    本题既有查找又含插入,采用二分查找解决查找问题,时间复杂度为O(logn),采用一次遍历解决插入问题,最好时间复杂度为O(1)最坏为O(n),故最坏为O(n)

    代码

    public int searchInsert(int[] nums, int target) {
    
        // 1 当数组为空时,直接将target插入,返回第一个位置
        if (nums.length < 1) {
            return 0;
        }
    
        // 2 当数组中有1个及以上元素时
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        while (left <= right) {
            int middle = left + (right - left) / 2;
            if (nums[middle] == target) {
                return middle;
            } else if (nums[middle] < target) {
                left = middle + 1;
            } else if (nums[middle] > target) {
                right = middle - 1;
            }
        }
    
        // 走到这一步说明没查到,就需要进行插入操作
        for (int i = 0; i < n; i++) {
            if (nums[i] > target) {
                return i;
            }
        }
        return n;   // 遍历完在尾端插入
    }
    

    复杂度分析

    • 时间复杂度:O(n) + O(logn)
    • 空间复杂度:O(1)

    540 有序数组中的单一元素

    题目描述

    给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

    示例 1:

    输入: [1,1,2,3,3,4,4,8,8]
    输出: 2

    示例 2:

    输入: [3,3,7,7,10,11,11]
    输出: 10

    注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。

    解题思路

    法一:暴力法

    法二:因为是有序数组,所以可以用二分法。
    取数组中间的数,当中间数的下标为奇数,说明前后元素的个数为奇数,偶数则剩余个数为偶数。奇数时:当nums[h]等于[h+1],唯一数处于前h-1,反之处于后h+1。偶数时:当nums[h]等于[h+1],唯一数处于后h+2,反之处于前h.(就是要保证剩余查找元素个数奇数)

    方法一:暴力法

    public int singleNonDuplicate(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
        }
    
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                return entry.getKey();
            }
        }
        return 0;  // 题目中假设一定存在  所以此处不会执行
    }
    

    复杂度分析

    • 时间复杂度:O(n)
    • 空间复杂度:O(n)

    方法二:二分法

    public int singleNonDuplicate(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        int middle;
        while (left < right) {
            middle = left + (right - left) / 2;
            if ((middle&1)== 1) { // 下标为奇数
                if (nums[middle] == nums[middle+1]) {
                    right = middle - 1;
                } else {
                    left = middle + 1;
                }
            } else {   // 下标为偶数
                if (nums[middle] == nums[middle+1]) {
                    left = middle + 2;
                } else {
                    right = middle - 1;
                }
            }
    
        }
        return nums[left];  // 返回的一定是左侧的元素
    }
    

    复杂度分析

    • 时间复杂度:O(logn)
    • 空间复杂度:O(1)
  • 相关阅读:
    Java 中的 volatile 关键字
    Java 隐式锁
    导致并发程序出问题的根本原因是什么?
    BZOJ_2434_[Noi2011]阿狸的打字机_AC自动机+出栈入栈序+树状数组
    BZOJ_5368_[Pkusc2018]真实排名_组合数
    CF上的3道小题(2)
    BZOJ_4199_[Noi2015]品酒大会_后缀自动机
    BZOJ_4566_[Haoi2016]找相同字符_后缀自动机
    BZOJ_3172_[Tjoi2013]单词_后缀自动机
    BZOJ_3998_[TJOI2015]弦论_后缀自动机
  • 原文地址:https://www.cnblogs.com/guohaoblog/p/suan.html
Copyright © 2020-2023  润新知