*题目编号为Leetcode中对应的题号。
某位大佬的Leetcode题解参考链接
查找问题
-
概述
查找有无
- 元素‘a’是否存在:set——几集合
查找对应关系(键值对应)
- 元素‘a’出现了几次:map——字典
-
常见操作
insert
find
erase
change(map)
-
(349两个数组的交集) 给定两个数组,编写一个函数来计算它们的交集。
示例 :
扫描二维码关注公众号,回复: 12686110 查看本文章输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2]
// set版
// 时间复杂度O(nlogn)
// 空间复杂度O(n) 如何用一个set来实现?
ector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// O(nlog) set底层是二叉树,插入、查找、删除操作都是O(logn)
set<int> record(nums1.begin(), nums1.end());
// O(nlogn)
set<int> resultSet;// 不含重复元素
for(int i=0;i<nums2.size();i++){
if(record.find(nums2[i]) != record.end())
resultSet.insert(nums2[i]);
}
// O(n)
return vector<int>(resultSet.begin(), resultSet.end());
}
-
(350两个数组的交集) 给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2,2]
// 标准版
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
map<int, int> record;
for(int i=0;i<nums2.size();i++)
if(record.find(nums2[i]) != record.end())// 先找再操作
record.insert(make_pair(nums2[i],1));
else
record[nums2[i]]++;
vector<int> ret;
for(int i=0;i<nums1.size();i++)
if(record.find(nums1[i]) != recors.end() && record[nums1[i]]>0){
//先找再操作
ret.push_back(nums1[i]);
record[nums1[i]]--;
if(record[nums1[i]] == 0)
record.erase(nums1[i]);
}
return ret;
}
// 简化版
// 时间复杂度O(nlogn)
// 空间复杂度O(n)
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
// O(nlogn)插入O(logn)
map<int, int> record;
for(int i=0;i<nums2.size();i++)
record[nums2[i]]++;// 只要调用即初始化为0
// O(nlogn)
vector<int> ret;
for(int i=0;i<nums1.size();i++)
if(record[nums1[i]]>0){
ret.push_back(nums1[i]);
record[nums1[i]]--;
}
return ret;
}
-
哈希表的缺点:失去了数据原有的顺序性
-
map和set的底层实现是平衡二叉树, unordered_set和unordered_map的底层实现是哈希表
-
(242有效的字母异位词) 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram" 输出: true
// 普通版
bool isAnagram(string s, string t) {
int arr_s[26]={
0};
int arr_t[26]={
0};
for(char c : s)
arr_s[c-'a']++;
for(char c : t)
arr_t[c-'a']++;
for(int i=0;i<26;i++)
if(arr_s[i]!=arr_t[i])
return false;
return true;
}
// 高端版
bool isAnagram(string s, string t) {
map<char, int> map_s;
map<char, int> map_t;
for(char c : s)
map_s[c]++;
for(char c : t)
map_t[c]++;
if(map_s!=map_t)
return false;
return ture;
}
-
(202快乐数) 编写一个算法来判断一个数是不是“快乐数”。
一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。如果可以变为 1,那么这个数就是快乐数。
输入: 19 输出: true 解释: 1^2 + 9^2 = 82 8^2 + 2^2 = 68 6^2 + 8^2 = 100 1^2 + 0^2 + 0^2 = 1
bool isHappy(int n) {
set<int> ocurr;
int res = 0;
while (true) {
while (n != 0) {
int t = n % 10;
res += t * t;
n /= 10;
}
if (ocurr.find(res) != ocurr.end())
return false;
if (res == 1)
return true;
ocurr.insert(res);
n = res;
res = 0;
}
}
-
(290单词规律) 给定一种规律
pattern
和一个字符串str
,判断str
是否遵循相同的规律。这里的 遵循 指完全匹配,例如,
pattern
里的每个字母和字符串str
中的每个非空单词之间存在着双向连接的对应规律。示例1:
输入: pattern = "abba", str = "dog cat cat dog" 输出: true
示例 2:
输入:pattern = "abba", str = "dog cat cat fish" 输出: false
/// HashMap
/// TimeComplexity: O(n)
/// Space Complexity: O(n)
class Solution {
public:
bool wordPattern(string pattern, string str) {
vector<string> words = split(str);
if(pattern.size() != words.size())
return false;
unordered_map<char, string> map1;
unordered_map<string, char> map2;
for(int i = 0 ; i < pattern.size() ; i++)
if(map1.find(pattern[i]) == map1.end()){
if(map2.find(words[i]) != map2.end())
return false;
map1[pattern[i]] = words[i];
map2[words[i]] = pattern[i];
}
else{
string s = map1[pattern[i]];
if(s != words[i])
return false;
}
return true;
}
private:
vector<string> split(const string& s){
vector<string> res;
int start = 0;
for(int i = start + 1 ; i <= s.size() ; )
if(i == s.size() || s[i] == ' '){
res.push_back(s.substr(start, i - start));
start = i + 1;
i = start + 1;
}
else
i ++;
return res;
}
};
-
(205同构字符串) 给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 :
输入: s = "egg", t = "add" 输出: true 输入: s = "foo", t = "bar" 输出: false
/// Mapping
/// Time Complexity: O(len(s))
/// Space Complexity: O(len of charset)
class Solution {
public:
bool isIsomorphic(string s, string t) {
if(s.size() != t.size())
return false;
int map[256];
memset(map, -1, sizeof(map));
bool mapped[256];
memset(mapped, false, sizeof(mapped));
for(int i = 0 ; i < s.size() ; i ++){
if(map[s[i]] == -1){
if(mapped[t[i]])
return false;
map[s[i]] = t[i];
mapped[t[i]] = true;
}
else if(map[s[i]] != t[i])
return false;
}
return true;
}
};
-
(451根据字符出现频率排序) 给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例
输入:"tree" 输出:"eert" 输入:"cccaaa" 输出:"cccaaa" 输入:"Aabb" 输出:"bbAa"
/// Time Complexity: O(n)
/// Space Complexity: O(256)
class Solution {
public:
string frequencySort(string s) {
pair<int, char> freq[256];
for(int i = 0 ; i < 256 ; i ++){
freq[i].first = 0;
freq[i].second = i;
}
for(int i = 0 ; i < s.size() ; i ++)
freq[s[i]].first ++;
sort(freq, freq + 256, greater<pair<int, char>>());
int index = 0;
for(int i = 0 ; i < s.size() ; i ++){
while(!freq[index].first)
index ++;
s[i] = freq[index].second;
freq[index].first --;
}
return s;
}
};
-
(1两数之和) 给定一个整数数组
nums
和一个目标值target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
// 将数组排序后使用对撞指针
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
srand(time(NULL));
vector<int> index(n);
// 记录每个元素的索引
for (int i = 0; i < n; i++)
index[i] = i;
// 快排从小到大
__quicksort(nums, index, 0, n - 1);
// 对撞指针
vector<int> ret;
int start = 0, end = n - 1;
while (start <= end) {
if (nums[start] + nums[end] < target)
start++;
else if (nums[start] + nums[end] > target)
end--;
else {
ret.push_back(index[start]);
ret.push_back(index[end]);
break;
}
}
return ret;
}
void __quicksort(vector<int>& nums, vector<int>& index, int start, int end) {
if (start >= end)
return;
int mid = __partition(nums, index, start, end);
__quicksort(nums, index, start, mid - 1);
__quicksort(nums, index, mid + 1, end);
}
int __partition(vector<int>& nums, vector<int>& index, int start, int end) {
int idx = rand() % (end - start) + start;
swap(nums[start], nums[idx]);
swap(index[start], index[idx]);
int target = nums[start];
int j = start;// [start+1, j]<target, [j+1, end]>=target
for (int i = start + 1; i <= end; i++) {
if (nums[i] < target) {
swap(nums[j + 1], nums[i]);
swap(index[j + 1], index[i]);
j++;
}
}
swap(nums[start], nums[j]);
swap(index[start], index[j]);
return j;
}
查找表:将所有元素放入查找表,之后对于每一个元素a,查找target-a是否存在。
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> index_map;
for(int i=0; i<nums.size(); i++){
int res = index_map.find(target-nums[i]);
if( res != index_map.end()){
int ret[2]={
res, i};
return vector<int>(ret, ret+2);
}
else
index_map[nums[i]]=i;
}
throw invalid_argument("The input has no solution");
}
-
(15三数之和) 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
unordered_map<int, int> counter;
for(int i = 0 ; i < nums.size() ; i ++)
counter[nums[i]] ++;
vector<vector<int>> res;
if(counter[0] >= 3)
res.push_back({
0, 0, 0});
// Remove duplication
sort(nums.begin(), nums.end());
vector<int>::iterator iter = unique(nums.begin(), nums.end());
nums.erase(iter, nums.end());
for(int i = 0 ; i < nums.size() ; i ++)
for(int j = i + 1 ; j < nums.size() ; j ++){
if(nums[i] * 2 + nums[j] == 0 && counter[nums[i]] >= 2)
res.push_back({
nums[i], nums[i], nums[j]});
if(nums[i] + nums[j] * 2 == 0 && counter[nums[j]] >= 2)
res.push_back({
nums[i], nums[j], nums[j]});
int c = 0 - nums[i] - nums[j];
if(c > nums[j] && counter[c] != 0)
res.push_back({
nums[i], nums[j], c});
}
return res;
}
};
-
(18四数之和) 给定一个包含 n 个整数的数组
nums
和一个目标值target
,判断nums
中是否存在四个元素 *a,*b,c 和 d ,使得 a + b + c + d 的值与target
相等?找出所有满足条件且不重复的四元组。**注意:**答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
/// Using hash table
/// Sort the array first.
/// Store every different c + d == t first
/// For every different number a and b, try to find a pair (c, d), which a + b + c + d == 0
///
/// Time Complexity: O(nlogn) + O(n^2) + O(n^3)
/// Space Complexity: O(n^2)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> res;
if(n < 4)
return res;
sort(nums.begin() , nums.end());
unordered_map<int, vector<pair<int, int>>> hashtable;
for(int i = 0 ; i < n - 1 ; i = nextNumberIndex(nums, i))
for(int j = i + 1 ; j < n ; j = nextNumberIndex(nums, j))
hashtable[nums[i]+nums[j]].push_back(make_pair(nums[i], nums[j]));
for( int i = 0 ; i <= n - 4 ; i = nextNumberIndex(nums, i) )
for (int j = i + 1; j <= n - 3; j = nextNumberIndex(nums, j)) {
int t = target - nums[i] - nums[j];
if( nums[j+1] + nums[j+2] > t || nums[n-1] + nums[n-2] < t)
continue;
if(hashtable.find(t) == hashtable.end())
continue;
vector<pair<int,int>>::iterator iter =
lower_bound(hashtable[t].begin(), hashtable[t].end(), make_pair(nums[j+1], nums[j+1]));
for(; iter != hashtable[t].end() ; iter ++)
res.push_back({
nums[i], nums[j], iter->first, iter->second});
}
return res;
}
private:
int nextNumberIndex( const vector<int> &nums , int index ){
for( int i = index + 1 ; i < nums.size() ; i ++ )
if( nums[i] != nums[index] )
return i;
return nums.size();
}
int preNumberIndex( const vector<int> &nums , int index ){
for( int i = index-1 ; i >= 0 ; i -- )
if( nums[i] != nums[index] )
return i;
return -1;
}
};
-
(16最接近的三数之和) 给定一个包括 n 个整数的数组
nums
和 一个目标值target
。找出nums
中的三个整数,使得它们的和与target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。示例:
输入:nums = [-1,2,1,-4], target = 1 输出:2 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
/// Sorting + Two Pointers
/// Time Complexity: O(nlogn) + O(n^2)
/// Space Complexity: O(1)
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
assert(nums.size() >= 3);
sort(nums.begin(), nums.end());
int diff = abs(nums[0] + nums[1] + nums[2] - target);
int res = nums[0] + nums[1] + nums[2];
for(int i = 0 ; i < nums.size() ; i ++){
int l = i + 1, r = nums.size() - 1;
int t = target - nums[i];
while(l < r){
if(nums[l] + nums[r] == t)
return nums[i] + nums[l] + nums[r];
else{
if(abs(nums[l] + nums[r] - t) < diff){
diff = abs(nums[l] + nums[r] - t);
res = nums[i] + nums[l] + nums[r];
}
if( nums[l] + nums[r] < t )
l ++;
else // nums[l] + nums[r] > t
r --;
}
}
}
return res;
}
};
-
(454四数相加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 。所有整数的范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 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
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> sumCD;
for(int i=0;i<C.size();i++)
for(int j=0;j<D.size();j++)
sumCD[C[i]+D[j]]++;
int res=0;
for(int i=0;i<A.size();i++)
for(int j=0;j<B.size();j++)
if(sumCD.find(-A[i]-B[j]) != sumCD.end())
res+=sumCD[-A[i]-B[j]];
return res;
}
-
(49字母异位词分组) 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [ ["ate","eat","tea"], ["nat","tan"], ["bat"] ]
/// Using HashMap
/// Using sorted string as key
/// Time Complexity: O(n*klogk) where k is the max length of string in strs
/// Space Complexity: O(n*k)
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> map;
for(const string& s: strs){
string key = s;
sort(key.begin(), key.end());
map[key].push_back(s);
}
vector<vector<string>> res;
for(const auto& p: map)
res.push_back(p.second);
return res;
}
};
-
(149直线上最多的点数) 给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例
输入: [[1,1],[2,2],[3,3]] 输出: 3 输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]] 输出: 4
/// Definition for a point.
struct Point {
int x;
int y;
Point() : x(0), y(0) {
}
Point(int a, int b) : x(a), y(b) {
}
};
/// Using Hash Map
/// Using string to record the slopes
/// Time Complexity: O(n^2)
/// Space Complexity: O(n)
class Solution {
public:
int maxPoints(vector<Point>& points) {
if( points.size() <= 1 )
return points.size();
int res = 1;
for( int i = 0 ; i < points.size() ; i ++ ){
unordered_map<string, int> record;
int samePoint = 0;
for( int j = 0 ; j < points.size() ; j ++ ){
if( points[i].x == points[j].x && points[i].y == points[j].y )
samePoint ++;
else
record[getPairStr(slope(points[j], points[i]))]++;
}
res = max(res, samePoint); // In case the record is empty and all the points are in the same point.
for( unordered_map<string,int>::iterator iter = record.begin() ; iter != record.end() ; iter ++ )
res = max( res , iter->second + samePoint );
}
return res;
}
private:
pair<int,int> slope( const Point &pa, const Point &pb ){
int dy = pa.y - pb.y;
int dx = pa.x - pb.x;
if( dx == 0 )
return make_pair(1,0);
if( dy == 0 )
return make_pair(0,1);
int g = gcd( abs(dy), abs(dx) );
dy /= g;
dx /= g;
if( dx < 0 ){
dy = -dy;
dx = -dx;
}
return make_pair( dy , dx );
}
int gcd( int a , int b ){
if( a < b )
swap( a , b );
if( a % b == 0 )
return b;
return gcd( b , a%b );
}
string getPairStr( const pair<int,int> p){
return to_string(p.first) + "/" + to_string(p.second);
}
};
-
(447回旋镖的数量) 给定平面上 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]]
int numberOfBoomerangs(vector<vector<int>>& points) {
int res = 0;
for (int i = 0; i < points.size(); i++) {
unordered_map<int, int> record;// 键:到该点的距离,值:点的个数
for (int j = 0; j < points.size(); j++) {
if (j != i) {
int dist = calcDist(points[i], points[j]);
record[dist]++;
}
}
for (auto iter = record.begin(); iter != record.end(); iter++) {
if (iter->second >= 2) {
res += (iter->second) * (iter->second - 1);
}
}
}
return res;
}
// 计算两点间距离
int calcDist(vector<int> pt1, vector<int> pt2){
return (pt1[0]-pt2[0])*(pt1[0]-pt2[0])+(pt1[1]-pt2[1])*(pt1[1]-pt2[1]);
}
-
(219存在重复元素II) 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3 输出: true
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_set<int> record;
int l = 0, r = -1;// r-l=k
while (r + 1 < nums.size()) {
if (record.find(nums[r + 1]) != record.end())
return true;
record.insert(nums[++r]);
if (r - l + 1> k)// 保持r-l=k-1,则r+1时与l的差的绝对值不超过k
record.erase(nums[l++]);
}
return false;
}
-
(220存在重复元素III) 给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0 输出: true
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
set<int> record;
int l = 0, r = -1;// r-l=k
while (r + 1 < nums.size()) {
if (record.lower_bound(nums[r + 1] - t) != record.end() &&
*record.lower_bound(nums[r + 1] - t) <= nums[r + 1] + t)
return true;
record.insert(nums[r + 1]);
r++;
if (r - l + 1 > k)
record.erase(nums[l++]);
}
return false;
}