leetcode 刷题记录(无序)
2.两数相加:
要熟悉链表操作。
/**
* 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
int x=0,cnt=0;
ListNode *ans=nullptr;
ListNode *tmp=nullptr;
while(l1!=nullptr&&l2!=nullptr){
x=cnt+l1->val+l2->val;
if(!ans) tmp=ans=new ListNode(x%10);
else {
ans->next=new ListNode(x%10);
ans=ans->next;
}
l1=l1->next;
l2=l2->next;
if(x>=10) cnt=1;
else cnt=0;
}
while(l1!=nullptr){
x=cnt+l1->val;
ans->next=new ListNode(x%10);
l1=l1->next;
ans=ans->next;
if(x>=10) cnt=1;
else cnt=0;
}
while(l2!=nullptr){
x=cnt+l2->val;
ans->next=new ListNode(x%10);
l2=l2->next;
ans=ans->next;
if(x>=10) cnt=1;
else cnt=0;
}
if(cnt>=1) ans->next=new ListNode(cnt);
return tmp;
}
};
5.最长回文子串:
N方的复杂度算法,用的动态规划,如果想知道怎么用N的方法写出来可以学一下马拉车。
class Solution {
public:
string longestPalindrome(string s) {
int len=s.length();
if(len<2) return s;
vector<vector<int>> vec(len,vector<int>(len));//定义二维动态数组的大小
int st,mxlen=0;
for(int i=0;i<len;i++)
vec[i][i]=1;
for(int l=2;l<=len;l++){
int j;
for(int i=0;i<len;i++){
j=l+i-1;
if(j>=len) break;
if(s[i]!=s[j]){
vec[i][j]=0;
}
else{
if(j-i+1<3)
vec[i][j]=1;
else vec[i][j]=vec[i+1][j-1];
}
}
}
for(int i=0;i<len;i++)
for(int j=i;j<len;j++){
if(vec[i][j]&&j-i+1>mxlen){
mxlen=j-i+1;
st=i;
}
}
return s.substr(st,mxlen);
}
};
6.Z字形变换:
找一下规律就好了,因为条件判断的等于号没有加上,坑了我很久,做题还是不是很仔细。
class Solution {
public:
string convert(string s, int numRows) {
string ans=s;
int n=s.length();
if(numRows==1) return s;
else if(numRows==2){
int i=0,j=0;
while(i<n){
ans[j]=s[i];
i+=2,j++;
}
i=1;
while(i<n){
ans[j]=s[i];
i+=2,j++;
}
return ans;
}
int m=(numRows-3)*2+4;
int j=0,i=0,k=0;
while(j<n&&i<numRows){
// cout<<i<<" "<<m<<" "<<j<<endl;
if(i==0||i==numRows-1){
if(i+k*m>=n){
i++; k=0;
continue;
}
ans[j]=s[i+k*m];
j++; k++;
}
else{
if(k==0){
ans[j]=s[i];
j++; k++;
}
if(i+k*m-2*i>=n){
i++; k=0; continue;
}
else if(i+k*m-2*i<n){
ans[j]=s[i+k*m-2*i];
j++;
}
if(i+k*m>=n){
i++; k=0; continue;
}
else if(i+k*m<n){
ans[j]=s[i+k*m];
j++; k++;
}
}
}
return ans;
}
};
7.整数反转
注意溢出处理
class Solution {
public:
int reverse(int x) {
int ans=0;
int maxn=INT_MAX,minn=INT_MIN;
if(x==0) return 0;
while(x){
if(x>0&&(maxn-x%10)/10<ans) return 0;
if(x<0&&(minn-x%10)/10>ans) return 0;
ans=ans*10+(x%10);
x/=10;
}
return ans;
}
};
8.字符串转换整数:
注意符号和越界。
class Solution {
public:
int myAtoi(string s) {
long long ans=0;
string str="";
int n=s.length();
int sng=1;
int flag=0;
int i=0;
while(s[i]==' ') i++;
if(flag==0){
if(s[i]=='-') sng=-1,i++;
else if(s[i]=='+') sng=1,i++;
}
while(i<n){
if(flag==2) break;
if(!flag&&s[i]<='9'&&s[i]>='0'){
flag=1;
str=str+s[i];
i++;
}
while(s[i]<='9'&&s[i]>='0'&&i<n){
str=str+s[i];
i++;
}
if(s[i]>'9'||s[i]<'0'){
flag=2;
}
i++;
}
int j=0;
while(str[j]=='0') j++;
if(str.length()-j+1>=13) return sng==1?INT_MAX:INT_MIN;
while(j<str.length()){
ans=ans*10+str[j]-'0';
j++;
}
ans=sng*ans;
if(ans<=INT_MIN) return INT_MIN;
else if(ans>=INT_MAX) return INT_MAX;
else return ans;
}
};
9.回文数:
class Solution {
public:
bool isPalindrome(int x) {
if(x<0) return 0;
string s;
char c;
while(x){
c=char(x%10+'0');
s=s+c;
x/=10;
}
string s2=s;
reverse(s.begin(),s.end());
if(s==s2) return 1;
else return 0;
}
};
11.盛最多水的容器:
双指针就可以过。
class Solution {
public:
int maxArea(vector<int>& height) {
int len=height.size();
int ans=0;
int l=0,r=len-1;
ans=(r-l)*min(height[r],height[l]);
while(l<r){
if(height[l]<=height[r]){
int tl=l;
while(l<len&&height[l]<=height[tl]) l++;
}
else{
int tr=r;
while(r>=0&&height[r]<=height[tr]) r--;
}
int tmp=0;
if(l<len&&r>=0)
tmp=(r-l)*min(height[l],height[r]);
ans=max(tmp,ans);
}
return ans;
}
};
12.整数转罗马:
小模拟。
class Solution {
public:
string intToRoman(int num) {
int t,h,s,n;
string ans="";
t=num/1000; h=num/100%10;
s=num/10%10; n=num%10;
while(t--) ans+='M';
if(h<4){
while(h--)
ans+='C';
}
else if(h==4) ans+="CD";
else if(h==9) ans+="CM";
else {
ans+='D';
h-=5;
while(h--) ans+='C';
}
if(s<4){
while(s--) ans+='X';
}
else if(s==4) ans+="XL";
else if(s==9) ans+="XC";
else{
ans+="L";
s-=5;
while(s--) ans+='X';
}
if(n<4){
while(n--) ans+="I";
}
else if(n==4) ans+="IV";
else if(n==9) ans+="IX";
else {
ans+='V';
n-=5;
while(n--) ans+='I';
}
return ans;
}
};
1515.服务器中心的最小值
题目链接
题意:给n个点的二维坐标,找一个中心点求出它到其它所有点的距离的和的小值。
- 三分套三分:
class Solution {
public:
const double eps=1e-6;
double ans=0,ly,lx,rx,ry;
vector<double>tx,ty;
double solve(double x,double y){
double dx=0,dy=0,sum=0;
for(int i=0;i<tx.size();i++){
dx=x-tx[i]; dy=y-ty[i];
sum+=sqrt(dx*dx+dy*dy);
}
return sum;
}
double f(double x){
double l=ly,r=ry;
double m1,m2;
m1=l+(r-l)/3;
m2=r-(r-l)/3;
while(abs(m1-m2)>=eps){
if(solve(x,m1)<solve(x,m2)) r=m2;
else l=m1;
m1=l+(r-l)/3;
m2=r-(r-l)/3;
}
return solve(x,l);
}
double getMinDistSum(vector<vector<int>>& positions) {
vector<vector<int>> ps=positions;
for(int i=0;i<ps.size();i++)
tx.push_back((double)ps[i][0]),ty.push_back((double)ps[i][1]);
lx=tx[0],rx=tx[0],ly=ty[0],ry=ty[0];
for(int i=1;i<tx.size();i++)
lx=min(lx,tx[i]),rx=max(rx,tx[i]),ly=min(ly,ty[i]),ry=max(ry,ty[i]);
double l=lx,r=rx;
double m1=l+(r-l)/3,m2=r-(r-l)/3;
while(abs(m1-m2)>eps){
if(f(m1)<f(m2)) r=m2;
else l=m1;
m1=l+(r-l)/3;
m2=r-(r-l)/3;
}
return f(l);
}
};
995.K连续位的最小翻转次数:
题意:长度为n的数组,每次可以任意选择长度为k的连续子数组进行翻转,翻转的过程是0->1,1->0。求最小的翻转次数。
思路:看到这个题的第一个想法就是差分,之前在洛谷也做过差分的题,可以看看这题思路很像。设置一个差分数组d[]记录相邻两个位置上的翻转次数之差,如果i位置需要翻转,便修改d[i+k]上的值。
class Solution {
public:
int minKBitFlips(vector<int>& A, int K) {
int len=A.size();
vector<int> d(len+1);
int ans=0,cnt=0;
for(int i=0;i<len;i++){
cnt+=d[i];//差分数组前缀和就是当前的翻转次数
if((A[i]+cnt)%2==0){
if(i+K>len){
return -1;
}
d[i+K]--;
ans++;//增加翻转次数
cnt++;
}
}
return ans;
}
};
28.实现strSTR
就是KMP的板子题,但是不知道为什么当把j==-1条件的判断放在后面的时候会报溢出错误,但是本地的IDE跑出来没有问题。
while(i<len1&&j<len2){
if(haystack[i]==needle[j]||j==-1){
i++,j++;
}
else j=nxt[j];
}
AC代码:
class Solution {
public:
int strStr(string haystack, string needle) {
int i=0,j=0,k=-1;
int len1=haystack.size(),len2=needle.size();
vector<int> nxt(len2+10);
nxt[0]=-1;
while(i<len2-1){
if(k==-1||needle[i]==needle[k]){
i++,k++;
nxt[i]=k;
}
else k=nxt[k];
}
i=0,j=0;
while(i<len1&&j<len2){
if(haystack[i]==needle[j]||j==-1){
i++,j++;
}
else j=nxt[j];
}
if(j>=len2) return i-len2;
else return -1;
}
};
112.路径总和
经典的广搜和宽搜,或者递归。1.注意广搜的时候返回值不一定是成功的那一条路径所以要把所有的记录一下,还有就是空字符串的判断和开始搜索时是让sum=0开始搜索还是从sum=root->val开始都要注意。附上三者代码。2.递归和广搜其实差不多,但是最终可以通过左右儿子的或运算解决。
//dfs:
class Solution {
public:
int ans=0;
void dfs(TreeNode* root,int targetSum,int sum){
if(root->left==nullptr&&root->right==nullptr){
if(sum==targetSum) ans++;
return ;
}
if(root->left!=nullptr){
int tmp=root->left->val;
dfs(root->left,targetSum,sum+tmp);
}
if(root->right!=nullptr){
int tmp=root->right->val;
dfs(root->right,targetSum,sum+tmp);
}
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(root==nullptr) return 0;
dfs(root,targetSum,root->val);
if(ans>=1)
return 1;
else return 0;
}
};
//递归:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root==nullptr) return 0;
if(root->left==nullptr&&root->right==nullptr)
return targetSum==root->val;
return hasPathSum(root->right,targetSum-root->val)||hasPathSum(root->left,targetSum-root->val);
}
};
//bfs:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root==nullptr) return 0;
queue<TreeNode *> que;
queue<int> que2;
que.push(root);
que2.push(root->val);
while(!que.empty()){
TreeNode *t1=que.front();
int tmp=que2.front();
que.pop();
que2.pop();
if(t1==nullptr) return 0;
if(t1->left==nullptr&&t1->right==nullptr){
if(tmp==targetSum) return 1;
continue;
}
if(t1->left!=nullptr)
que.push(t1->left),que2.push(tmp+t1->left->val);
if(t1->right!=nullptr)
que.push(t1->right),que2.push(tmp+t1->right->val);
}
return 0;
}
};
819.最常见字符串:
字符串的题每次调试花的时间挺长的,首先是substr()的形参忘了后者是长度不是结尾的字符位置调试挺久的,其次转大小写那儿,脑子犯糊涂写错了。
class Solution {
public:
string mostCommonWord(string paragraph, vector<string>& banned) {
map<string ,int > mmap;
vector<string> used;
int j=0;
for(int i=0;i<paragraph.size();i++){
string str="";
j=i;
bool flag=0;
while((paragraph[i]>='a'&¶graph[i]<='z')||(paragraph[i]<='Z'&¶graph[i]>='A')){
i++; flag=1;
}
if(flag&&i!=0){
str=paragraph.substr(j,i-j);//忘记了substr()后面的那个形参是指的长度
for(int i=0;i<str.size();i++){
if(!islower(str[i]))
str[i]=towlower(str[i]);
}
mmap[str]++;
used.push_back(str);
}
}
string ans;
int maxm=0;
for(int i=0;i<used.size();i++){
vector<string>::iterator it=find(banned.begin(),banned.end(),used[i]);
if(it==banned.end()&&mmap[used[i]]>maxm){
ans=used[i];
maxm=mmap[used[i]];
}
}
return ans;
}
};
820.单词压缩
两种方法:1.哈希表set去重后缀,结果是剩余每个单词长度加1。2.字典树。
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
unordered_set<string> se(words.begin(),words.end());
for(const string& word:se){
for(int i=1;i<word.size();i++){
se.erase(word.substr(i));
}
}
int ans=0;
for(const string& word:se){
ans+=word.size()+1;
}
return ans;
}
};
91.解码方法
经典的DP,如果s[0]=='0’就直接结束了,输出0。如果s[0]!=‘0’,dp[0]=1;接下来分两种情况:1.之后就是i位置上的数和前一位的数是否拆开,如果拆开dp[i]+=dp[i-1]。(拆开条件:s[i]!=‘0’)2.如果不拆开,题中说了06这种不符合条件,所以dp[i]+=dp[i-2](不拆开条件dp[i-1]!=‘0’&&dp[i-1]*10+dp[i]-‘0’<=26)。
class Solution {
public:
int numDecodings(string s) {
int n=s.size();
vector<int> dp(n+100);
if(s[0]=='0') return 0;
else dp[0]=1;
bool flag=0;
if((s[0]-'0')*10+(s[1]-'0')<=26) dp[1]+=dp[0],flag=1;//不拆
if(s[1]!='0') dp[1]+=dp[0],flag=1;//拆
if(!flag) return 0;
for(int i=2;i<=n-1;i++){
flag=0;
if(s[i-1]!='0'&&((s[i-1]-'0')*10+s[i]-'0')<=26)
dp[i]+=dp[i-2],flag=1;//不拆
if(s[i]!='0')//拆
dp[i]+=dp[i-1],flag=1;
if(!flag) return 0;
}
return dp[n-1];
}
};
952.按公因数计算最大组件:
看到题就直接用并查集写了,但是超时了,因为在合并两个并查集的时候,用了双层循环复杂度直接N方了,然后想到了用每个数因子对并查集连接,过了。
超时代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int> f;
vector<int> vis;
int Find(int x){
while(x!=f[x])
x=f[x]=f[f[x]];
return x;
}
void emerge(int x,int y){
int fx=Find(x),fy=Find(y);
if(fx!=fy)
f[fy]=fx,vis[fx]=vis[fy]+vis[fx];
}
int main() {
int n; cin>>n;
vector<int> A(n);
for(int i=0;i<n;i++)
cin>>A[i];
for(int i=1;i<=100005;i++)
f.push_back(i-1),vis.push_back(1);
for(int i=0;i<A.size();i++){
for(int j=i+1;j<A.size();j++){
if(__gcd(A[i],A[j])>1)
emerge(A[i],A[j]);
}
}
int ans=0;
for(int i=0;i<A.size();i++){
if(f[A[i]]==A[i]&&vis[A[i]]>ans){
ans=vis[A[i]];
// cout<<A[i]<<" "<<f[A[i]]<<endl;
}
}
// cout<<ans<<endl;
system("pause");
return 0;
}
AC代码:
class Solution {
public:
int largestComponentSize(vector<int>& A) {
unordered_map<int,int> f;
for(auto a: A){
for(int i=2;i<=sqrt(a);i++){
if(a%i==0)
f[Find(f,i)]=f[Find(f,a/i)]=f[Find(f,a)];//根据因子进行合并
}
}
unordered_map<int,int> cnt;
int ans=0;
for(auto a:A){
++cnt[Find(f,a)];
if(cnt[Find(f,a)]>ans) ans=cnt[Find(f,a)];
}
return ans;
}
int Find(unordered_map<int,int> &vec,int x){
if(!vec.count(x)) return vec[x]=x;//如果没有直接等于本身
while(vec[x]!=x)
x=vec[x]=vec[vec[x]];
return x;
}
};
504.七进制转换
string的加法十分友好,如果是在本字符串前面加,加号和加数放前面,反之,相反。
class Solution {
public:
string convertToBase7(int num) {
int x=abs(num);
string ans="";
if(x==0) return "0";
while(x>0){
int tmp=x%7;
ans=(char)(tmp+'0')+ans;
x/=7;
}
if(num<0)
ans="-"+ans;
return ans;
}
};
503.下一个最大元素2:
单调栈维护没找到最大元素数组的下标。
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int len=nums.size();
vector<int> num(len*2);
vector<int> ans(len);
for(int i=0;i<len;i++){
num[i]=num[i+len]=nums[i];
ans[i]=-1;
}
stack<int> st;
for(int i=0;i<len*2-1;i++){
while(!st.empty()&&num[i%len]>num[st.top()]){
ans[st.top()]=num[i%len];
st.pop();
}
st.push(i%len);
}
return ans;
}
};
1011.在D天内送达包裹的能力:
这题是一个二分的题,要找的是最小的载重,载重的左右边界分别是数组的最大值,数组总和,如果在mid的载重下可以在给定的天数内完成修改右边界,不可以修改左边界。
class Solution {
public:
int shipWithinDays(vector<int>& weights, int D) {
int mmax=-100;
int sum=0;
int len=weights.size();
for(int i=0;i<len;i++) sum+=weights[i],mmax=max(mmax,weights[i]);
int l=mmax,r=sum;
int mid=0;
while(l<r){
mid=(l+r)>>1;
int cnt=1,cur=0,i=0;
while(i<len){
cur+=weights[i];
if(cur>mid){
cnt++;
cur=weights[i];
}
i++;
}
if(cnt>D) l=mid+1;
else if(cnt<=D) r=mid;
}
return l;
}
};
字符串的滑动窗口。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> st;
int r=-1;
int len=s.length();
int ans=0;
for(int i=0;i<len;i++){
if(i!=0){
st.erase(s[i-1]);
}
while(r+1<len&&!st.count(s[r+1])){
st.insert(s[r+1]);
r++;
}
ans=max(ans,r-i+1);
}
return ans;
}
};
二分:
33.搜索旋转排序数组
class Solution {
public:
int search(vector<int>& nums, int target) {
int n=nums.size();
int l=0,r=n-1;
while(l<r){
int mid=(l+r+1)>>1;
if(nums[mid]>=nums[0]){
l=mid;
}
else r=mid-1;
}
if(target>=nums[0]){
l=0;
}
else{
r=n-1; l=l+1;
}
while(l<r){
int mid=(l+r)>>1;
if(nums[mid]>=target)
r=mid;
else l=mid+1;
}
return nums[r]==target?r:-1;
}
};
1.为什么check(mid)==true,修改l和修改r的时候mid的计算方法不同。2.最后二分得出结果时,有时是用l有时是r。3.比使用二分时,去要求范围内是单调的更重要的是理解二分的二段性!
29.两数相除:
快速乘(倍增)+二分。会有溢出,所以用long long。
class Solution {
public:
int divide(int dividend, int divisor) {
int flag1=dividend>=0?1:-1,flag2=divisor>=0?1:-1;
int sng=flag1*flag2;
int maxn=INT_MAX,minn=INT_MIN;
if(divisor==1||divisor==-1){
if(dividend==minn)
return sng==1?maxn:dividend;
}
if(divisor==minn){
if(dividend!=minn) return 0;
else return 1;
}
long long y=abs(dividend),x=abs(divisor);
long long l=0,r=y;
while(l<r){
long long mid=(l+r)>>1;
if(y<=quip(x,mid))
r=mid;
else l=mid+1;
}
return sng*l;
}
long long quip(long x,long i){
long ans=0;
if(i==1||x==0) return x;
while(i){
if(i&1) ans=ans+x;
x+=x;
i>>=1;
}
return ans;
}
};
81搜索排序数组:
数组中有重复元素,先恢复二段性,再找出旋点,再二分查找。
class Solution {
public:
bool search(vector<int>& nums, int target) {
int n=nums.size();
if(n==1) return nums[0]==target;
int l=0,r=n-1;
int R=r;//恢复二段性后的右边界
while(nums[R]==nums[0]&&R>0) R--;
r=R;
while(l<r){
int mid=(l+r+1)>>1;
if(nums[mid]>=nums[0]) l=mid;
else r=mid-1;
}
if(target<nums[0]&&l<R) l++,r=R;
else l=0;
while(l<r){
int mid=l+r>>1;
if(nums[mid]>=target) r=mid;
else l=mid+1;
}
if(nums[l]==target) return true;
else return false;
}
};
4.寻找两个正序数组的中位数:
第一个想法都是合并两个数组,但是这样复杂读太大了,所以就就想到二分,但是怎么二分呢?1.如果合并后的数组是奇数的长度,中位数就一个,如果合并后的数组长度是偶数,中位数就有两个。2.中位数之前的数肯定都小于它,之后的数肯定都大于它,但是在两个数组之间要怎么去划分?假设两个数组正好平分了中位数之前的数,那就找下标是a[k/2-1]和b[k/2-1]比较,如果a[k/2-1]<=b[k/2-1],那么可以排除a数组包括位置k/2-1及之前的数,因为最多也就(k/2-1)*2+1个数小于k,所以每次排除k/2个数,只需要注意好下标的变化,以及当index+k/2-1大于该数组的长度时这类边界条件的判断。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1=nums1.size(),len2=nums2.size();
if((len1+len2)%2!=0) return get(nums1,nums2,(len1+len2+1)/2);
else return (get(nums1,nums2,(len1+len2)/2)+get(nums1,nums2,(len1+len2)/2+1))/2.0;
}
double get(vector<int>& a,vector<int>& b,int k){
int len1=a.size(),len2=b.size();
int i1=0,i2=0;
while(1){
if(i1==len1){
return b[i2+k-1];
}
else if(i2==len2){
return a[i1+k-1];
}
if(k==1){
return min(a[i1],b[i2]);
}
int h=k/2;
int i11=min(i1+h,len1)-1;
int i22=min(i2+h,len2)-1;
int p1=a[i11],p2=b[i22];
if(p1<=p2){
k-=i11-i1+1;
i1=i11+1;
}
else {
k-=i22-i2+1;
i2=i22+1;
}
}
}
};
赛车: