LeetCode刷题(七)-----数组-------easy部分(Java、C++)

LeetCode刷题(七)-----数组-------easy部分(Java、C++)

219. 存在重复元素 II

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

示例1:
在这里插入图片描述
示例2:
在这里插入图片描述
示例3:
在这里插入图片描述
思路一:
方法一 (线性搜索) 【超时】
思路:将每个元素与它之前的k个元素中比较查看它们是否相等。
算法:这个算法维护了一个k大小的滑动窗口,然后在这个窗口里面搜索是否存在跟当前元素相等的元素。
在这里插入图片描述
时间复杂度分析
时间复杂度:O(nmin(k,n))
每次搜索都要花费 O(min(k,n)) 的时间,哪怕k比n大,一次搜索中也只需比较 n次。
空间复杂度:O(1)
方法二 (二叉搜索树) 【超时】
思路:通过自平衡二叉搜索树来维护一个k大小的滑动窗口。
算法
这个方法的核心在于降低方法一中搜索前 k个元素所耗费的时间。可以想一下,我们能不能用一个更复杂的数据结构来维持这个k大小的滑动窗口内元素的有序性呢?考虑到滑动窗口内元素是严格遵守先进先出的,那么队列会是一个非常自然就能想到的数据结构。链表实现的队列可以支持在常数时间内的 删除,插入,然而 搜索 耗费的时间却是线性的,所以如果用队列来实现的话结果并不会比方法一更好。

一个更好的选择是使用自平衡二叉搜索树(BST)。BST 中搜索,删除,插入都可以保持O(logk) 的时间复杂度,其中k是 BST 中元素的个数。在大部分面试中你都不需要自己去实现一个 BST,所以把 BST 当成一个黑盒子就可以了。大部分的编程语言都会在标准库里面提供这些常见的数据结构。在 Java 里面,你可以用 TreeSet 或者是 TreeMap。在 C++ STL 里面,你可以用 std::set 或者是 std::map。

假设你已经有了这样一个数据结构,伪代码是这样的:
遍历数组,对于每个元素做以下操作:
在 BST 中搜索当前元素,如果找到了就返回 true。
在 BST 中插入当前元素。
如果当前 BST 的大小超过了 kk,删除当前 BST 中最旧的元素。
返回 false。
在这里插入图片描述
复杂度分析
时间复杂度:O(nlog(min(k,n)))

我们会做n次搜索,删除,插入 操作。每次操作将耗费对数时间,即为 log(min(k,n))。注意,虽然k可以比n大,但滑动窗口大小不会超过n。
空间复杂度:O(min(n,k))
只有滑动窗口需要开辟额外的空间,而滑动窗口的大小不会超过 O(min(n,k))。
注意事项
这个算法在n和k很大的时候依旧会超时。

方法三 (散列表) 【通过】
思路:用散列表来维护这个k大小的滑动窗口。
算法:在之前的方法中,我们知道了对数时间复杂度的 搜索 操作是不够的。在这个方法里面,我们需要一个支持在常量时间内完成 搜索,删除,插入 操作的数据结构,那就是散列表。这个算法的实现跟方法二几乎是一样的。
遍历数组,对于每个元素做以下操作:
在散列表中搜索当前元素,如果找到了就返回 true。
在散列表中插入当前元素。
如果当前散列表的大小超过了k, 删除散列表中最旧的元素。
返回 false。
在这里插入图片描述
复杂度分析
时间复杂度:O(n)
我们会做 n次搜索,删除,插入操作,每次操作都耗费常数时间。
空间复杂度:O(min(n,k))
开辟的额外空间取决于散列表中存储的元素的个数,也就是滑动窗口的大小 O(min(n,k))。
作者:LeetCode
链接:https://leetcode-cn.com/problems/contains-duplicate-ii/solution/cun-zai-zhong-fu-yuan-su-ii-by-leetcode/
思路二:
思路
标签:哈希
维护一个哈希表,里面始终最多包含 k 个元素,当出现重复值时则说明在 k 距离内存在重复元素
每次遍历一个元素则将其加入哈希表中,如果哈希表的大小大于 k,则移除最前面的数字
时间复杂度:O(n),n为数组长度
代码
在这里插入图片描述
思路三:
思路:此题依然可以采取哈希表的方式,只不过这次哈希表映射的东西变了,我们可以采取映射每个数字的位置,有人可能会问,对于相同数字,怎么映射他们的位置呢?其实大可不必担心,因为本题找的是两个位置差的小于k,所以我们每遍历一个数字就去找是否map中存在,如果存在就判断map中存的那个数字出现的位置和当前位置之差,如果小于k就返回true。进行该操作后,我们还需要把当前遍历到的数字存入map中,不管这个数字是否存在,因为即使存在哪位置也必定更靠前,得出的差值也必定更大,所有不管出现过没有直接放入该数字以及位置,以便下次遍历到相同数字时使用。我也看到了30ms以下的无不是去掉了大哥的解法,我超喜欢这里这里个个都是人才。
在这里插入图片描述
我的代码:
在这里插入图片描述

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) 
    { 
        map<int,int> map;
        for(int i=0;i<nums.size();i++)
        {
            if(map.count(nums[i])!=0 && i-map[nums[i]]<=k)
                return true;
            map[nums[i]]=i;
        }
        return false;
    }
};

