1、给你一个整数数组
nums
。如果任一值在数组中出现 至少两次 ,返回true
;如果数组中每个元素互不相同,返回false
。示例:输入:nums = [1,2,3,1] 输出:true
输入:nums = [1,2,3,4] 输出:false
代码:大概有两种方法可以完整这个任务,一是可以先排序然后查找是否有重复元素,有则输出true,遍历完毕数组没有重复则输出false;二是通过哈希表完成任务。
(1)排序法:
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
sort(nums.begin(),nums.end()); //快排
int n = nums.size();
for (int i =0;i<=n;i++){
if(nums[i]==nums[i+1]){
return true;
}
}
return false;
}
};
时间复杂度:O(nlogn)。
空间复杂度:O(log N),递归调用栈的深度。
(2)哈希表set查找
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set <int> s; //创建哈希表
for(int x:nums){
if(s.find(x)!= s.end()){ //找相同数值
return true;
}
s.insert(x); //元素插入到哈希表中
}
return false;
}
};
哈希表相对时间复杂度更低,只要数组每个元素遍历一遍即可,时间复杂度O(n);空间复杂度相对更大,为O(n)。
(3)哈希表map
和第二个哈希表set大同小异,但是使用哈希表map统计元素数量后再决定。
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_map <int,int> s; //构建哈希表map
for (int num:nums){
s[num]++; //计算元素数量
if (s[num] >= 2){
return true;
}
}
return false;
}
};
2、给你一个整数数组
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。示例:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为6 。
这个题是第一次做啊....有点不大会,虽然看题目要求知道要选择动态规划,但是不会实操。于是看了下其他人的题解,学了一下大概要这样做:
(1)动态规划
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int a = 0, Maxans = nums[0];
for (const auto&x:nums){
a = max(a+x,x); // 连续子数组加和
Maxans = max(Maxans,a); //动态规划转移公式,选出最大子数组
}
return Maxans;
}
};
运用动态规划的时间复杂度比较小,遍历即可O(n);空间复杂度O(1)。
(2)分治
除了动态规划以外还可以使用分治的方法,但相对代码没有那么简洁。
class Solution {
public:
struct status{
int lsum,rsum,msum,isum;
};
status pushup(status l,status r){ //求得最大子数组
int isum = l.isum + r.isum; // 整个数组的区间和
int rsum = max ((r.isum+l.rsum),r.rsum); //右侧子数组的最大子数组
int lsum = max ((l.isum+r.lsum),l.lsum); //左侧子数组的最大子数组
int msum = max(max(r.msum , l.msum),(r.lsum+l.rsum)); //整个数组的最大子数组,要么等于左边,要么等于右边
//要么就等于左右连接(左边的右侧最大子数组+右侧的左边最大子数组)
return status {lsum,rsum,msum,isum}; //返回左侧最大、右侧最大、数组最大、数组总和
};
status get(vector<int>&n,int l,int r){
if (l==r){
return status{n[l],n[l],n[l],n[l]}; //只有一个就全是这个元素
}
int m = (l + r) >>1;
status lsub = get (n,l,m);
status rsub = get (n,m+1,r);
return pushup(lsub,rsub); //上面定义的pushup算出四个量
};
int maxSubArray(vector<int>& nums) {
return get(nums, 0, nums.size() - 1).msum; //嵌套函数算出数组的四个量,但我们只需要msum(数组中的最大子数组),所以就输出了msum
}
};
比较重点的部分就是pushup函数的设定,是比较经典的分而治之思想的体现。之后的get函数就是进行简单的判定以及找出中间的分隔处了。最后用设定的函数处理数组一步到位。(不得不说pushup函数还是需要比较清晰的思路,而且变量稍稍微微有点多,容易搞错)。最后这种算法的时间复杂度和空间复杂度的计算也相对更加复杂(没有前面的算法那么显而易见)。
时间复杂度:把递归的过程看作是一颗二叉树的先序遍历,那么这颗二叉树的深度的渐进上界为 O(logn),这里的总时间相当于遍历这颗二叉树的所有节点,故总时间的渐进上界是O(n)。
空间复杂度:递归会使用 O(logn) 的栈空间,故渐进空间复杂度为 O(logn)。
优势:这个题的结构比较简单所以没有体现出这种方法的优势:可以求出所有子区间的最大子数组,这样的话任意部分的最大子数组也会较容易求出。(但的确有点难)