LeetCode刷题记录——哈希表
- 560. 和为K的子数组
- 166. 分数到小数
- 454. 四数相加 II
- 380. 常数时间插入、删除和获取随机元素
- 347. 前 K 个高频元素
- 138. 复制带随机指针的链表
- 49. 字母异位词分组
- 36. 有效的数独
- 3. 无重复字符的最长子串(剑指48)
- 387. 字符串中的第一个唯一字符
- 350. 两个数组的交集 II
- 面试题 02.01. 移除重复节点
- 242. 有效的字母异位词
- 217. 存在重复元素
- 204. 计数质数
- 202. 快乐数
- 136. 只出现一次的数字
- 1. 两数之和
- 剑指Offer(五十六):数组中唯一只出现一次的数字(重要,哈希表,位运算)
- 剑指Offer(五十):第一个只出现一次的字符 (重要,哈希表)
- 剑指 Offer 48. 最长不含重复字符的子字符串
560. 和为K的子数组
难度中等478
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
- 数组的长度为 [1, 20,000]。
- 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
Code:
class Solution {
public:
//前缀和 + 哈希表优化
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> mp;
mp[0]=1;
//pre[i] 为 [0..i] 里所有数的和
int pre=0;
int cnt=0;
for(int i=0;i<nums.size();++i){
//pre[i]−pre[j−1]==k
//pre[j−1]==pre[i]−k
pre+=nums[i];
if(mp.find(pre-k)!=mp.end()) cnt+=mp[pre-k];
mp[pre]++;
}
return cnt;
}
};
超时:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();++i){
int sum=0;
for(int j=i;j<nums.size();++j){
//int tmp=calSum(nums,i,j);
sum+=nums[j];//在计算[i,j]时可以利用[i,j-1]的值
mp[sum]++;
}
}
for(auto m:mp){
if(m.first==k) return m.second;
}
return 0;
}
};
166. 分数到小数
难度中等140
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数。
如果小数部分为循环小数,则将循环的部分括在括号内。
示例 1:
输入: numerator = 1, denominator = 2
输出: “0.5”
示例 2:
输入: numerator = 2, denominator = 1
输出: “2”
示例 3:
输入: numerator = 2, denominator = 3
输出: “0.(6)”
Awsl:
class Solution {
public:
string fractionToDecimal(int numerator, int denominator) {
if(numerator==0) return "0";
if(denominator==0) return "";
string res="";
long long num=static_cast<long long>(numerator);//转换为longlong防止溢出
long long denom=static_cast<long long>(denominator);
if((num>0)^(denom>0)) res.push_back('-');//处理正负号,一正一负取负号
//要考虑负分数以及极端情况
//分子分母全部转换为正数
num=llabs(num);
denom=llabs(denom);
res.append(to_string(num/denom));//处理整数部分
num%=denom;//处理小数部分//获得余数
if(num==0) return res; //余数为0,表示整除了,直接返回结果
res.push_back('.');//余数不为0,添加小数点
int ind=res.size()-1;//获得小数点的下标
unordered_map<int,int> map; //map用来记录出现重复数的下标,然后将'('插入到重复数前面就好了
while(num&&map.count(num)==0){//小数部分:余数不为0且余数还没有出现重复数字
map[num]=++ind;
num*=10;//余数扩大10倍,然后求商,和草稿本上运算方法是一样的
res+=to_string(num/denom);
num%=denom;
}
if(map.count(num)==1){ //出现循环余数,直接在重复数字前面添加'(',字符串末尾添加')'
res.insert(map[num],"(");
res.push_back(')');
}
return res;
}
};
454. 四数相加 II
难度中等162
给定四个包含整数的数组列表 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
解释:
两个元组如下:
- (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
- (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
修改(四数相加简化成两数相加):
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int,int> hash1;
unordered_map<int,int> hash2;
for(int i=0;i<A.size();++i){
for(int j=0;j<B.size();++j){
int tmpSum=A[i]+B[j];
hash1[tmpSum]++;
}
}
for(int i=0;i<C.size();++i){
for(int j=0;j<D.size();++j){
int tmp=C[i]+D[j];
hash2[tmp]++;
}
}
int cnt=0;
for(auto hash:hash1){
int tmp=-hash.first;
if(hash2.find(tmp)!=hash2.end()){
cnt+=hash2[tmp]*hash.second;
}
}
return cnt;
}
};
380. 常数时间插入、删除和获取随机元素
难度中等142
设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。
- insert(val):当元素 val 不存在时,向集合中插入该项。
- remove(val):元素 val 存在时,从集合中移除该项。
- getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。
示例 :
// 初始化一个空的集合。
RandomizedSet randomSet = new RandomizedSet();
// 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(1);
// 返回 false ,表示集合中不存在 2 。
randomSet.remove(2);
// 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomSet.insert(2);
// getRandom 应随机返回 1 或 2 。
randomSet.getRandom();
// 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomSet.remove(1);
// 2 已在集合中,所以返回 false 。
randomSet.insert(2);
// 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
randomSet.getRandom();
参考:
class RandomizedSet {
/*此题的正确解法是利用到了一个一维数组和一个 HashMap,
其中数组用来保存数字,HashMap 用来建立每个数字和其在数组中的位置之间的映射。
插入操作——先看这个数字是否已经在 HashMap 中存在,
如果存在的话直接返回 false,不存在的话,将其插入到数组的末尾,
然后建立数字和其位置的映射(map的第一个参数是元素的值,第二个参数是该值在数组中的下标)
删除操作——比较 tricky 的,还是要先判断其是否在 HashMap 里,如果没有,直接返回 false。
由于 HashMap 的删除是常数时间的,而数组并不是,
为了使数组删除也能常数级,实际上将要删除的数字和数组的最后一个数字调换个位置,
然后修改对应的 HashMap 中的值,这样只需要删除数组的最后一个元素即可,保证了常数时间内的删除
返回随机数——对于数组来说就很简单了,只要随机生成一个位置,返回该位置上的数字即可
*/
unordered_map<int,int> hash;
vector<int> res;
public:
/** Initialize your data structure here. */
RandomizedSet() {
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
bool insert(int val) {
if(hash.find(val)==hash.end()){
res.push_back(val);
hash[val]=res.size()-1;
return true;
}else{
return false;
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
bool remove(int val) {
if(hash.find(val)!=hash.end()) {
hash[res.back()] = hash[val];//!!!!!!!!
int index=hash[val];
swap(res[index],res.back());
res.pop_back();
hash.erase(val);
return true;
}
return false;
}
/** Get a random element from the set. */
int getRandom() {
return res[rand() % res.size()];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/
347. 前 K 个高频元素
难度中等368
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
• 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
• 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
• 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
• 你可以按任意顺序返回答案。
修改:键->数字,值->出现次数(也可以反过来)
class Solution {
public:
static bool cmp(const pair<int,int> &a,const pair<int,int> &b){
return a.second>b.second;//注意排序方向
}
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> res;
unordered_map<int,int> mp;
for(int num:nums){
mp[num]++;
}
vector<pair<int,int>> tmp(mp.begin(),mp.end());
sort(tmp.begin(),tmp.end(),cmp);
for(auto m:tmp){
res.push_back(m.first);
k--;
if(k<=0) break;
}
return res;
}
};
class Solution {
public:
static bool cmp(const pair<int,int> &a,const pair<int,int> &b){
return a.second>b.second;//注意排序方向
}
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> res;
unordered_map<int,int> mp;
for(int num:nums){
mp[num]++;
}
vector<pair<int,int>> tmp;
for(auto m:mp) tmp.push_back(make_pair(m.second,m.first));
sort(tmp.begin(),tmp.end());
int i=0;
while(i<k){
res.push_back(tmp[tmp.size()-i-1].second);
//cout<<tmp[tmp.size()-i-1].second<<endl;
i++;
}
return res;
}
};
138. 复制带随机指针的链表
难度中等301
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
• val:一个表示 Node.val 的整数。
• random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示:
• -10000 <= Node.val <= 10000
• Node.random 为空(null)或指
修正:
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL) return NULL;
Node* cur=head;
while(cur){
Node* tmp=new Node(cur->val);
tmp->next=cur->next;
cur->next=tmp;
cur=tmp->next;
}
cur=head;
while(cur){
Node* tmp=cur->next;
if(cur->random) tmp->random=cur->random->next;//cur!!!
cur=tmp->next;
}
cur=head;
Node* p=head->next;
while(cur){
Node* tmp=cur->next;
cur->next=tmp->next;
if(tmp->next) tmp->next=tmp->next->next;
cur=cur->next;
}
return p;
}
};
49. 字母异位词分组
难度中等372
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
说明:
• 所有输入均为小写字母。
• 不考虑答案输出的顺序
Code:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
//只要引入一个hash表,索引是排序后的单词,值为结果vector的下标,循环一遍就好了
vector<vector<string>> res;
//if(strs.size()==0) return res;
unordered_map<string,int> mp;
int sub=0;//res的下标
for(int i=0;i<strs.size();i++){
string tmp=strs[i];
sort(tmp.begin(),tmp.end());
if(mp.find(tmp)!=mp.end()){
res[mp[tmp]].push_back(strs[i]);
}else{
vector<string> v(1,strs[i]);
res.push_back(v);
mp[tmp]=sub++;
}
}
return res;
}
};
超时!:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
//if(strs.size()==0) return res;
unordered_map<string,int> mp;
for(int i=0;i<strs.size();++i){
string tmp=strs[i];
sort(tmp.begin(),tmp.end());
mp[tmp]++;
}
int len=mp.size();
vector<vector<string>> res;
for(auto m:mp){
vector<string> out;
for(int j=0;j<strs.size();++j){
string tmp=strs[j];
sort(tmp.begin(),tmp.end());
if(tmp==m.first&&m.second>=0){
out.push_back(strs[j]);
m.second--;
}
}
res.push_back(out);
}
return res;
}
};
36. 有效的数独
难度中等351
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例 1:
输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true
示例 2:
输入:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
• 一个有效的数独(部分已被填充)不一定是可解的。
• 只需要根据以上规则,验证已经填入的数字是否有效即可。
• 给定数独序列只包含数字 1-9 和字符 ‘.’ 。
• 给定数独永远是 9x9 形式的。
My:
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int row=board.size();
int col=board[0].size();
for(int i=0;i<9;++i){
unordered_map<char,int> mp;
for(int j=0;j<9;++j){
//if(board[i][j]=='.') continue;
mp[board[i][j]]++;
//cout<<'r'<<endl;
if(mp[board[i][j]]>1&&board[i][j]!='.') return false;
}
}
for(int i=0;i<9;++i){
unordered_map<char,int> mp;
for(int j=0;j<9;++j){
mp[board[j][i]]++;
//cout<<'c'<<endl;
if(mp[board[j][i]]>1&&board[j][i]!='.') return false;
}
}
for(int i=0;i<9;i+=3){
for(int j=0;j<9;j+=3){
cout<<i<<'*'<<j<<endl<<endl;
unordered_map<char,int> mp;
for(int m=i;m<i+3;m++){
for(int n=j;n<j+3;n++){
cout<<m<<'*'<<n<<endl;
if(board[m][n]=='.') continue;
mp[board[m][n]]++;
if(mp[board[m][n]]>1&&board[m][n]!='.') return false;
}
}
}
}
return true;
}
};
3. 无重复字符的最长子串(剑指48)
难度中等3855
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
//原字符串的一个滑动窗口,并维护窗口内不能有重复字符,同时更新窗口的最大值。
for (int i = 0, j = 0; i < s.size(); i ++){
hash[s[i]] ++;
//第一次出现次数大于1时,将s[i]的次数减到1,j从0开始向右移动,直到使其为1;
while (hash[s[i]] > 1) {
hash[s[j ++]] --;
}
res = max(res, i - j + 1);
}
return res;
}
};
387. 字符串中的第一个唯一字符
难度简单220
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例:
s = “leetcode”
返回 0
s = “loveleetcode”
返回 2
提示:你可以假定该字符串只包含小写字母。
My:
class Solution {
public:
int firstUniqChar(string s) {
if(s.empty()) return -1;
unordered_map<char,int> mp;
for(char c:s){
mp[c]++;
}
int res=-1;
for(int i=0;i<s.size();++i){
if(mp[s[i]]==1){
res=i;
break;
}
}
return res;
}
};
350. 两个数组的交集 II
难度简单291
给定两个数组,编写一个函数来计算它们的交集。
示例 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 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
/*使用 set 来实现线性时间复杂度。需要使用 HashMap 来跟踪每个数字出现的次数。
先在 HashMap 记录一个数组中的存在的数字和对应出现的次数。
然后,遍历第二个数组,检查数字在 HashMap 中是否存在,
如果存在且计数为正,则将该数字添加到答案并减少 HashMap 中的计数 */
if (nums1.size() > nums2.size()) {
return intersect(nums2, nums1);
}
vector<int> res;
unordered_map<int,int> mp;
for(int num1:nums1){
mp[num1]++;
}
for(int num2:nums2){
if(mp.find(num2)!=mp.end()&&mp[num2]>0){
res.push_back(num2);
mp[num2]--;
}
}
return res;
}
};
面试题 02.01. 移除重复节点
难度简单33
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
示例1:
输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]
示例2:
输入:[1, 1, 1, 1, 2]
输出:[1, 2]
提示:
- 链表长度在[0, 20000]范围内。
- 链表元素在[0, 20000]范围内。
进阶:
如果不得使用临时缓冲区,该怎么解决?
参考题解:https://leetcode-cn.com/problems/remove-duplicate-node-lcci/solution/yi-chu-zhong-fu-jie-dian-by-leetcode-solution/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//哈希表:
class Solution {
public:
ListNode* removeDuplicateNodes(ListNode* head) {
unordered_set<int> mp;
if(head==NULL||head->next==NULL) return head;
ListNode* p=head;
mp.insert(p->val);
while(p->next){
//ListNode* tmp=p->next;
bool is=mp.count(p->next->val);
if(is){
p->next=p->next->next;
}else{
mp.insert(p->next->val);
p=p->next;
}
}
p->next=NULL;
return head;
}
};
//双层链表循环:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeDuplicateNodes(ListNode* head) {
if(head==NULL||head->next==NULL) return head;
ListNode* p=head;
while(p){
ListNode* tmp=p;
while(tmp->next){
if(p->val==tmp->next->val){
tmp->next=tmp->next->next;
}else{
tmp=tmp->next;
}
}
p=p->next;
}
return head;
}
};
242. 有效的字母异位词
难度简单206
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
My1:
class Solution {
public:
bool isAnagram(string s, string t) {
sort(s.begin(),s.end());
sort(t.begin(),t.end());
if(s==t) return true;
return false;
}
};
My2:
class Solution {
public:
bool isAnagram(string s, string t) {
unordered_map<char,int> mp;
for(char c:s){
mp[c]++;
}
for(char c:t){
mp[c]--;
}
for(auto m:mp){
if(m.second!=0) return false;
}
return true;
}
};
217. 存在重复元素
难度简单260
给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
My:
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> mp;
for(int num:nums){
if(mp.find(num)!=mp.end()) return true;
else mp.insert(num);
}
return false;
}
};
204. 计数质数
难度简单368
统计所有小于非负整数 n 的质数的数量。
示例:
输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
Not my:
class Solution {
public:
bool isPrime(int n){
for(int i=2;i*i<n;++i){//!!!!!!
if(n%i==0) return false;
}
return true;
}
int countPrimes(int n) {
if(n<=1) return 0;
if(n==3) return 1;
vector<bool> v(n,true);
for(int i=2;i*i<n;i++){//!!!
if(isPrime(i)){
for(int j=i*i;j<n;j+=i){//!!
v[j]=false;
}
}
}
int cnt=0;
for(int i=2;i<n;i++){
if(v[i]) cnt++;
}
return cnt;
}
};
202. 快乐数
难度简单375
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
My1:
class Solution {
public:
int calHapppy(int n){
int res=0;
while(n){
res+=pow(n%10,2);
n=n/10;
}
return res;
}
bool isHappy(int n) {
set<int> mp;
while(n!=1){
int tmp=calHapppy(n);
mp.insert(n);
if(mp.find(tmp)!=mp.end()) return false;
if(tmp==1) return true;
n=tmp;
}
return true;
}
};
My2:
class Solution {
public:
int calHapppy(int n){
int res=0;
while(n){
res+=pow(n%10,2);
n=n/10;
}
return res;
}
bool isHappy(int n) {
unordered_map<int,int> mp;
while(true){
int tmp=calHapppy(n);
mp[n]++;;
if(mp[tmp]>1) return false;
if(tmp==1) return true;
n=tmp;
}
return true;
}
};
136. 只出现一次的数字
难度简单1341
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
My1:
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int,int> mp;
for(int num:nums){
mp[num]++;
}
for(int num:nums){
if(mp[num]==1) return num;
}
return -1;
}
};
My2:
class Solution {
public:
int singleNumber(vector<int>& nums) {
if(nums.empty()) return -1;
//int res=nums[0];
for(int i=1;i<nums.size();++i){
nums[0]^=nums[i];
//res^=nums[i];
}
return nums[0];
}
};
1. 两数之和
难度简单8497
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();++i){
mp[nums[i]]=i;
}
for(int i=0;i<nums.size();++i){
int num=nums[i];
int t=target-num;
if(mp.count(t)>=1&&mp[t]!=i){
return vector<int>{i,mp[t]};
}
}
return vector<int>();
}
};
剑指Offer(五十六):数组中唯一只出现一次的数字(重要,哈希表,位运算)
剑指 Offer 56 - II. 数组中数字出现的次数 II
难度中等38
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
限制:
• 1 <= nums.length <= 10000
• 1 <= nums[i] < 2^31
1.) 碰到次数,首先想到用哈希表。时间复杂度:O(n),空间复杂度O(n)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res;
unordered_map<int,int> mp;
for(int num:nums){
mp[num]++;
}
for(int num:nums){
if(mp[num]==1) return num;
}
return res;
}
};
2.) 排序数组中寻找只出现一次,时间复杂度:O(nlogn),空间复杂度O(1)
3.) hash_set, 将输入数组存储到 HashSet,然后使用 HashSet 中数字和的三倍与数组之和比较。时间复杂度:O(n),空间复杂度O(n)
3×(a+b+c)−(a+a+a+b+b+b+c)=2c
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
long sum_set = 0;
long sum_array = 0;
unordered_set<long> s;
for (int n : nums){
sum_array+=n;
s.insert((long)n);
}
for (auto num : s){
sum_set+=num;
}
res =(3 * sum_set - sum_array)/2;
return res;
}
};
4.)
位运算,
• 记录每一位不为0的数字出现的次数
• 如果出现的次数对3取模为1,则说明只出现一次的数字此位也是1
• 将所有模3为1的位想加,得到最终结果
时间复杂度:O(n),空间复杂度:O(1)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = 0;
for (int i = 0; i < 32; i++){
int count = 0;
for (auto n : nums){
if ((1 << i ) & n)
count++;
}
if (count % 3)
ans += (1 << i);
}
return ans;
}
};
剑指Offer(五十):第一个只出现一次的字符 (重要,哈希表)
剑指 Offer 50. 第一个只出现一次的字符
难度简单29
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例:
s = “abaccdeff”
返回 “b”
s = “”
返回 " "
限制:
0 <= s 的长度 <= 50000
哈希表,C++利用unordered_map<int,int>来进行次数统计,可以用int类型是因为c++会自动将字符转成对应的ASCII码数字,可以利用unordered_map<char,int>来进行次数统计.Python 利用collections.Counter()模块进行次数统计。
时间复杂度为O(n),空间复杂度O(n)。
class Solution {
public:
char firstUniqChar(string s) {
char res=' ';
unordered_map<char,int> mp;
if(s.size()==0) return res;
for(int i=0;i<s.size();++i){
mp[s[i]]++;
}
for(int i=0;i<s.size();++i){
if(mp[s[i]]==1) return s[i];
}
return res;
}
};
剑指 Offer 48. 最长不含重复字符的子字符串
难度中等52
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
提示:
• s.length <= 40000
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
//原字符串的一个滑动窗口,并维护窗口内不能有重复字符,同时更新窗口的最大值。
for (int i = 0, j = 0; i < s.size(); i ++){
hash[s[i]] ++;
//第一次出现次数大于1时,将s[i]的次数减到1,j从0开始向右移动,直到使其为1;
while (hash[s[i]] > 1) {
hash[s[j ++]] --;
}
res = max(res, i - j + 1);
}
return res;
}
};