268.缺失数字

给定一个包含0,1,2,...,n中n个数的序列,找出0..n中没有出现在序列中的那个数。

在这里插入图片描述
说明:你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?
思路一:
方法一:排序
分析:如果数组是有序的,那么就很容易知道缺失的数字是哪个了。
算法
首先我们对数组进行排序,随后我们可以在常数时间内判断两种特殊情况:0 没有出现在数组的首位,以及 n没有出现在数组的末位。如果这两种特殊情况都不满足,那么缺失的数字一定在 0 和 n之间(不包括两者)。此时我们可以在线性时间内扫描这个数组,如果某一个数比它前面的那个数大了超过 1,那么这两个数之间的那个数即为缺失的数字。
在这里插入图片描述
复杂度分析
时间复杂度:O(nlogn)。由于排序的时间复杂度为O(nlogn),扫描数组的时间复杂度为 O(n),因此总的时间复杂度为 O(nlogn)。
空间复杂度:O(1)或O(n)。空间复杂度取决于使用的排序算法,根据排序算法是否进行原地排序(即不使用额外的数组进行临时存储),空间复杂度为 O(1)或O(n)。

方法二:哈希表
分析
我们可以直接查询每个数是否在数组中出现过来找出缺失的数字。如果使用哈希表,那么每一次查询操作都是常数时间的。
算法
我们将数组中的所有数插入到一个集合中,这样每次查询操作的时间复杂度都是 O(1)的。
在这里插入图片描述
复杂度分析
时间复杂度:O(n)。集合的插入操作的时间复杂度都是 O(1),一共插入了 n个数,时间复杂度为O(n)。集合的查询操作的时间复杂度同样是O(1),最多查询 n+1次,时间复杂度为O(n)。因此总的时间复杂度为O(n)。
空间复杂度:O(n)。集合中会存储n个数,因此空间复杂度为 O(n)。

方法三:位运算
分析
由于异或运算(XOR)满足结合律,并且对一个数进行两次完全相同的异或运算会得到原来的数,因此我们可以通过异或运算找到缺失的数字。
算法
我们知道数组中有n个数,并且缺失的数在[0…n]中。因此我们可以先得到 [0…n]的异或值,再将结果对数组中的每一个数进行一次异或运算。未缺失的数在[0…n]和数组中各出现一次,因此异或后得到0。而缺失的数字只在 [0…n]中出现了一次,在数组中没有出现,因此最终的异或结果即为这个缺失的数字。
在这里插入图片描述
复杂度分析
时间复杂度:O(n)。这里假设异或运算的时间复杂度是常数的,总共会进行 O(n)次异或运算,因此总的时间复杂度为 O(n)。
空间复杂度:O(1)。算法中只用到了O(1) 的额外空间,用来存储答案。
方法四:数学
分析
我们可以用高斯求和公式求出[0…n]的和,减去数组中所有数的和,就得到了缺失的数字。高斯求和公式即
在这里插入图片描述
算法:我们在线性时间内可以求出数组中所有数的和,并在常数时间内求出前n+1个自然数(包括 0)的和,将后者减去前者,就得到了缺失的数字。
在这里插入图片描述
复杂度分析
时间复杂度:O(n)。求出数组中所有数的和的时间复杂度为 O(n),高斯求和公式的时间复杂度为 O(1),因此总的时间复杂度为O(n)。
空间复杂度:O(1)。算法中只用到了O(1) 的额外空间,用来存储答案。
作者:LeetCode
链接:https://leetcode-cn.com/problems/missing-number/solution/que-shi-shu-zi-by-leetcode/

思路二:
解法1:
排序后比对值和下标是否对应
在这里插入图片描述
解法2:异或
在这里插入图片描述
解法3:等差数列求和
用数列和sum减去num中的数据,剩下的数据就是缺失的数据
在这里插入图片描述
解法4:
在这里插入图片描述
我的提交:
在这里插入图片描述

class Solution {
public:
    int missingNumber(vector<int>& nums) 
    {
        int sum=(nums.size()*(nums.size()+1))/2;
        for(int i=0;i<nums.size();i++)
        {
            sum = sum - nums[i];
        }
        return sum;
    }
};

283.移动零

给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。

在这里插入图片描述
说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。
    思路:
    我的:
    在这里插入图片描述
发布了47 篇原创文章 · 获赞 21 · 访问量 7250

猜你喜欢

转载自blog.csdn.net/qq_18315295/article/details/103129101