文章目录
数组:
删除排序数组中的重复项
题意:
一个升序数组nums,要求使用原地算法,要求返回数组内有几种数字
若有K种,需要保证原数组处理完[0,K-1]
以原序存储每种数字各一个
解:
由于要求使用原地算法,所以直接对原数组操作,使用一个指针L表示所指向数字的第一个,默认指向0
遍历数组,如果遇到一个和指针L指向的数字不同的数字ElseNum
,就将ElseNum
放在L+1的位置,然后L指向ElseNum
表示ElseNum
这种数字已经找到了第一个,可以找下一种了
代码:
#include<bits/stdc++.h>
using namespace std;
int removeDuplicates(vector<int>& nums)
{
int l=0,k=1,lg=nums.size();
for(int i=1;i<lg;i++)
{
if(nums[i]!=nums[l])
{
swap(nums[i],nums[++l]);
k++;
}
}
return k;
}
int main()
{
vector<int>nums;int temp;
while(cin>>temp)
{
nums.push_back(temp);
}
int ans=removeDuplicates(nums);
cout<<ans<<endl;
return 0;
}
买卖股票的最佳时机 II
题意:
给定一个整数数组,代表每天的股票价格,最多只能持有一股,求最大利润
解:
贪心,有的赚先赚。任意两个数字[A,B],A表示今天的价格,B表示你预知明天的价格,如果A<B,则明天涨价,那就在今天买明天卖;如果B<A,则明天跌落,今天先卖明天再买可以用更低的成本持有一股。
由于核心思路是通过看下一天的价格来考虑今天是否持有一股(初始没有持有),所以对任意一段求最大利润不需要依赖其他部分。
代码:
#include<bits/stdc++.h>
using namespace std;
int maxProfit(vector<int>& prices)
{
int lg=prices.size(),ans=0;
for(int i=1;i<lg;i++) ans=ans+max(0,prices[i]-prices[i-1]);
return ans;
}
int main()
{
vector<int>prices;int temp;
while(cin>>temp)
{
prices.push_back(temp);
}
int ans=maxProfit(prices);
cout<<ans<<endl;
return 0;
}。
旋转数组
题意:
给一个整数数组,要求整体右移k个位置,超出数组的元素按原序填补到前面空出来的位置
解:
1、新数组,先装k到lg-1,然后再装0到k-1
2、bool数组标记处理过的数字,从i开始处理i+k、i+2k直到回到i
3、每移动lg次相当于没移动,所以k=k%lg,最后结果相当于把最后k个数字原序移动到开头,可以先翻转整个数组,然后分别翻转[0,k-1]
和[k,lg-1]
,原地算法
代码:
#include<bits/stdc++.h>
using namespace std;
inline void reserve(vector<int>& nums,int l,int r)
{
while(l<r) swap(nums[l++],nums[r--]);
}
void rotate(vector<int>& nums, int k)
{
int lg=nums.size();k%=lg;
reserve(nums,0,lg-1);
reserve(nums,0,k-1);
reserve(nums,k,lg-1);
}
int main()
{
vector<int>nums;int temp,k;
cin>>k;
while(cin>>temp)
{
nums.push_back(temp);
}
rotate(nums,k);
for(auto num:nums) cout<<num<<ends;
cout<<endl;
return 0;
}
存在重复元素
题意:
给一个整数数组,如果存在重复的数字返回true
解:
数字范围很大,不能bool数组,可以map或者set这种log级别的查找
先排序再遍历也可以,C++11 std::sort
能稳定在O(NlogN)
左右
代码:
#include<bits/stdc++.h>
using namespace std;
bool containsDuplicate(vector<int>& nums)
{
int lg=nums.size();
sort(nums.begin(),nums.end());
for(int i=1;i<lg;i++)
{
if(nums[i]==nums[i-1]) return true;
}
return false;
}
int main()
{
vector<int> nums;int temp;
while(cin>>temp)
{
nums.push_back(temp);
}
bool ans=containsDuplicate(nums);
cout<<ans<<endl;
return 0;
}
两个数组的交集 II
题意:
字面意思,求两个数组交集
解:
先排序,然后直接intersection函数求交集,或者双指针处理
T1-免排序
T2-按T3,map方式(数量少,免排序)
T3-内存有限,map或者bool数组(num[i]
很小)存个数,然后遍历nums2
代码:
intersection:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2)
{
vector<int>ans;
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
set_intersection(nums1.begin(),nums1.end(),nums2.begin(),nums2.end(),inserter(ans,ans.begin()));
return ans;
}
T3-内存限制:
#include<bits/stdc++.h>
using namespace std;
vector<int> intersect(vector<int>& nums1, vector<int>& nums2)
{
vector<int>book(1001);
vector<int>ans;
for(auto num:nums1) book[num]++;
for(auto num:nums2)
{
if(book[num])
{
ans.push_back(num);
book[num]--;
}
}
return ans;
}
int main()
{
vector<int> nums1,nums2;int temp;
while(cin>>temp)
{
nums1.push_back(temp);
}
cin.clear();
while(cin>>temp)
{
nums2.push_back(temp);
}
vector<int> ans=intersect(nums1,nums2);
for(auto a:ans) cout<<a<<ends;
cout<<endl;
return 0;
}
加一
题意:
用vector容器存储了一个非负整数,求计算这个整数+1后的值用vector存储并返回
解:
类似字符串大整数加法,从尾部遍历,一个bool存储进位即可
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> plusOne(vector<int>& digits)
{
int lg=digits.size(),add=0;
digits[lg-1]++;
for(int i=lg-1;i>=0;i--)
{
digits[i]+=add;
add=digits[i]/10;
digits[i]%=10;
}
vector<int>ans;
if(add) ans.push_back(add);
for(auto &digit:digits) ans.push_back(digit);
return ans;
}
int main()
{
vector<int> digits;int temp;
while(cin>>temp)
{
digits.push_back(temp);
}
vector<int>ans=plusOne(digits);
for(auto a:ans) cout<<a<<ends;
cout<<endl;
return 0;
}
移动零
题意:
给定一个数组,将零全部移动到后面去,其他元素要保持原序
解:
O(2n)
双指针,一个指向最靠前面的0,一个指向最靠前面的非0数字,如果0在非零前面,交换
O(n)
由于第N位非0数字应该在第N位,所以index存储目前有几个非0数字,遇到非零数字和对应位置交换(下标偏差)
代码:
#include<bits/stdc++.h>
using namespace std;
void moveZeroes(vector<int>& nums)//N
{
int lg=nums.size(),index=-1;
for(int i=0;i<lg;i++) if(nums[i]!=0) swap(nums[++index],nums[i]);
/* //Check
for(auto num:nums) cout<<num<<ends;
cout<<endl;*/
}
/*
void moveZeroes(vector<int>& nums)//2N
{
int l=-1,r=-1,lg=nums.size();
while(true)
{
while(++l<lg && nums[l]!=0);//指向最近的0
while(++r<lg && nums[r]==0);//指向最近的非0
if(r==lg || l==lg) break;
//cout<<l<<" "<<r<<endl;
if(l<r) swap(nums[l],nums[r]);
else l--;
}
//Check
for(auto num:nums) cout<<num<<ends;
cout<<endl;
}*/
int main()
{
vector<int> nums;int temp;
while(cin>>temp)
{
nums.push_back(temp);
}
moveZeroes(nums);
return 0;
}
两数之和
题意:
给定一个数组nums和整数K,求当i != j
时nums[i]+nums[j]==K
,返回任意顺序的 i 和 j
解:
map,第一遍遍历存数字A第一次出现的位置,第二遍遍历对于每个A找K-A是否存在(特判A==K-A)
优化:只需要一遍遍历,处理A的时候先不把A加入map,直接查找K-A是否已经存在,就能解决A==K-A的特判
unordered_map:操作快,但不稳定,空间占用大部分情况下大于map
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target)
{
map<int,int>mp;
int lg=nums.size();
for(int i=0;i<lg;i++)
{
if(mp.count(target-nums[i])) return {mp[target-nums[i]],i};
else mp[nums[i]]=i;
}
return {0,1};
}
int main()
{
vector<int> nums;int t,temp;cin>>t;
while(cin>>temp)
{
nums.push_back(temp);
}
vector<int>ans=twoSum(nums,t);
for(auto a:ans) cout<<a<<ends;
cout<<endl;
return 0;
}
有效的数独
题意:
给一个9*9的char数组,表示一个数独,检查每一行,每一列和每个小九宫格里是否有重复的数字
解:
三次遍历,行检查、列检查、小九宫格检查,int使用位运算替换数组进行更快的取值判断和初始化
应该有比官方题解使用更少空间(book进行标记,num可以替换,starts可以换成9段循环或者一大段if条件)
官方题解:使用多个数组存储每行、每列、每个小九宫格里的数字数量,遍历一次,通过位置加入数组
代码:
#include<bits/stdc++.h>
using namespace std;
bool isValidSudoku(vector<vector<char>>& board)
{
int book,num;
for(int i=0;i<9;i++)
{
book=0;
for(int j=0;j<9;j++)
{
if(board[i][j]=='.') continue;//忽略.
else
{
num=board[i][j]-'0';
if(book & (1<<num)) return false;
else book=book|(1<<num);
}
}
}
for(int i=0;i<9;i++)
{
book=0;
for(int j=0;j<9;j++)
{
if(board[j][i]=='.') continue;//忽略.
else
{
num=board[j][i]-'0';
if(book & (1<<num)) return false;
else book=book|(1<<num);
}
}
}
int starts[9][2]={ {0,0},{3,0},{6,0},{0,3},{3,3},{6,3},{0,6},{3,6},{6,6} };
for(auto start:starts)
{
book=0;
for(int i=start[0];i<start[0]+3;i++)
{
for(int j=start[1];j<start[1]+3;j++)
{
if(board[i][j]=='.') continue;//忽略.
else
{
num=board[i][j]-'0';
if(book & (1<<num)) return false;
else book=book|(1<<num);
}
}
}
}
return true;
}
int main()
{
vector<vector<char>> board;int row;cin>>row;
while(row--)
{
vector<char>temp;char ctemp;
while(cin>>ctemp)
{
temp.push_back(ctemp);
}
board.push_back(temp);
}
bool ans=isValidSudoku(board);
cout<<ans<<endl;
return 0;
}
旋转图像
题意:
一个N*N数组,顺时针旋转90度,要求原地算法
解:
因为是旋转90度,所以每四个格子相互关联,推一下下标变换就可以了,处理时注意第一行是(0,lg-2)
第二行就是(1,lg-3)
因为每次旋转完成相当于少一层,行列长度均-2(头尾),同时每行最后一个是由第一个变换来的,再推一下关系
还有一个官方题解两次翻转,上下翻转+对角线翻转,挺妙的,了解一下就行
代码:
#include<bits/stdc++.h>
using namespace std;
void rotate(vector<vector<int>>& matrix)
{
int lg=matrix.size();
for(int i=0;i<lg/2;i++)
{
for(int j=0;j<lg;j++)
{
swap(matrix[i][j],matrix[lg-i-1][j]);
}
}
for(int i=0;i<lg;i++)
{
for(int j=i;j<lg;j++)
{
swap(matrix[i][j],matrix[j][i]);
}
}
}
/*
void rotate(vector<vector<int>>& matrix)
{
int lg=matrix.size();
for(int i=0;i<lg-1;i++)
{
for(int j=i;j<lg-i-1;j++)
{
int temp=matrix[i][j];
swap(temp,matrix[j][lg-i-1]);
swap(temp,matrix[lg-i-1][lg-j-1]);
swap(temp,matrix[lg-j-1][i]);
swap(temp,matrix[i][j]);
}
}
}*/
int main()
{
vector<vector<int>> matrix;int row;cin>>row;
while(row--)
{
vector<int>temp;int itemp;
while(cin>>itemp)
{
temp.push_back(itemp);
}
matrix.push_back(temp);
cin.clear();
}
rotate(matrix);
for(auto row:matrix)
{
for(int col:row) cout<<col<<ends;
cout<<endl;
}
return 0;
}