编程语言:C++
第一部分-数组简介
1. 寻找数组的中心索引
自己做法总结:
下面第一个程序存在bug,不过系统可以通过
class Solution {
public:
int pivotIndex(vector<int>& nums) {
//获取数组个数
int N=nums.size();
/**********下面这个说法是错误的,故需要更正**********/
//元素少于三个是没有中心索引的
if(N<=2)
return -1;
else
{
//前累加和、后累加和
int sumpre=0;
int sumpos=0;
for(int i=1;i<N;i++)
{
sumpos+=nums[i];
}
//从下标0扫描到下标N-1
int j;
for(j=0;j<N;j++)
{
if(sumpre==sumpos)
break;
else
{
//向后累加
sumpre+=nums[j];
//向后累减
sumpos-=nums[j+1];
}
}
if(j==N)
return -1;
else
return j;
}
}
};
更正程序
class Solution {
public:
int pivotIndex(vector<int>& nums) {
//获取数组个数
int N=nums.size();
//前累加和、后累加和
int sumpre=0;
int sumpos=0;
for(int i=1;i<N;i++)
{
sumpos+=nums[i];
}
//从下标0扫描到下标N-1
int j;
for(j=0;j<N;j++)
{
if(sumpre==sumpos)
break;
else
{
//向后累加
sumpre+=nums[j];
//向后累减
sumpos-=nums[j+1];
}
}
if(j==N)
return -1;
else
return j;
}
};
大佬做法总结:
class Solution {
public:
int pivotIndex(vector<int>& nums) {
int sum=0;
int sumleft=0;
int len=size(nums);
//计算sum
for(int i=0;i<len;i++)
{
sum+=nums[i];
}
//从前往后扫描
for(int j=0;j<len;j++)
{
if(sumleft*2+nums[j]==sum)
return j;
else
sumleft+=nums[j];
}
return -1;
}
};
2 . 搜索插入位置
个人思路总结:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
//获取数组个数
int len=size(nums);
//扫描
int i;
for(i=0;i<len;i++)
{
if(target<=nums[i])
return i;
}
return len;
}
};
大佬解法:二分查找
这里可以看一下这个 https://www.zhihu.com/question/36132386
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
//求非降序范围[first, last)内第一个不小于value的值的位置
int len=size(nums);
int first=0;
int last=len;
int mid;
while(first<last)//搜索区间[first, last)不为空
{
mid=first+(last-first)/2;//防溢出
if(nums[mid]<target)
first=mid+1;
else
last=mid;
}
return first;//last也行,因为[first, last)为空的时候它们重合
}
};
3合并区间
个人思路总结
Tips
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
//二维数组
vector<vector<int>> temp;
//获取原数组行数
int row=intervals.size();
sort(intervals.begin(), intervals.end());
//扫描
int j=0;
int flag=0;
int maxR=0;
if(row==0)
{
return {
};
}
else if(row==1)
{
temp.push_back({
intervals[0][0],intervals[0][1]});
}
else
{
int i;
for(i=0;i<row-1;i++)
{
//保留第一元素,上锁
if(flag==0)
{
temp.push_back({
intervals[i][0],intervals[i][1]});
flag=1;
}
//记录目前所有[L,R]最大的R
maxR=max(maxR,intervals[i][1]);
//如果存在数组存在间距,解锁并保留第二元素
if(flag==1)
{
if(maxR<intervals[i+1][0])
{
temp.back()[1]=maxR;
flag=0;
}
}
}
maxR=max(maxR,intervals[i][1]);
if(flag==1)//如果没有解锁,则需要针对最后一个
{
temp.back()[1]=maxR;
}
else
temp.push_back({
intervals[i][0],intervals[i][1]});
}
return temp;
}
};
大佬做法
做法一总结:
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
//空数组直接返回
if(intervals.size()==0)
{
return {
};
}
vector<vector<int>> temp;
//依据L排序
sort(intervals.begin(),intervals.end());
for(int i=0;i<intervals.size();i++)
{
int L=intervals[i][0],R=intervals[i][1];
if(!temp.size()||temp.back()[1]<L)//只有在第一次或者temp顶部信息R
{
temp.push_back({
L,R});
}
else//更新
{
temp.back()[1]=max(temp.back()[1],R);
}
}
return temp;
}
};
解法二总结
基本思路:排序+双指针
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> temp;
sort(intervals.begin(),intervals.end());
for(int i=0;i<intervals.size();)
{
int maxR=intervals[i][1];
int j=i+1;
while(j<intervals.size()&&maxR>=intervals[j][0])
{
maxR=max(maxR,intervals[j][1]);
j++;
}
temp.push_back({
intervals[i][0],maxR});
i=j;
}
return temp;
}
};
第二部分-二维数组简介
1旋转矩阵
方法一 :使用辅助数组
个人思路总结
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
//获取矩阵行数
int size=matrix.size();
//矩阵的拷贝
auto matrix_new=matrix;
//行扫描
for(int i=0;i<size;i++)
{
for(int j=0;j<size;j++)
{
matrix_new[j][size-1-i]=matrix[i][j];//i行变成size-1-i列
}
}
//矩阵的反拷贝
matrix=matrix_new;
}
};
官方其他的做法
方法二:原地旋转
个人思路总结:
利用旋转的对称性,一个大矩阵其实是由4个小矩阵组成的。可以通过遍历左上角的矩阵,利用旋转关系实现扫描整个大矩阵。
下面的旋转关系看似复杂,其实就是通过整个顺序进行赋值的
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
//获取矩阵的行数
int size=matrix.size();
//需要旋转的单位长度
int i_size,j_size;
//奇数
if(size%2)
{
i_size=size/2;
j_size=size/2+1;
}
else
{
i_size=size/2;
j_size=size/2;
}
//中间变量
int temp;
//左上角区域
for(int i=0;i<i_size;i++)
{
for(int j=0;j<j_size;j++)
{
temp=matrix[i][j];
matrix[i][j]=matrix[size-1-j][i];
matrix[size-1-j][i]=matrix[size-1-i][size-1-j];
matrix[size-1-i][size-1-j]=matrix[j][size-1-i];
matrix[j][size-1-i]=temp;
}
}
}
};
看了官方的写法的体会:
4 * 4矩阵的小旋转矩阵是22的矩阵
5 * 5的矩阵的小旋转矩阵是23的矩阵
所以行都是size/2,列则是(size+1)/2
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < (n + 1) / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
};
方法三:翻转矩阵
旋转矩阵实际上是进行一次行对称翻转+一次对角线翻转
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
//获取矩阵的行数
int size=matrix.size();
//上下翻转
for(int i=0;i<size/2;i++)
{
for(int j=0;j<size;j++)
{
swap(matrix[i][j],matrix[size-1-i][j]);
}
}
//对角线翻转
for(int i=0;i<size;i++)
{
for(int j=0;j<i;j++)
{
swap(matrix[i][j],matrix[j][i]);
}
}
}
};
2零矩阵
个人naive做法总结
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
//过渡矩阵
vector<vector<int>> matrix_new;
matrix_new=matrix;
//获取矩阵的行数和列数
int row=matrix.size();
int col=matrix[0].size();
int flag; //0标志
//scan
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
if(!matrix[i][j])
{
flag=1;
}
if(flag)
{
//行赋值0
for(int k=0;k<col;k++)
{
matrix_new[i][k]=0;
}
//列赋值0
for(int k=0;k<row;k++)
{
matrix_new[k][j]=0;
}
flag=0;
}
}
}
matrix=matrix_new;
}
};
改进思路:前面赋值0的行和列无需再进行扫描
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
//获取行数和列数
int mrow=matrix.size();
int mcol=matrix[0].size();
if(mrow==0||mcol==0)
return;
//两个一维数组
vector<bool> row(mrow,false);
vector<bool> col(mrow,false);
//扫描
for(int i=0;i<mrow;i++)
{
for(int j=0;j<mcol;j++)
{
if(!matrix[i][j])
{
row[i]=true;
col[j]=true;
}
}
}
//矩阵输出
for(int i=0;i<mrow;i++)
{
for(int j=0;j<mcol;j++)
{
if(row[i]||col[j])
matrix[i][j]=0;
}
}
}
};
3对角线遍历
个人思路总结:
一些做题的雷区
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& matrix) {
//建立一维数组
vector<int> nums;
//获取行数和列数//一定要分开书写,因为没行的时候是不会有matrix[0]
int row=matrix.size();
if(row==0)
return nums;
int col=matrix[0].size();
if(col==0)
return nums;
int flag=1;//奇数偶数标志位
for(int i=0,j=0; ;)
{
nums.push_back(matrix[i][j]);
if((i==row-1)&&(j==col-1))
break;
//转折点处i与j的更新
if(flag%2)//奇数上走
{
if((i==0)&&(j==col-1))//遇到右上角点
{
i++;
flag++;
}
else if(i==0)//遇到上边界点
{
j++;
flag++;
}
else if(j==col-1)//遇到右边界点
{
i++;
flag++;
}
else
{
i--;
j++;
}
}
else//偶数下走
{
if((j==0)&&(i==row-1))//遇到左下角点
{
j++;
flag++;
}
else if(i==row-1)//遇到下边界点
{
j++;
flag++;
}
else if(j==0)//遇到左边界点
{
i++;
flag++;
}
else
{
i++;
j--;
}
}
}
return nums;
}
};
看了官方解析后的一些改进:
主要是针对边界处理,以上行为例,之前是分为上边界、右边界、右上角三种情况进行处理。
缩减为上边界和右边界两种情况,但是if判断时必须右边界先进行判断,再判断上边界
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& matrix) {
//建立一维数组
vector<int> nums;
//获取行数和列数//一定要分开书写,因为没行的时候是不会有matrix[0]
int row=matrix.size();
if(row==0)
return nums;
int col=matrix[0].size();
if(col==0)
return nums;
int flag=1;//奇数偶数标志位
for(int i=0,j=0; ;)
{
nums.push_back(matrix[i][j]);
if((i==row-1)&&(j==col-1))
break;
//转折点处i与j的更新
if(flag%2)//奇数上走
{
if(j==col-1)//遇到右边界点
{
i++;
flag++;
}
else if(i==0)//遇到上边界点
{
j++;
flag++;
}
else
{
i--;
j++;
}
}
else//偶数下走
{
if(i==row-1)//遇到下边界点
{
j++;
flag++;
}
else if(j==0)//遇到左边界点
{
i++;
flag++;
}
else
{
i++;
j--;
}
}
}
return nums;
}
};
大佬解法
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& matrix) {
//一维数组
vector<int> result;
//获取行数和列数
int row=matrix.size();
if(row==0) return result;
int col=matrix[0].size();
if(col==0) return result;
int i=0;//第几条对角线
bool flag=true;
while(i<row+col-1)//对角线扫描次数
{
int pm=flag ? row:col;
int pn=flag ? col:row;
int x=i<pm ? i:pm-1;//选取最大的x
int y=i-x;//x+y=对角线次数
while(x>=0 && y<pn)
{
result.push_back(flag ? matrix[x][y]:matrix[y][x]);
x--;
y++;
}
i++;
flag=!flag;
}
return result;
}
};
第三部分-字符串简介
1.最长公共前缀
个人做法总结
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
//最大前缀字符串
string strpre;
//如果少于1个字符串,输出空。
if(strs.size()<1)
return "";
else if(strs.size()==1)//如果等于1个字符串,输出这个字符串。
return strs[0];
int len=strs[0].size();
//寻找最大前缀子串
for(int i=1,k=0;i<strs.size();i++)
{
for(k=0;k<len;k++)
{
if(strs[i][k]==strs[0][k])
{
strpre+=strs[0][k];
}
else
break;
}
len=k;
/新添入,最大子串空的时候提前结束//
if(len<1)
break;
/
if(i!=strs.size()-1)
strpre="";
}
return strpre;
}
};
官方做法一复现:横向比较
总结:
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
//没有字符串时
if(strs.size()<1)
return "";
//大于等于1个字符串的情况
string prefix=strs[0];
for(int i=1;i<strs.size();i++)
{
prefix=longestCommonPrefix(prefix, strs[i]);
if(!prefix.size())
break;
}
return prefix;
}
string longestCommonPrefix(const string& str1, const string& str2)
{
//取两个字符串较短的长度
int len=min(str1.size(),str2.size());
//while比较
int index=0;
while(index<len && str1[index]==str2[index])
{
index++;
}
//妙处
return str1.substr(0,index);
}
};
官方做法二-纵向比较
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
//没有字符串时
if(strs.size()<1)
return "";
//获取第一个字符串的长度
int length=strs[0].size();
//获取字符串个数
int count=strs.size();
//纵向比较
for(int i=0;i<length;i++)
{
int c=strs[0][i];
for(int j=1;j<count;j++)
{
//当字符串长度跟i相等或者字符串的字符不等于c字符的时候就直接返回字符串前缀
if(i==strs[j].size() || strs[j][i]!=c)
{
return strs[0].substr(0,i);
}
}
}
return strs[0];
}
};
官方做法三-分而治之
总结:
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(!strs.size())
return "";
else
return longestCommonPrefix(strs, 0, strs.size()-1);
}
string longestCommonPrefix(vector<string>& strs, int start, int end) {
if(start==end)
return strs[start];
else
{
int mid=(start+end)/2;
string leftLCP=longestCommonPrefix(strs,start,mid);
string RightLCP=longestCommonPrefix(strs,mid+1,end);
return CommonPrefix(leftLCP,RightLCP);
}
}
string CommonPrefix(const string& leftLCP, const string& RightLCP){
int minlength=min(leftLCP.size(),RightLCP.size());
for(int i=0;i<minlength;i++)
{
if(leftLCP[i]!=RightLCP[i])
return leftLCP.substr(0,i);
}
return leftLCP.substr(0,minlength);
}
};
2.最长回文子串
自己没有做出
官方做法一动态规划
总结:
class Solution {
public:
string longestPalindrome(string s) {
//特别处理,字符串长度为0或者1时直接返回s
int len=s.size();
if(len<2)
return s;
//定义最大长度和下标起始点
int maxlen=1;
int begin=0;
//初始化dp二维数组
vector<vector<int>> dp(len, vector<int>(len,0));
for(int i=0;i<len;i++)
{
dp[i][i]=1;
}
//动态规划-先列后行
for(int j=1;j<len;j++)//先列
{
for(int i=0;i<j;i++)//后行
{
if(s[i]!=s[j])//如果左右端点不相等肯定不是回文串
{
dp[i][j]=0;
}
else
{
if(j-i<3)//长度为3或者2的时候肯定是回文串
{
dp[i][j]=1;
}
else//看除端点之外的子串
{
dp[i][j]=dp[i+1][j-1];
}
}
if(dp[i][j] && j-i+1>maxlen)
{
begin=i;
maxlen=j-i+1;
}
}
}
return s.substr(begin,maxlen);
}
};
官方做法二-中心扩散
总结:
class Solution {
public:
pair<int, int> expandAroundCenter(const string& s, int left, int right) {
while (left >= 0 && right < s.size() && s[left] == s[right]) {
--left;
++right;
}
return {
left + 1, right - 1};
}
string longestPalindrome(string s) {
int start = 0, end = 0;
for (int i = 0; i < s.size(); ++i) {
auto [left1, right1] = expandAroundCenter(s, i, i);
auto [left2, right2] = expandAroundCenter(s, i, i + 1);
if (right1 - left1 > end - start) {
start = left1;
end = right1;
}
if (right2 - left2 > end - start) {
start = left2;
end = right2;
}
}
return s.substr(start, end - start + 1);
}
};
3.翻转字符串中的单词
自己做法总结:
class Solution {
public:
string reverseWords(string s) {
string temp;
//字符串长度
int length=s.size();
//从后往前扫描
//双指针。begin指针从后向前寻找单词首字符,end指针从首字符依次向后用于字符复制
int begin,end;
for(int begin=length-1;begin>=0;begin--)
{
if((s[begin]!=' ' && begin==0)||(s[begin]!=' ' && s[begin-1]==' '))//单词的首字符
{
end=begin;
while((s[end]!=' ')&&(end<=s.size()-1))//到达字符串末尾或者单词的尾字符
{
temp.push_back(s[end++]);
}
temp.push_back(' ');
}
}
//去一个末尾空字符
temp.erase(temp.size()-1);
return temp;
}
};
官方做法之自己编写的翻转函数
总结:
class Solution {
public:
string reverseWords(string s) {
//先对整个字符串进行一次大翻转
reverse(s.begin(),s.end());
//获取字符串的长度
int length=s.size();
//从前往后遍历
//一个单词头指针,一个单词尾指针
int start,end=0;
//新字符串的指针
int idx=0;
for(start=0;start<length;start++)
{
//寻找不是空格的字符,也即单词的首字符
if(s[start]!=' ')
{
//每一个单词后面跟的空格
if(idx!=0)
s[idx++]=' ';
//把每一个单词的字符前移复制
end=start;
while(end<length && s[end]!=' ')
{
s[idx++]=s[end++];
}
//对每一个单词进行一次翻转
reverse(s.begin()+idx-(end-start),s.begin()+idx);
//头指针移到尾指针位置
start=end;
}
}
//擦除后面的冗余字符
s.erase(s.begin()+idx,s.end());
return s;
}
};
官方做法三-数据结构栈的使用
总结:
class Solution {
public:
string reverseWords(string s) {
int left=0;
int right=s.size()-1;
//去掉字符串前部空格
while(s[left]==' ')
left++;
//去掉字符串尾部空格
while(s[right]==' ')
right--;
stack<string> temp;
string word;
while(left<=right)//扫描
{
//word不空且遇到空格,说明读完一个单词
if(word.size() && s[left]==' ')
{
temp.push(word);
word="";
}
else if(s[left]!=' ')
{
word+=s[left];
}
left++;
}
temp.push(word);//最后一个单词没有空格需要单独处理
string ans;
while(!temp.empty())
{
//取栈首元素
ans+=temp.top();
//出栈
temp.pop();
if(!temp.empty())
ans+=' ';
}
return ans;
}
};
4.KMP算法的字符串匹配
个人思路总结:
class Solution {
public:
void buildMatch(string needle, int *match)
{
int M=needle.size();
//字符串为1需要单独处理
if(M==1)
match[0]=-1;
else
{
match[0]=-1;
match[1]=0;
int cn=0;
int pos=2;
while(pos<M)
{
//pos-1字符与cn字符相等,则match[pos]=match[pos-1]+1=cn+1
if(needle[pos-1]==needle[cn])
{
match[pos++]=++cn;
}
else if(cn>0)
{
cn=match[cn];
}
else
{
match[pos++]=0;
}
}
}
}
int strStr(string haystack, string needle) {
//获取字符串的长度
int N=haystack.size();
int M=needle.size();
//模式串为空返回0,模式串长度大于原字符串则返回-1
if(M==0)
return 0;
if(M>N)
return -1;
//模式串的前后缀匹配表建立
int *match=(int *)malloc(sizeof(int)*M);
buildMatch(needle,match);
//模式串与原字符串的匹配过程
int n=0,m=0;
while(n<N && m<M)
{
//匹配成功,下标则一同进步
if(haystack[n]==needle[m])
{
n++;
m++;
}
//匹配不成功,模式串需要转到match[]位置
else if(m>0)
{
m=match[m];
}
//匹配不成功,模式串下标且已经退回到模式串0地址
else
{
n++;
}
}
//释放动态数组
free(match);
//依据模式串是否走到末尾决定返回下标
return m==M ? n-m : -1;
}
};
第四部分-双指针技巧
1.反转字符串
时间复杂度:O(N)。N/2次交换
空间复杂度:O(1)
class Solution {
public:
void reverseString(vector<char>& s) {
int begin=0,end=s.size()-1;
while(begin<end)
{
swap(s[begin],s[end]);
begin++;
end--;
}
}
};
2.数组拆分I
个人思路总结
class Solution {
public:
int arrayPairSum(vector<int>& nums) {
int n=nums.size();
sort(nums.begin(),nums.end());
int sum=0;
for(int i=0;i<n;i+=2)
{
sum+=nums[i];
}
return sum;
}
};
官方哈希表做法
总结:
class Solution {
public:
int arrayPairSum(vector<int>& nums) {
//建立哈希表初始化
int arr[20001]={
0};
//i元素的个数存放在arr[i+10000]位置
for(int i=0;i<nums.size();i++)
{
arr[nums[i]+10000]++;
}
//扫描哈希表
int d=0;
int sum=0;
for(int i=0;i<20001;i++)
{
sum+=(arr[i]+1-d)/2*(i-10000);//除2向上取整
d=(2+arr[i]-d)%2;//避免出现负数
}
return sum;
}
};
3.输入有序数组
这道题可以使用 两数之和 的解法,使用 O(n^2)的时间复杂度和 O(1) 的空间复杂度暴力求解,或者借助哈希表使用O(n) 的时间复杂度和 O(n) 的空间复杂度求解。但是这两种解法都是针对无序数组的,没有利用到输入数组有序的性质。利用输入数组有序的性质,可以得到时间复杂度和空间复杂度更优的解法
总结:个人做法没有A过,超出时间限制
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int> temp;
for(int i=0;i<numbers.size()-1;i++)
{
for(int j=i+1;j<numbers.size();j++)
{
if(numbers[i]+numbers[j]==target)
{
temp.push_back(i+1);
temp.push_back(j+1);
break;
}
}
if(temp.size())
break;
}
return temp;
}
};
官方做法一双指针
总结
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int low=0,high=numbers.size()-1;
int sum=0;
while(low<high)
{
sum=numbers[low]+numbers[high];
if(sum==target)
{
return {
low+1,high+1};
}
else if(sum>target)
{
high--;
}
else
{
low++;
}
}
return {
-1,-1};
}
};
个人做法-二分查找
可以看看本人总结的二分查找模板
https://blog.csdn.net/qq_39309050/article/details/109097745
总结
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
for(int i=0;i<numbers.size()-1;i++)
{
int findnum=target-numbers[i];
int low=i+1;
int high=numbers.size();//左闭右开
int mid=0;
//二分查找
while(low<high)
{
mid=low+(high-low)/2;
if(findnum>numbers[mid])
{
low=mid+1;
}
else
{
high=mid;
}
}
if(low==numbers.size())//如果最后找到的下标是右端点,则失效
continue;
if(numbers[low]==findnum)//判断找到的元素是否正确
return {
i+1,low+1};
}
return {
-1,-1};
}
};
使用了大佬的模板之后
https://blog.csdn.net/qq_39309050/article/details/109101552
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
for(int i=0;i<numbers.size()-1;i++)
{
int findnum=target-numbers[i];
int low=i+1;
int high=numbers.size()-1;//左闭右开
int mid=0;
//二分查找
while(low<=high)
{
mid=low+(high-low)/2;
if(findnum>numbers[mid])
{
low=mid+1;
}
else if(findnum<numbers[mid])
{
high=mid-1;
}
else if(findnum==numbers[mid])
return {
i+1,mid+1};
}
}
return {
-1,-1};
}
};
4.双指针之移除元素
总结:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow=0;
int len=nums.size();
for(int fast=0;fast<len;fast++)
{
if(nums[fast]!=val)
nums[slow++]=nums[fast];
}
return slow;
}
};
比较喜欢官方说的那个适用于删除较少元素的双指针操作
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int len=nums.size();
int fast=0;
while(fast<len)
{
if(nums[fast]==val)
{
nums[fast]=nums[len-1];
len--;
}
else
{
fast++;
}
}
return len;
}
};
5.最大连续1的个数
总结
class Solution {
public:
int findMaxConsecutiveOnes(vector<int>& nums) {
int slow = 0;
int maxslow = 0;
for(int i = 0; i < nums.size(); i++)
{
if(nums[i]!=1)
{
if(slow > maxslow)
{
maxslow = slow;
}
slow = 0;
}
else
{
slow++;
}
}
if(slow > maxslow)
{
maxslow = slow;
}
return maxslow;
}
};
6.长度最小的子数组
总结:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
if(len == 0)
return 0;
long minlen = 1410065407;
long sum = 0;
int fast = 0;
int slow = 0;
int i = 0;
for(int fast = 0; fast < len; fast++)
{
sum += nums[fast];
if(sum >= s)
{
if(fast - slow + 1 < minlen)
{
minlen = fast - slow + 1;
}
sum = 0;
fast = slow++;
}
//cout << "sum:" << sum << "minlen:" << minlen << endl;
}
if(minlen == 1410065407)
return 0;
return minlen;
}
};
官方一:暴力解法
总结:
//暴力解法
class Solution {
public:
int min(int a, int b)
{
return a > b ? b : a;
}
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
if(len == 0)
return 0;
int sum = 0;
int minlen = INT_MAX;
for(int i = 0; i < len; i++)
{
sum = 0;
for(int j = i; j < len; j++)
{
sum += nums[j];
if (sum >= s)
{
minlen = min (minlen, j - i + 1);
break;
}
}
}
return minlen == INT_MAX ? 0 : minlen;
}
};
官方二:前缀和+二分 解法
总结:
class Solution {
public:
int min(int a, int b)
{
return a > b ? b : a;
}
int twoDivid(vector<int>& sum, int target)
{
int left = 0;
int right = sum.size();
while(left < right)
{
int mid = left + (right - left) / 2;
if(sum[mid] < target)
{
left = mid + 1;
}
else
{
right = mid;
}
}
return left;
}
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
if(len == 0)
return 0;
vector<int> sum(len + 1, 0);
//sum[i]存放[0]+[1]……+[i-1]
//sum[i]表示前i个元素和
for(int i = 1; i <= len; i++)
{
sum[i] = sum[i - 1] + nums[i - 1];
cout << "sum[%d]-"<< i << " "<<sum[i] <<endl;
}
int target = 0;
int bound = 0;
int minlen = INT_MAX;
for(int i = 1; i <= len + 1; i++)
{
target = sum[i - 1] + s;
bound = twoDivid(sum, target);
//auto bound = lower_bound(sum.begin(), sum.end(), target);
if (bound != sum.size())
minlen = min(bound - i + 1,minlen);
}
return minlen == INT_MAX ? 0: minlen;
}
};
官方的可取之处
for (int i = 1; i <= n; i++) {
int target = s + sums[i - 1];
auto bound = lower_bound(sums.begin(), sums.end(), target);
if (bound != sums.end()) {
ans = min(ans, static_cast<int>((bound - sums.begin()) - (i - 1)));
}
}
class Solution {
public:
int min(int a,int b)
{
return a<b ? a:b;
}
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
if(len==0)
return 0;
int start = 0;
int end = 0;
int sum = 0;
int minlen = INT_MAX;
while(end < len)
{
sum += nums[end];
while(sum >= s)
{
minlen = min(end - start + 1, minlen);
sum -= nums[start++];
}
end++;
}
return minlen==INT_MAX ? 0 : minlen;
}
};
官方双指针做法
总结:
class Solution {
public:
int min(int a,int b)
{
return a<b ? a:b;
}
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
if(len==0)
return 0;
int start = 0;
int end = 0;
int sum = 0;
int minlen = INT_MAX;
while(end < len)
{
sum += nums[end];
while(sum >= s)
{
minlen = min(end - start + 1, minlen);
sum -= nums[start++];
}
end++;
}
return minlen==INT_MAX ? 0 : minlen;
}
};