NeetCode 150 Arrays & Hashing 题解

情人节是啥玩意, 真不熟.

Contains Duplicate#

https://neetcode.io/problems/duplicate-integer/question?list=neetcode150

要求时间复杂度 O (n) 所以不能用 map, 于是用 unordered_set.

unordered_set: 基于哈希表实现, 查找, 插入, 删除都是 O (1) 复杂度.

set: 基于红黑树实现, 有序, 复杂度 O (logn). 可以顺序遍历或范围查找.

class Solution {
public:
bool hasDuplicate(vector<int>& nums) {
unordered_set<int> flag;
for(int i = 0; i < nums.size(); ++i){
flag.insert(nums[i]);
}
for(int elem : flag){
cout << elem << " ";
}
if(flag.size() < nums.size())
return true;
else
return false;
}
};

Valid Anagram#

https://neetcode.io/problems/is-anagram/question?list=neetcode150

没啥好说的.

class Solution {
public:
bool isAnagram(string s, string t) {
if(s.size() != t.size())
return false;
int a[30] = {0}, b[30] = {0};
for(int i = 0; i < s.size(); ++i){
++a[(int)(s[i] - 'a')];
++b[(int)(t[i] - 'a')];
}
for(int i = 0; i < 26; ++i)
if(a[i] != b[i])
return false;
return true;
}
};

Two Sum#

https://neetcode.io/problems/two-integer-sum/question?list=neetcode150

同样没啥好说的.

class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_set<int> diff;
vector<int> ans(2, 0);
for(int i = 0; i < nums.size(); ++i){
diff.insert(target - nums[i]);
}
for(int i = 0; i < nums.size(); ++i){
if(diff.find(nums[i]) != diff.end()){
for(int j = nums.size() - 1; j > i; --j){
if(nums[j] == target - nums[i]){
// cout << i << " " << j;
ans[0] = i;
ans[1] = j;
return ans;
}
}
}
}
return ans;
}
};

Group Anagrams#

https://neetcode.io/problems/anagram-groups/question?list=neetcode150

unordered_map 平均时间复杂度也是 O (1).

用 string 作 key 可以避免手写哈希函数的痛苦.

class Solution {
private:
struct Array {
int data[30]={0};
};
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> ans;
Array count;
unordered_map<string, vector<string>> mp;
// for(int i = 0; i < strs.size(); ++i) {
// for(int j = 0; j < 26; ++j) {
// count[i].data[j] = 0;
// }
// }
for(int i = 0; i < strs.size(); ++i) {
for(int j = 0; j < strs[i].size(); ++j) {
++count.data[(int)(strs[i][j] - 'a')];
}
string str = "";
for(int j = 0; j < 26; ++j) {
str += to_string(count.data[j]) + '?';
count.data[j] = 0;
}
mp[str].push_back(strs[i]);
}
for(auto it = mp.begin(); it != mp.end(); ++it) {
ans.push_back(it->second);
// cout<<it->first<<"hajmi"<<endl;
}
return ans;
}
};

Top K Frequent Elements#

正常情况可以用最小堆, 复杂度 O (nlogk), 但是题目要求 O (n), 所以可以使用空间复杂度更高的桶排序方法.

class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<vector<int>> bucket(nums.size()+1);
vector<int> count(2025, 0);
for(int i = 0; i < nums.size(); ++i) {
nums[i] += 1001;
++count[nums[i]];
}
int maxcount = 0;
for(int i = 0; i < count.size(); ++i) {
if(count[i] == 0) continue;
bucket[count[i]].push_back(i - 1001);
}
int temp = k;
vector<int> ans;
for(int i = bucket.size() - 1; i >= 0; --i) {
if(bucket[i].size() <= 0) continue;
for(auto it = bucket[i].begin(); it != bucket[i].end(); ++it) {
if(temp <= 0) return ans;
ans.push_back(*it);
temp --;
}
if(temp <= 0) return ans;
}
return ans;
}
};

Encode and Decode Strings#

https://neetcode.io/problems/string-encode-and-decode/question?list=neetcode150

主要内容是怎么在字符串列表里搞个分隔符. 计网好像学过好几个解决方案但是我忘光了, 反正没有长度要求就用了最唐的把字符串长度写出来的方法.

