一. set的使用 Intersection of Two Arrays
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]
说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。
python版解法
class Solution:
def intersection(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
set1 = set(nums1)
set2 = set(nums2)
result = set1 & set2
return list(set3)
c++版
// 时间复杂度: O(nlogn)
// 空间复杂度: O(n)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
set<int> record(nums1.begin(), nums1.end());
set<int> resultSet;
for( int i = 0 ; i < nums2.size() ; i ++ )
if(record.find(nums2[i]) != record.end()) // 在遍历到record.end前找到了
resultSet.insert(nums2[i]);
return Vector<int>(resultSet.begin(), resultSet.end());
}
};
二. map的使用 Intersection of Two Arrays II
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
用map解题
// 时间复杂度: O(nlogn)
// 空间复杂度: O(n)
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
map<int, int> record;
for(int i = 0 ; i < nums1.size() ; i ++)
if(record.find(nums1[i]) == record.end())
record.insert(make_pair(nums1[i],1)); // 第一次, 给map赋值1
else
record[nums1[i]] += 1;
vector<int> resultVector;
for(int i = 0 ; i < nums2.size() ; i ++)
if(record.find(nums2[i]) != record.end() &&
record[nums2[i]] > 0){
resultVector.push_back(nums2[i]);
record[nums2[i]] --;
if(record[nums2[i]] == 0)
record.erase(nums2[i]);
}
return resultVector;
}
};
三. set和map不同底层实现的区别
- set和map底层实现的不同, 复杂度也不同
方法 | 普通数组实现 | 顺序数组实现 | 二分搜索树(平衡) | 哈希表 |
---|---|---|---|---|
插入 | O(1) | O(n) | O(logn) | O(1) |
查找 | O(n) | O(logn) | O(logn) | O(1) |
删除 | O(n) | O(n) | O(logn) | O(1) |
- 一般的内置数据结构都是使用平衡的二分搜索树来作为set和map的底层结构
- 哈希表时间复杂度最低,但有一个缺点: 保存的数据没有顺序性
- 在一些对数据顺序性没有要求的代码中, 可以使用以哈希表为底层结构的set和map
使用哈希表作为底层来提高效率
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
// 349. Intersection of Two Arrays
// 使用哈希表作为底层
// 时间复杂度: O(len(nums1)+len(nums2))
// 空间复杂度: O(len(nums1))
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> record(nums1.begin(), nums1.end());
unordered_set<int> resultSet;
for(int i = 0; i < nums2.size(); i ++ )
if(record.find(nums2[i]) != record.end())
resultSet.insert(nums2[i]);
return vector<int>(resultSet.begin(), resultSet.end());
}
};
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
// 350. Intersection of Two Arrays II
// https://leetcode.com/problems/intersection-of-two-arrays-ii/description/
// 时间复杂度: O(len(nums1)+len(nums2))
// 空间复杂度: O(len(nums1))
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int, int> record;
for( int i = 0 ; i < nums1.size() ; i ++ )
record[nums1[i]] += 1;
vector<int> resultVector;
for(int i = 0; i < nums2.size(); i ++)
if(record[ nums2[i] ] > 0){
resultVector.push_back(nums2[i]);
record[nums2[i]] --;
}
return resultVector;
}
};
四. 使用查找表的经典问题 Two Sum
给出一个整型数组nums。 返回这个数组中两个数字的索引值i和j,
使得nums[i]+nums[j]等于一个给定的target值。 两个索引不能相等。
- 如 nums = [2, 7, 11,15], target=9
- 返回[0, 1]
- 使用map作为查找表
- 遍历到的元素放入查找表
- 如果遍历到的元素v, 已经在表中了,更新表中元素v的index
- 每一次遍历新元素v, 都在表中查找是否有元素满足值为 target-v
c++
#include <iostream>
#include <vector>
#include <cassert>
#include <unordered_map>
using namespace std;
// 1. Two Sum
// https://leetcode.com/problems/two-sum/description/
// 时间复杂度:O(n)
// 空间复杂度:O(n)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> record;
for(int i = 0 ; i < nums.size() ; i ++){
int complement = target - nums[i];
if(record.find(complement) != record.end()){
int res[] = {i, record[complement]};
return vector<int>(res, res + 2);
}
record[nums[i]] = i;
}
throw invalid_argument("the input has no solution");
}
};
python
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
record = dict()
for i in range(len(nums)):
compont = target - nums[i]
if compont in record.keys():
return [i, record[compont]]
record[nums[i]] = i
五. 灵活选择键值 4Sum II
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:
2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
解题思路
- 将C和D相加的所有可能放入查找表map
- 在遍历A和B, 查找符合条件的结果。 复杂度为n^2
#include <iostream>
#include <vector>
#include <unordered_map>
#include <cassert>
using namespace std;
// 454. 4Sum II
// https://leetcode.com/problems/4sum-ii/description/
// 时间复杂度: O(n^2)
// 空间复杂度: O(n^2)
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int,int> hashtable;
for(int i = 0 ; i < C.size() ; i ++)
for(int j = 0 ; j < D.size() ; j ++)
hashtable[C[i]+D[j]] += 1;
int res = 0;
for(int i = 0 ; i < A.size() ; i ++)
for(int j = 0 ; j < B.size() ; j ++)
if(hashtable.find(-A[i]-B[j]) != hashtable.end())
res += hashtable[-A[i]-B[j]];
return res;
}
};
六. 灵活选择键值 Number of Boomerangs
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
示例:
输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
解题思路
- 对于点i, 在查找表中存放其他点到i的距离, 以及相同距离的点的个数
- 即map[距离] = 个数, 找出个数>=2 的建值对
- 结果就是 个数 * (个数-1)
#include <iostream>
#include <vector>
#include <unordered_map>
#include <cassert>
#include <stdexcept>
using namespace std;
// 447. Number of Boomerangs
// https://leetcode.com/problems/number-of-boomerangs/description/
// 时间复杂度: O(n^2)
// 空间复杂度: O(n)
class Solution {
public:
int numberOfBoomerangs(vector<pair<int, int>>& points) {
int res = 0;
for( int i = 0 ; i < points.size() ; i ++ ){
// record中存储 点i 到所有其他点的距离出现的频次
unordered_map<int, int> record;
for(int j = 0 ; j < points.size() ; j ++)
if(j != i)
// 计算距离时不进行开根运算, 以保证精度
record[dis(points[i], points[j])] += 1;
for(unordered_map<int, int>::iterator iter = record.begin() ; iter != record.end() ; iter ++)
res += (iter->second) * (iter->second - 1);
}
return res;
}
private:
int dis(const pair<int,int> &pa, const pair<int,int> &pb){ //防止出现浮点误差, 不开根号
return (pa.first - pb.first) * (pa.first - pb.first) +
(pa.second - pb.second) * (pa.second - pb.second);
}
};
七. 查找表和滑动窗口 Contain Duplicate 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
思路
- 区间[l, l+k] k为题中给定值, l从0开始
- 遍历所有这样的区间, 找到nums [i] = nums [j] 其中i!=j, 并且在该区间中
- 能找到 就是true, 找不到就是false
c++
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
// 219. Contains Duplicate II
// https://leetcode.com/problems/contains-duplicate-ii/description/
// 时间复杂度: O(n)
// 空间复杂度: O(k)
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
if(nums.size() <= 1)
return false;
if(k <= 0)
return false;
unordered_set<int> record;
for(int i = 0 ; i < nums.size() ; i ++){
if(record.find(nums[i]) != record.end())
return true;
record.insert(nums[i]);
// 保持record中最多有k个元素
// 因为在下一次循环中会添加一个新元素,使得总共考虑k+1个元素
if(record.size() == k + 1)
record.erase(nums[i - k]);
}
return false;
}
};
python
class Solution:
def containsNearbyDuplicate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
if len(nums) <= 1 or k <= 0:
return False
record = set()
for i in range(len(nums)):
if nums[i] in record:
return True
record.add(nums[i])
if len(record) >= k+1:
record.remove(nums[i-k])
return False
八. 二分搜索树底层实现的顺序性 Contain Duplicate III
给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 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
解题思路
- 用一种大小<=k的 数据结构存放数组的元素, 这种数据结构 有 ceil 和floor两个方法,
- ceil和floor得到的元素 之差 的绝对值 <= t
#include <iostream>
#include <vector>
#include <set>
#include <cassert>
#include <stdexcept>
#include <cmath>
using namespace std;
// 220. Contains Duplicate III
// https://leetcode.com/problems/contains-duplicate-iii/description/
// 时间复杂度: O(nlogk)
// 空间复杂度: O(k)
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
// 这个问题的测试数据在使用int进行加减运算时会溢出
// 所以使用long long
set<long long> record;
for(int i = 0 ; i < nums.size() ; i ++){
if(record.lower_bound((long long)nums[i] - (long long)t) != record.end() &&
*record.lower_bound((long long)nums[i] - (long long)t ) <= (long long)nums[i] + (long long)t)
return true;
record.insert(nums[i]);
if(record.size() == k + 1)
record.erase( nums[i-k] );
}
return false;
}
};