本来想总结一下套路,结果写完发现这一篇博客成了水题合集
不过还是有很多思考与启发的!
目录
哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。
一个通俗的例子是,为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名 xxx 到首字母 F(x)F(x)F(x) 的一个函数关系),在首字母为 WWW 的表中查找 “王” 姓的电话号码,显然比直接查找就要快得多。这里使用人名作为关键字,“取首字母” 是这个例子中哈希函数的函数法则 F()F()F(),存放首字母的表对应哈希表。关键字和函数法则理论上可以任意确定。
哈希表是使用 O(1)O(1)O(1) 时间进行数据的插入删除和查找,但是哈希表不保证表中数据的有序性,这样在哈希表中查找最大数据或者最小数据的时间是 O(N)O(N)O(N) 实现。
LeetCode387.字符串中的第一个唯一字符
class Solution {
public:
int firstUniqChar(string s) {
unordered_map<char,int> m;
for (auto elem : s)
m[elem]++;
for (int i = 0; i < s.size(); i++) {
if (m[s[i]] == 1)
return i;
}
return -1;
}
};
LeetCode1365.有多少小于当前的数字
方法一:暴力求解
空间复杂度居然还可以,击败了所有人,我也是很疑惑……
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> a;
for(int i=0;i<nums.size();i++){;
int b = 0;
for(int j=0;j<nums.size();j++){
if(nums[j]<nums[i])
b++;
}
a.push_back(b);
}
return a;
}
};
还能更好吗? 当然可以用哈希表做,但是复杂度比这个高,所以这题对学习哈希表来说没什么意义……
LeetCode771.宝石与石头
用两个for嵌套循环暴力法,居然时间复杂度最高,我很迷啊
class Solution {
public:
int numJewelsInStones(string J, string S) {
int sum = 0;
for(int i=0;i<J.size();i++){
for(int j=0;j<S.size();j++){
if(J[i]==S[j])
sum++;
}
}
return sum;
}
};
用木桶排序的思想,将两个嵌套的for循环变成两个独立的for循环
时空复杂度反而更低,醉了
class Solution {
public:
int numJewelsInStones(string J, string S) {
int sum = 0;
set<char> jew;
for(auto i:J)
jew.insert(i);//记录宝石类型
for(auto s:S)
if(jew.count(s))
sum++;//若拥有的石头里有宝石,答案加一
return sum;
}
};
LeetCode面试题16.02.单词频率
当然可以用结构体做,但肯定会超时!感受一下恐怖的测试样例一角:
class WordsFrequency {
public:
struct b{
string s;
int num = 0;
}b[100001];
int size;
WordsFrequency(vector<string>& book) {
size = book.size();
for(int i=0;i<book.size();i++){
b[i].s = book[i];
b[i].num++;
}
}
int get(string word) {
int n = 0;
for(int i=0;i<size;i++){
if(b[i].s == word)
n+=b[i].num;
}
return n;
}
};
/**
* Your WordsFrequency object will be instantiated and called as such:
* WordsFrequency* obj = new WordsFrequency(book);
* int param_1 = obj->get(word);
*/
想不超时,改成哈希表解决即可!
使用map,内存可以击败100%哈哈
class WordsFrequency {
public:
map<string,int> m;
WordsFrequency(vector<string>& book) {
for(int i=0;i<book.size();i++){
m[book[i]]++;
}
}
int get(string word) {
if(m.find(word)!=m.end())
return m[word];
return 0;
}
};
/**
* Your WordsFrequency object will be instantiated and called as such:
* WordsFrequency* obj = new WordsFrequency(book);
* int param_1 = obj->get(word);
*/
也可以换用可重复的muliset:
class WordsFrequency {
public:
multiset<string> s;
WordsFrequency(vector<string>& book) {
int len = book.size();
for(int i = 0 ; i < len; i++){
s.insert(book.at(i));
}
}
int get(string word) {
return s.count(word);
}
};
/**
* Your WordsFrequency object will be instantiated and called as such:
* WordsFrequency* obj = new WordsFrequency(book);
* int param_1 = obj->get(word);
*/
时空复杂度差不多……
LeetCode1207.独一无二的出现次数
套路:以后碰到判断时候用重复的元素的题目,只要将其插入到set中,set会自动去重,再比较去重前后size大小是否变化就可以快速判断是否有重复元素!
class Solution {
public:
bool uniqueOccurrences(vector<int>& arr) {
map<int,int> m;
for(int i=0;i<arr.size();i++){
m[arr[i]]++;
}
set<int>times;
for(auto p:m)
times.insert(p.second);
return times.size()==m.size();
}
};
注意这里map不需要排序,理论上使用unordered_map比使用map的时间复杂度要低很多!
但是实际上map比unorder_map快不少,这是为啥……
LeetCode500.键盘行
用正则表达式空间复杂度会小一些,也会简洁不少;
但是搞个set套餐暴力模拟,非常快,可以干掉everybody
就是把键盘上这么多字母输入进去有点麻烦,可能是我太懒了哈哈
class Solution {
public:
vector<string> findWords(vector<string>& words) {
char a1[20] = {'q','w','e','r','t','y','u','i','o','p','Q','W','E','R','T','Y','U','I','O','P'};
char a2[18] = {'a','s','d','f','g','h','j','k','l','A','S','D','F','G','H','J','K','L'};
char a3[14] = {'z','x','c','v','b','n','m','Z','X','C','V','B','N','M'};
set<char> s1(a1,a1+20);
set<char> s2(a2,a2+18);
set<char> s3(a3,a3+14);
vector<string> findword;
for(int i=0;i<words.size();i++){
int c1=0,c2=0,c3=0;
for(int j=0;j<words[i].size();j++){
if(s1.find(words[i][j])!=s1.end())
c1++;
if(s2.find(words[i][j])!=s2.end())
c2++;
if(s3.find(words[i][j])!=s3.end())
c3++;
}
//如果三个数中只有一个为0,则符合条件
if((c1&&!c2&&!c3)||(!c1&&!c2&&c3)||(!c1&&c2&&!c3))
findword.push_back(words[i]);
}
return findword;
}
};
LeetCode349.两个数组的交集
用两个set,去重查找
用木桶可能会快一些,但是题目没说大数的范围,可能有坑……
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
set<int> s1(nums1.begin(),nums1.end());
set<int> s2(nums2.begin(),nums2.end());
vector<int> a;
for(set<int>::iterator it = s1.begin();it!=s1.end();it++){
if(s2.find(*it)!=s2.end())
a.push_back(*it);
}
return a;
}
};
LeetCode面试题03.数组中重复的数字
用木桶法可以用空间换时间,空间上可以击败everybody
当然可以用哈希法,用时间换空间
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int box[nums.size()]={0};
for(int i=0;i<nums.size();i++){
box[nums[i]]++;
if(box[nums[i]]>1)
return nums[i];
}
return 0;
}
};
LeetCode1208.尽可能使字符串相等
这题用给的FindElements函数,没有返回值,不好直接构造递归形式的函数;
所以将其拆出来添加一个参数,这种写法可以省下不少麻烦,这一点也是我之前没有认真思考过的!
所以思维要尽量打开啊
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class FindElements {
public:
FindElements(TreeNode* root) {
root->val = 0;
fun(root,0);
}
bool find(int target) {
return s.find(target)!=s.end();
}
void fun(TreeNode* root,int value){
if(root==NULL)
return;
s.insert(value);
root->val = value;
fun(root->left,2*value+1);
fun(root->right,2*value+2);
}
private:
set<int> s;
};
/**
* Your FindElements object will be instantiated and called as such:
* FindElements* obj = new FindElements(root);
* bool param_1 = obj->find(target);
*/