class Solution {
public:

string encode(vector<string>& strs) {
string ans = "";
for(int i = 0; i < strs.size(); ++i) {
ans += "@" + to_string(strs[i].size()) + "@" + strs[i];
}
cout<<ans;
return ans;
}

vector<string> decode(string s) {
vector<string> ans;
for(int i = 0; i < s.size(); ++i) {
int len = 0;
if(s[i] == '@') {
len = 0;
++i;
while(s[i] != '@') {
len = len * 10 + (int)(s[i] - '0');
++i;
// cout<<"len="<<len<<endl;
}
string temp = "";
for(int j = 0; j < len; ++j) {
temp += s[++i];
}
ans.push_back(temp);
// cout<<"string="<<temp<<endl;
}
}
return ans;
}
};

Products of Array Except Self#

https://neetcode.io/problems/products-of-array-discluding-self/question?list=neetcode150

好吧我才知道 product 还有乘积的意思.

题目要求不许用除法, 所以用类似前缀和的方法, 算左边所有元素的和 × 右边所有元素的和.

class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> left, right(nums.size() + 5);
int temp = 1;
// cout<<"left:"<<endl;
for(int i = 0; i < nums.size(); ++i) {
temp *= nums[i];
left.push_back(temp);
// cout<<temp<<" ";
}
temp = 1;
// cout<<"\nright:"<<endl;
for(int i = nums.size() - 1; i >= 0; --i) {
temp *= nums[i];
right[i] = temp;
}
// for(int i = 0; i < nums.size(); ++i) cout<<right[i]<<" ";
vector<int> ans;
ans.push_back(right[1]);
for(int i = 1; i < nums.size() - 1; ++i) {
ans.push_back((int)(left[i-1] * right[i+1]));
}
ans.push_back(left[nums.size() - 2]);
return ans;
}
};

想优化空间复杂度的话其实 right 和 left 两个数组都是不必要的, 不过写出来容易理解一点.

Valid Sudoku#

https://neetcode.io/problems/valid-sudoku/question?list=neetcode150

没看懂这题是何意味, 查一遍就完事了, 理论时间复杂度 O (1).

想优化可以用位运算, 但是我都 O (1) 了我管你这那的.

#include<cstring>
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
bool flag[10];
int num;
for(int i = 0; i < 9; ++i) {
memset(flag, 0, sizeof(flag));
for(int j = 0; j < 9; ++j) {
if(board[i][j] != '.') {
num = (int)(board[i][j] - '0');
if(flag[num])
return false;
flag[num] = true;
}
}
}
for(int i = 0; i < 9; ++i) {
memset(flag, 0, sizeof(flag));
for(int j = 0; j < 9; ++j) {
if(board[j][i] != '.') {
num = (int)(board[j][i] - '0');
if(flag[num])
return false;
flag[num] = true;
}
}
}
for(int i = 0; i < 3; ++i) {
for(int m = 0; m < 3; ++m) {
memset(flag, 0, sizeof(flag));
for(int j = 0; j < 3; ++j) {
for(int k = 0; k < 3; ++k) {
if(board[j + 3 * i][k + 3 * m] != '.') {
num = (int)(board[j + 3 * i][k + 3 * m] - '0');
if(flag[num])
return false;
flag[num] = true;
}
}
}
}
}
return true;
}
};

Longest Consecutive Sequence#

https://neetcode.io/problems/longest-consecutive-sequence/question?list=neetcode150

本来以为是类似最长上升子序列的题, 结果闹了半天根本不关心数字出现顺序, 只要出现了就可以, unordered_map 解决.

class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_map<int, bool> mp;
for(int i = 0; i < nums.size(); ++i) {
mp[nums[i]] = true;
}
int ans = 0;
for(int i = 0; i < nums.size(); ++i) {
if(mp[nums[i] - 1] != true) {
int t = 0;
for(int j = nums[i];; ++j) {
if(mp[j]) ++t;
else break;
}
ans = (ans > t)?ans:t;
}
}
return ans;
}
};

第一小节做完了, 猜猜我能坚持多久.