Leetcode 每日一题打卡系列
03.14 设计哈希映射(706)
class MyHashMap {
public:
/** Initialize your data structure here. */
vector<list<pair<int,int>>>data;
const static int base=769;
static int hash(int key){
return key%base;
}
MyHashMap():data(base) {
}
/** value will always be non-negative. */
void put(int key, int value) {
int h=hash(key);
for(auto it=data[h].begin();it!=data[h].end();it++){
if((*it).first==key){
(*it).second=value;
return ;
}
}
//pair<int,int>;make_pair<key,value>
data[h].push_back(make_pair(key,value));
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
int get(int key) {
int h=hash(key);
for(auto iter=data[h].begin();iter!=data[h].end();iter++){
if((*iter).first==key){
return (*iter).second;
}
}
return -1;
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
void remove(int key) {
int h=hash(key);
for(auto it=data[h].begin();it!=data[h].end();it++){
if((*it).first==key){
data[h].erase(it);
return ;
}
}
}
};
/**
* Your MyHashMap object will be instantiated and called as such:
* MyHashMap* obj = new MyHashMap();
* obj->put(key,value);
* int param_2 = obj->get(key);
* obj->remove(key);
*/
总结:利用除留取余法构造哈希函数,利用拉链法解决冲突问题;
pair<int,int>
构造数对,
03.15 螺旋矩阵(54)
总结:
1、设置四个标志位top、bottom、left、right ,模拟旋转即可
03.16螺旋矩阵2
和昨天类似,没什么,设置四个位模拟;
03.17 不同的子序列
S的子序列中可以匹配t字符的次数
动态规划:
代码实现
class Solution {
public:
int numDistinct(string s, string t) {
long long len1=s.size();
long long len2=t.size();
if(len1<len2) return 0;
vector<vector<long long >>dp(len1+1,vector<long long >(len2+1,0));
for(int i=0;i<len1+1;i++) dp[i][0]=1;
for(int j=0;j<len2+1;j++) dp[0][j]=0;
dp[0][0]=1;
for(int i=1;i<len1+1;i++){
for(int j=1;j<len2+1;j++){
if(s[i-1]==t[j-1]){
dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
}else{
dp[i][j]=dp[i-1][j];
}
}
}
return dp[len1][len2];
}
};
0318 反转链表(92)
解题思路
这题最开始应用了每K个反转链表的思路,找到左区间的前一个位置,找到有区间末尾位置以及右区间下一个位置,将中间链表反转然后接上去
贴一下错误代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode *reverselist(ListNode *head){
if(head==nullptr||head->next==nullptr) return head;
ListNode *pre=nullptr;
ListNode *cur=head;
while(cur){
ListNode *temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
}
ListNode* reverseBetween(ListNode* head, int left, int right) {
if(head==nullptr||head->next==nullptr) return head;
ListNode *dumpy=new ListNode(-1,head);
ListNode *cur=dumpy;
while(cur){
for(int i=left-1;i>0&&cur;i--){
cur=cur->next;
}
if(cur==nullptr) break;
ListNode *first=cur;
ListNode *start=first->next;
for(int i=left;i<=right&&cur;i++){
cur=cur->next;
}
if(cur==nullptr) break;
ListNode *next=cur->next;
cur->next=nullptr;
ListNode *reversehead=reverselist(start);
first->next=reversehead;
start->next=next;
}
return dumpy->next;
}
};
错误原因在于,套用K个链表,用了两层循环。因为每K个是要循环整个链表,遇到K个数就反转,而这题只用反转依次m到n的即可,所以不需要加外层循环,但是这份代码可以通过30个测试用例;为什么呢
分析【1,2,3,4,5】,m=2,n=4;第一次反转之后为1,4,3,2,5;
此时cur指向4,执行找左边界没问题,走到2;然后执行找右边界,走的时候为空,退出循环,没有执行反转输出14325;
如果是【1,2,3,4,5】;m=2,n=3;这时候输出13254;
解释:首先第一次是13245,cur指向3,然后循环,13254,cur指向空,退出循环,
正确解法: 去掉外层的循环即可;
代码实现:
class Solution {
public:
ListNode *reverselist(ListNode *head){
if(head==nullptr||head->next==nullptr) return head;
ListNode *pre=nullptr;
ListNode *cur=head;
while(cur){
ListNode *temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
}
ListNode* reverseBetween(ListNode* head, int left, int right) {
if(head==nullptr||head->next==nullptr) return head;
ListNode *dumpy=new ListNode(-1,head);
ListNode *cur=dumpy;
for(int i=left-1;i>0&&cur;i--){
cur=cur->next;
}
ListNode *first=cur;
ListNode *start=first->next;
for(int i=left;i<=right&&cur;i++){
cur=cur->next;
}
ListNode *next=cur->next;
cur->next=nullptr;
ListNode *reversehead=reverselist(start);
first->next=reversehead;
start->next=next;
return dumpy->next;
}
};
0319 停车场系统
解题思路: 定义一个flag数组,初始化为每个停车位的数量,如果有车进来则减1,直到为零返回false;
代码实现:
class ParkingSystem {
public:
int flag[3];
int cnt[3]={0};
ParkingSystem(int big, int medium, int small) {
flag[0]=big;
flag[1]=medium;
flag[2]=small;
}
bool addCar(int carType) {
//注释为自己写的,后面为优化,可以不建立cnt数组。
// if(flag[carType-1]>cnt[carType-1]){
// cnt[carType-1]++;
// return true;
// }
// return false;
return flag[carType-1]-->0;
}
};
0320 逆波兰表达式(150)
一刷,AC
解题思路:对于每一个字符串,先判断长度是否为1,再看是否是运算符,如果是运算符,直接计算,是数字入栈,如果长度不为1,肯定是数字,入栈,而且这题要主要这题中还有负数
代码实现
class Solution {
public:
int to_digit(string s){
int n=s.size();
int sum=0;
for(int i=0;i<n;i++){
if('0'<=s[i]&&s[i]<='9'){
sum=sum*10+s[i]-'0';
}
}
//判断是否是负数;
return s[0]=='-'?-1*sum:sum;
}
int evalRPN(vector<string>& tokens) {
int n=tokens.size();
stack<int>nums;
for(int i=0;i<n;i++){
//先判断是否是运算符,因为运算符肯定长度只有一位;
if(tokens[i].size()==1){
char str=tokens[i][0];
//如果是数字,直接入栈:
if('0'<=str&&str<='9'){
nums.push(str-'0');
//不是数字,进行运算;
}else{
int a=nums.top();nums.pop();
int b=nums.top();nums.pop();
switch (str){
case '+':nums.push(a+b);break;
case '-':nums.push(b-a);break;
case '*':nums.push(a*b);break;
case '/':nums.push(b/a);break;
}
}
}
//长度不为1,则肯定是数字,转换为数字直接入栈
else nums.push(to_digit(tokens[i]));
}
//最后的结果一定在栈顶
return nums.top();
}
};
0321 矩阵置零(73)
解题思路
这里采用的是先找到为0的行数和列数储存·起来,然后遍历矩阵,将对应的行和列置为零即可,但是不满足空间复杂度为O(1),还可以优化,优化是,在第一行和第一列增加标志位判断;
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
vector<pair<int,int>>p;
for(int i=0;i<matrix.size();i++){
for(int j=0;j<matrix[0].size();j++){
if(matrix[i][j]==0){
p.push_back(make_pair(i,j));
}
}
}
for(int i=0;i<p.size();i++){
for(int j=0;j<matrix[0].size();j++){
matrix[p[i].first][j]=0;
}
for(int k=0;k<matrix.size();k++){
matrix[k][p[i].second]=0;
}
}
}
};
0322 位1的个数(191)
解题思路
将数字不断地和1相与,然后循环右移,统计1的个数即可,
将n&(n-1)不断地相与,直到n为零,这个运算做的次数即为n中1的个数
class Solution {
public:
int hammingWeight(uint32_t n) {
int cnt=0;
while(n){
if(n&1){
cnt++;
}
n>>=1;
}
return cnt;
}
};
class Solution {
public:
int hammingWeight(uint32_t n) {
int cnt=0;
while(n){
n&=n-1;
cnt++;
}
return cnt;
}
};
0323扁平化嵌套列表迭代器(341)
/**
* // This is the interface that allows for creating nested lists.
* // You should not implement it, or speculate about its implementation
* class NestedInteger {
* public:
* // Return true if this NestedInteger holds a single integer, rather than a nested list.
* bool isInteger() const;
*
* // Return the single integer that this NestedInteger holds, if it holds a single integer
* // The result is undefined if this NestedInteger holds a nested list
* int getInteger() const;
*
* // Return the nested list that this NestedInteger holds, if it holds a nested list
* // The result is undefined if this NestedInteger holds a single integer
* const vector<NestedInteger> &getList() const;
* };
*/
class NestedIterator {
public:
//2021/03/23抄答案,不懂
vector<int>vals;
vector<int>::iterator cur;
void dfs(const vector<NestedInteger>&nestedList){
for(auto &nest:nestedList){
if(nest.isInteger()){
vals.push_back(nest.getInteger());
}else{
dfs(nest.getList());
}
}
}
NestedIterator(vector<NestedInteger> &nestedList) {
dfs(nestedList);
cur=vals.begin();
}
int next() {
return *cur++;
}
bool hasNext() {
return cur!=vals.end();
}
};
/**
* Your NestedIterator object will be instantiated and called as such:
* NestedIterator i(nestedList);
* while (i.hasNext()) cout << i.next();
*/
0324 132模式(456)
解题思路:
单调栈解法:
本题条件为i<j<k;时ai<ak<aj;
可以看到j和k对应的关系是一个单调减的关系
所以可以逆序,采用单调递减栈找到第一个大于ak的元素,记录下来为aj;
当新入栈元素大于栈顶元素时,记录当前栈顶元素值,如果新入栈的元素还大于栈顶元素
如此循环,找到小于aj的最大的元素即为ak,记录下来,然后遍历新的值,如果新的值小于记录ak的值,说明满足132,输出为True;
代码实现:
class Solution {
public:
bool find132pattern(vector<int>& nums) {
//i,j,k;
//ai<ak<aj;
stack<int>sta;
int n=nums.size();
int midval=INT_MIN;
for(int i=n-1;i>=0;i--){
if(nums[i]>=midval){
while(!sta.empty()&&nums[i]>nums[sta.top()]){
midval=nums[sta.top()];
sta.pop();
}
sta.push(i);
}else{
return true;
}
}
return false;
}
};
0325 删除排序链表中的重复元素II(82)
解题思路:
遍历整个链表,如果当前节点的下一个元素的值等于当前节点下一个的下一个元素的值,则
cur->next=cur->next->next
;
为防止第一个节点是重复元素,可以设置虚拟头节点;
为了删除所有重复的节点,还需要临时变量记录cur->next
的值。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head==nullptr||head->next==nullptr) return head;
ListNode *dummpy=new ListNode(-1,head);
ListNode *cur=dummpy;
while(cur->next&&cur->next->next){
if(cur->next->val==cur->next->next->val){
int x=cur->next->val;
while(cur->next&&cur->next->val==x)
cur->next=cur->next->next;
}else{
cur=cur->next;
}
}
return dummpy->next;
}
};
0326 删除排序链表中的重复元素(83)
解题思路:
和昨天的一题相似,但是还留了一个重复的元素,可以不用临时变量记录
cur->next
的值。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head==nullptr||head->next==nullptr) return head;
ListNode *dummpy=new ListNode(-1,head);
ListNode *cur=dummpy;
while(cur->next&&cur->next->next){
if(cur->next->val==cur->next->next->val){
cur->next=cur->next->next;
}else{
cur=cur->next;
}
}
return dummpy->next;
}
};
0327 旋转链表(61)
解题思路:
这题刚开始想的时候,最开始的思路是先遍历,找到第K个节点,那么第K个节点的下一个为新的节点,遍历到尾节点然后把前面的拼接到后面,但是实现起来有很多边界条件容易出错;
思路2: 把整个链表构造成为环状的,然后移动即可,但是这里面又有一个新问题,移动的步数是链表长度减去移动的k(如果k大于链表长度,则为(k%链表长度);)
代码实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head==nullptr||head->next==nullptr||k==0) return head;
ListNode *cur=head;
int cnt=1;
while(cur->next){
cur=cur->next;
cnt++;
}
cur->next=head;//连接成环
int num=k%cnt;
num=cnt-num-1;
cur=head;
while(num--){
cur=cur->next;
}
ListNode *newhead=cur->next;
cur->next=nullptr;
return newhead;
}
};
0328 二叉搜索树迭代器
解题思路:
实际上就是一个二叉树的中序遍历;这里面要注意的是,先进先出,采用队列这种数据结构来存储元素更为方便
代码实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class BSTIterator {
public:
queue<int>que;
void inorder(TreeNode *root){
if(root==nullptr) return ;
inorder(root->left);
que.push(root->val);
inorder(root->right);
}
BSTIterator(TreeNode* root) {
head=root;
inorder(root);
}
int next() {
int temp=que.front();
que.pop();
return temp;
}
bool hasNext() {
return !que.empty();
}
private:
TreeNode *head;
};
/**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator* obj = new BSTIterator(root);
* int param_1 = obj->next();
* bool param_2 = obj->hasNext();
*/
0329 颠倒二进制位
解题思路:
对于这题刚开始没有思路,看答案才知道怎么做
思路1:整数反转
相当于十进制的123反转为321每次%10之后在/10得出每一位,然后反向计算得出最后的结果,对于二进制来书就是%2之后在/2;计算得到最后的结果;
思路2:二进制
对于二进制数每次和1相与得到最后一位,用ans记录,然后每次ans向左移一位,直到全部移位完成;
代码实现:
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
// uint32_t res=0;
// for(int i=0;i<32;i++){
// res=res*2+n%2;
// n=n/2;
// }
// return res;
uint32_t ans=0;
int i=32;
while(i--){
ans<<=1;
ans+=(n&1);
n>>=1;
}
return ans;
}
};
0415 打家劫舍(213)
打家劫舍213
解题思路:
因为是环形的,而且相邻不能偷,相比不是环形的多了头尾不能相邻的情况,所以需要特殊考虑,考虑两次,一次是从0到n-2,一次是从1到n-1,即可;
状态转移方程
d p [ i ] = m a x ( d p [ i − 2 ] + n u m [ i ] , d p [ i − 1 ] ) ; dp[i]=max(dp[i-2]+num[i],dp[i-1]); dp[i]=max(dp[i−2]+num[i],dp[i−1]);
代码实现
class Solution {
public:
vector<int>dp;
int clirclerob(vector<int>&nums,int left,int right){
dp[left]=nums[left];
dp[left+1]=max(nums[left],nums[left+1]);
for(int i=left+2;i<=right;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[right];
}
int rob(vector<int>& nums) {
int n=nums.size();
dp.resize(n,0);
if(n==1) return nums[0];
if(n==2) return max(nums[0],nums[1]);
return max(clirclerob(nums,0,n-2),clirclerob(nums,1,n-1));//前闭后开
}
};