写给自己的:
算法是一个很关键的技能,关于思路和编译能力的提升都有很大的帮助,一个题不要只是想怎么解决出来,而是想怎么最快的解决,虽然这样很慢,但是现在的目标不是什么速成,慢下来可能也是件好事吧。去真正的搞懂每一题。
这题使用双向法,使用k,i,j的索引来标记三个数,使用while循环,判断哪三个数符合要求。
代码:
class Solution {
public:
vector> threeSum(vector& nums) {
vector> res;
sort(nums.begin(),nums.end());
if(nums.empty() || nums.back()<0 ||nums.front()>0) {
return {};
}
for(int k=0;k0) {break;}
if(k>0&&nums[k]==nums[k-1]) {
continue;
}
int i=k+1,j=nums.size()-1,target=0-nums[k];
while(inums[i]+nums[j]) {
i++;
} else {
j--;
}
}
}
return res;
}
};
跟三数之和的寻找的思路一样,也是头尾查询法,同时寻找出target和tep的最小差距值,最后输出tep
minDel:最小差距值
class Solution {
public:
int threeSumClosest(vector& nums, int target) {
int res,minDel=INT_MAX;
sort(nums.begin(),nums.end());
if(nums.size()==0||nums.size()<3) {
return {};
}
for(int i=0;iabs(target-tmp)) {
minDel=abs(target-tmp);
res=tmp;
}
if(tmp>target) {
k--;
} else if(tmp
如题目一样,就是在两组数组合并成一个数组,找出他们之间的中位数。(这里科普一下中位数的意义:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。)
target:中位数的位置
index1,index2:标记两个需要求得中位数的相关数(如果有必要的话)
m,n:表示nums1和nums2的大小(nums1必须是比较大的那个数组。)
i,j:表示分割两个数组后,在右边数组中原两个数组的第一个数的索引
iMin,iMax:表示nums1(较大数组)的有效范围,需要通过这个范围来确定i的值。(当分割的数组不符合“左边的任意一个数均小于右边的任意一个数”时,需要调试有效范围从而达到对i的增大减小)
temp:当总数为偶数时,表示为另一个数,从而求得中位数
double findMedianSortedArrays(vector& nums1, vector& nums2) {
double res=0.0;
int sz=nums1.size()+nums2.size();
int target=sz/2;
if(nums1.size()==0) {
if(sz%2==0) {
res=(nums2[target-1]+nums2[target])/2.0;
return res;
} else {
res=nums2[target];
return res;
}
} else if(nums2.size()==0) {
if(sz%2==0) {
res=(nums1[target-1]+nums1[target])/2.0;
return res;
} else {
res=nums1[target];
return res;
}
}
if(sz%2==0) {
int index1=0,index2=0,i,j;
target++;
for(i=0,j=0;inums2[j]) {
target--;
if(target==1) {
index1=nums2[j];
} else if(target==0) {
index2=nums2[j];
break;
}
j++;
} else {
target--;
if(target==1) {
index1=nums1[i];
} else if(target==0) {
index2=nums1[i];
break;
}
i++;
}
}
while(index1==0||index2==0) {
if(i==nums1.size()) {
target--;
if(target==1) {
index1=nums2[j];
} else if(target==0) {
index2=nums2[j];
break;
}
j++;
} else {
target--;
if(target==1) {
index1=nums1[i];
} else if(target==0) {
index2=nums1[i];
break;
}
i++;
}
}
res=(index1+index2)/2.0;
} else {
int i,j;
target++;
for(i=0,j=0;inums2[j]) {
target--;
if(target==0) {
res=nums2[j];
break;
}
j++;
} else {
target--;
if(target==0) {
res=nums1[i];
break;
}
i++;
}
}
while(res==0.0) {
if(i==nums1.size()) {
target--;
if(target==0) {
res=nums2[j];
break;
}
j++;
} else {
target--;
if(target==0) {
res=nums1[i];
break;
}
i++;
}
}
}
return res;
}
double findMedianSortedArrays(vector& nums1, vector& nums2) {
double res=0.0;
int m=nums1.size();
int n=nums2.size();
if(m>n) {
nums1.swap(nums2); //漏点1.
swap(m,n);
}
int iMin=0,iMax=m;
int mid=(m+n+1)/2; //***求中值时记得要总数加一!!
while(iMin<=iMax) {
int i=(iMin+iMax)/2,j=mid-i; //***问题3.
if(i>iMin&&nums1[i-1]>nums2[j]) {
iMax=i-1; //问题1.
//i--;
} else if(inums1[i]) {
iMin=i+1; //问题2.
//i++;
} else {
if(i==0) {
res=nums2[j-1];
} else if(j==0) {
res=nums1[i-1];
} else {
res=max(nums1[i-1],nums2[j-1]);
}
if((m+n)%2!=0) {
return res;
} else {
double temp;
if(i==m) {
temp=nums2[j];
} else if(j==n) {
temp=nums1[i];
} else {
temp=min(nums1[i],nums2[j]);
}
return (res+temp)/2.0;
}
}
}
return res;
}
max:最长回文子串的长度
index:最长子串的头字符的索引
temp,tmp:反转后的子串,保存原子串
start,end:最长回文子串的头尾字符索引
len1,len2,len:循环中以自己为中心的最长回文子串,以自己后一个字符为中心的最长回文子串,两个中较大的最长回文子串
L,R:动态函数中以中心向两边展开的左右索引
class Solution {
public:
string longestPalindrome(string s) {
int sz=s.size();
if(sz==0||sz==1) {
return s;
}
int index,max=0;
for(int i=0;i
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()==0) {
return "";
}
int m=s.size();
int start=0,end=0;
for(int i=0;iend-start) {
start=i-(len-1)/2;
end=i+len/2;
}
}
return s.substr(start,end-start+1);
}
int expandAoundCenter(string s,int left,int right) {
int L=left,R=right;
while(L>=0 && R
这个代码是没法通过的,因为时间复杂度为O(n^3),超出时间限制;
这道题好像是只有蛮力法来解决,但是问题是存在着很多问题没有提及的情况,需要我们一步步来调试,这题最难的就是一步通过的问题。
res(long):存放最终结果,必须要定义为long才能检验int溢出问题的数据类型
index:记录开头第一个不是空格的索引
flag:记录是否为负数
class Solution {
public:
int myAtoi(string str) {
long res=0;//使用long避免溢出
int index=0;//记录开头第一个不是空格的索引
bool flag=false;//记录是否为负数
if(str.size()==0) {
return res;
}
for (int i = 0; i < str.size(); i++) { //排除空格
if (str[i] == ' ') {
index++;
}
else {
break;
}
}
if ((str[index]-'0'<0 || str[index]-'0'>9) && str[index] != '-' &&str[index]!='+') { //如果开头为字母,直接返回0
return 0;
}
if (str[index] == '-') { //判断是否为负数
flag = true;
index++;
}else if (str[index] == '+') { //判断是否存在"+"号
index++;
}
for (int i = index; i= 0 && tmp <= 9) { //如果是数字的转化
res = res * 10 + tmp;
}
if ((tmp<0 || tmp>9 ||str[i] == ' ') && i != index) {
break;
}
if (res>=INT_MAX) { //溢出问题的解决
if (flag&&res==INT_MAX) {
return INT_MIN+1;
}
if(flag&&res>INT_MAX) {
return INT_MIN;
}
if(!flag) {
return INT_MAX;
}
}
}
if(flag) { //判断是否为负数
return -res;
} else {
return res;
}
}
};
这题我想到的方法就很蛮力法,双循环找到相同的部分,添加给string中返回即可,在网上看了的思路原理都是使用LCP方法,但是因为我一开始就没有看懂这个思路,有待更新。
res:就是返回结果
class Solution {
public:
string longestCommonPrefix(vector& strs) {
string res="";
if(strs.size()==0) {
return res;
}
for(int i=0;i
就是注意下为空的情况即可;
使用栈进行入栈出栈操作即可
num1,num2,num3:分别代表剩余{,(,[的个数
tmp:栈顶的字符
class Solution {
public:
bool isValid(string s) {
stack ss;
if(s.size()==0) {
return true;
}
if(s.size()==1) {
return false;
}
int num1=0,num2=0,num3=0;
for(int i=0;i
class Solution {
public:
int removeDuplicates(vector& nums) {
for(int i=1;i
class Solution {
public:
int removeDuplicates(vector& nums) {
if(nums.empty()) {
return 0;
}
int i=0;
for(int j=1;j
在数组中,找到两个数作为一个长方形,长为它们索引的差值,高为两个数中较小的那个数,求得所以数组中的最大长方形
使用双重循环把全部的长方形都求出,最后得出最大的那个
使用双索引值,总是较小值向较大值移动即可求出
i,j:头尾索引值
class Solution {
public:
int maxArea(vector& height) {
int res,maxtmp=0;
for(int i=0;i
class Solution {
public:
int maxArea(vector& height) {
int maxtmp=0;
int i=0,j=height.size()-1;
while(i
很明显,这个方法的效率很低,所以当数组的数量较多时,就会超出时间限制无法AC
这个其实就是蛮力法的一个优化,去除多余的求解,重点在于头尾索引的移动方式:较小值向较大值移动,能想到这点就可以AC了
m,n:num1和num2的位数
bits:用来存储结果位数的各个数
p:每个计算中结果存储在哪个位置的索引值
sz:除去0之后的最高位索引值
class Solution {
public:
string multiply(string num1, string num2) {
int m=num1.size(),n=num2.size();
vector bits(m+n,0);
//下面进行的乘法原理计算,很重要!!!
for(int i=m-1;i>=0;i--) {
int p=m-1-i;
for(int j=n-1;j>=0;j--) {
int res=(num1[i]-'0')*(num2[j]-'0')+bits[p];
if(res>=10) {
bits[p+1]+=res/10;//记得这里是加等于!!
}
bits[p]=res%10;
p++;
}
}
string result;
int sz=m+n-1;
while(sz>=0&&bits[sz]==0) {
sz--;
}
for(int i=0;i<=sz;i++) {
result=char(bits[i]+'0')+result;
}
return result==""?"0":result;//分析结果为0的情况
}
};
以空格为分割条件,用substr来取子串,把子串全部反转后,用新的string存储起来,最后返回即可
index:每个子串的第一个字符串的索引
temp:子串的存储
class Solution {
public:
string reverseWords(string s) {
int index = 0;
string res = "";
for (int i = 0; i < s.size(); i++)
{
if (s[i] == ' ') {
string temp = s.substr(index, i - index);
index = i+1;
reverse(temp.begin(), temp.end());
res = res + temp + " ";
}
if (i == s.size() - 1) {
string temp = s.substr(index, s.size() - index);
reverse(temp.begin(), temp.end());
res += temp;
}
}
return res;
}
};
ins:记录顺序乘积数组
ret:先是记录逆序乘积的数组,然后是结果数组
num1,num2:通过计算得出ins,ret每个索引值的乘积数
class Solution {
public:
vector productExceptSelf(vector& nums) {
int len=nums.size();
if(len==0||len==1) {
return nums;
}
vector ins(len,nums[0]); //先是存储全部的第一个数
vector ret(len,nums[len-1]);//先是存储全部的最后一个数
int num1=nums[0],num2=nums[len-1];
for(int i=1;i
在二维数组中,从外到内,一层一层的遍历到最后一个数
在一维数组中,需要像”蛇“一样的遍历数组
这两题都没有什么巧妙的方法,就是通过归纳找到每层的索引值然后赋值,这两题的难点都在于怎么找到这个规律
m,n:二维数组中的行数和列数
count:一共搜索的共次数
i:当前的层数
c,i:当前的数和层数
class Solution {
public:
vector spiralOrder(vector>& matrix) {
vector res;
if(matrix.empty()||matrix[0].empty()) {
return res;
}
int m=matrix.size(),n=matrix[0].size();
int count=(min(m,n)+1)/2; //一共搜索的层数
int i=0;
//每层外部搜索,要搞清楚其中的逻辑
while(i=i&&(m-1-i!=i);j--) { //记得n-1和i+1要加上括号!!!!!!!!!为什么呢???
res.push_back(matrix[m-1-i][j]);
}
for(int j=(m-1)-(i+1);j>=i+1&&(n-1-i)!=i;j--) {
res.push_back(matrix[j][i]);
}
i++;
}
return res;
}
};
class Solution {
public:
vector> generateMatrix(int n) {
vector> ivec;
vector vec(n,0);
for(int i=0;i=i;j--) {
ivec[n-i-1][j]=c++;
}
for(int j=n-i-2;j>i;j--) {
ivec[j][i]=c++;
}
i++;
}
return ivec;
}
};
当然是找到规律啦,这个就不解释了,本来就是纯逻辑的事
这个没有什么注意的细节,要注意的还是规律的归纳罢了
使用map函数,分别类型为string,vector
svec:返回结果类型
sv:map函数存储相同的值
temp:每个单词按顺序排序后存储的值
class Solution {
public:
vector> groupAnagrams(vector& strs) {
vector> svec;
if(strs.empty()) {
return svec;
}
sort(strs.begin(),strs.end());
map> sv;
for(int i=0;i>::iterator iter=sv.begin();iter!=sv.end();iter++) {
svec.push_back(iter->second);
}
return svec;
}
};
sc:set函数,存储目前存在的字符
pos:set函数的迭代数,用来寻找重复数
i,j:左右边界
mci:map函数,存储字符和字符最后所在的位置
i,j:分别代表左右边界
m:存储ascii表中的字符在数组中的位置
left:左边界
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n=s.size(),res=0;
set sc;
set::iterator pos;
int i=0,j=0;
while(i
时间为104ms
class Solution {
public:
int lengthOfLongestSubstring(string s) {
map mci;
map::iterator pos;
int res=0;
for(int i=0,j=0;j
时间为32ms
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int m[256]={0};
int left=0,maxlen=0;
for(int i=0;i
时间为4ms
题目的要求就不能使用蛮力法来解决问题,只能使用一个循环,使用一个while循环,先找到最小值,再找中间值,最后如果存在最大值即可返回true,最小中间值会随着循环的进行可能会更新数据
Min:最小值
medium:中间值
class Solution {
public:
bool increasingTriplet(vector& nums) {
int n=nums.size();
if(n<3) {
return false;
}
int Min=INT_MAX,medium=INT_MAX;
int i=0;
while(iMin&&nums[i]medium) {
return true;
}
i++;
}
return false;
}
};
题目只存在0,1,把1变成0后的原理就是把1和0的位置进行交换,所以找到位置相同和位置相反的数量最大的即是答案
mss:存储与原数组配对的相同和相反的string数组
msi:存储数组和反数组的数量
class Solution {
public:
int maxEqualRowsAfterFlips(vector>& matrix) {
map mss;
map msi;
for(int i=0;i ivec,int flag) {
string res="";
for(auto v:ivec) {
res+='0'+(flag?!v:v);
}
return res;
}
};
temp:两个字符串中相等的部分
temp2:以temp的公约数为基数,创建的首个子串
temp3:以temp的公约数为基数,每公约数个子串
tmp,tmp2:temp和temp2的长度
flag:判断该子串是否为公约串
n3:str1,str2长度的最大公约数
isMatch函数:是否为公约串
class Solution {
public:
string gcdOfStrings(string str1, string str2) {
string temp,res;
int n=str1.size(),m=str2.size();
if(n=tmp) {
bool flag=true;
int tmp2=temp2.size();
for(int i=0;i
class Solution {
public:
string gcdOfStrings(string str1, string str2) {
int n1=str1.size(),n2=str2.size();
int n3=gcd(n1,n2);
string res=str1.substr(0,n3);
if(isMatch(res,str1)&&isMatch(res,str2)) {
return res;
}
return "";
}
bool isMatch(string str1,string str2) {
for(int i=0;i
找出子串中重复部分是最难的,且每个公约数子串都得找,十分耗时,思路很直接,实现复杂
直接找出最大公约数,以最大公约数找到子串,直接判断是否为公约串,判断的方法十分巧妙
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *prev=NULL,*curr=head;
while(curr!=NULL) {
ListNode *temp=curr->next;
curr->next=prev;
prev=curr;
curr=temp;
}
return prev;
}
};
没什么巧妙的方法,分情况讨论,主要注意的是每种情况的细节和边界情况
index:标志了一共几个循环数
class Solution {
public:
string fractionToDecimal(int numerator, int denominator) {
string res="";
if(denominator==0||numerator==0) {
return "0";
}
long num=numerator,den=denominator;
if(denominator<0^numerator<0) { //只有当其中一个符合才能执行代码
res+="-";
}
num=abs(num);
den=abs(den);
long temp1=num/den;
res+=to_string(temp1);
if(num%den==0) {
return res;
}
res+=".";
unordered_map mli;
long sub=num%den;
int index=0;
while(sub&&mli.find(sub)==mli.end()) {
mli[sub]=index++;
sub*=10;
res+=to_string(sub/den);
sub%=den;
}
if(mli.find(sub)!=mli.end()) {
res+="()";
int cur=res.size()-2;
while(index-->mli[sub]) {
swap(res[cur],res[cur-1]);
cur--;
}
}
return res;
}
};
就是把pow方法的原理搞清楚,使用递归思路解决
class Solution {
public:
double myPow(double x, int n) {
bool flag=true;
if(n<0) {
return 1/Half(x,n);
}
return Half(x,n);
}
double Half(double x,int n) {
if(n==0) {
return 1.0;
}
double res=Half(x,n/2);
if(n%2==0) {
return res*res;
} else {
return res*res*x;
}
}
};
两数相除但是又用不到除法,就只能使用列竖式算除法的原理,计算机的乘法和除法是通过左(乘)右(除)移被除数来完成的,记录被除数移动的位数,当除数大于被除数时,就加上二进制(第count+1位上的1)转换为十进制,注意边界的情况
class Solution {
public:
int divide(int dividend, int divisor) {
bool flag=false;
if((dividend<0) ^ (divisor<0)) {
flag=true;
}
if(dividend == INT_MIN && divisor == -1)
return INT_MAX;
if(dividend == INT_MIN && divisor == 1)
return INT_MIN;
long long div=dividend,divs=divisor,res=0,count=0;
div=abs(div);
divs=abs(divs);
while(div>=divs) {
count++;
divs<<=1;
}
while(count) {
count--;
divs>>=1;
if(div>=divs) {
res+=1<INT_MAX) {
return INT_MAX;
}
return res;
}
};
这题使用的递归法,递归的规律是backtrack(cur+letter,next.substr(1));,cur是当前一个string,例如"a","ab",letter是当前要添加的char类型,最后next.substr(1)取的就是除了当前char以后的string。这个思路有点难想到,但要是想通了就很简单了
letters:指的是当前的sting数字对应的字母群
letter:当前每个字母
next:除了当前string的string
class Solution {
public:
map mss;
void addMss() {
mss["2"]="abc";
mss["3"]="def";
mss["4"]="ghi";
mss["5"]="jkl";
mss["6"]="mno";
mss["7"]="pqrs";
mss["8"]="tuv";
mss["9"]="wxyz";
}
vector svec;
void backtrack(string cur,string next) {
addMss();
if(next.size()==0) {
svec.push_back(cur);
}
string letters=mss[next.substr(0,1)];
for(int i=0;i letterCombinations(string digits) {
if(digits.size()!=0) {
backtrack("",digits);
}
return svec;
}
};
因为是从有序数组中进行旋转得来的数组,所以我们把数组分成两部分,一部分是升序递增,另一部分是降序递减,我们找到target应该所在的部分来进行搜索,返回索引值
class Solution {
public:
int search(vector& nums, int target) {
if(nums.empty()) {
return -1;
}
if(nums.size()==1) {
if(target==nums[0]) {
return 0;
} else {
return -1;
}
}
int left=0,right=nums.size()-1;
while(left<=right) {
if(target==nums[left]) {
return left;
} else if(target==nums[right]) {
return right;
} else if(target>nums[left]) {
while(target>nums[left]) {
if(left+1=0) {
if(nums[right]>=nums[right-1]) {
right--;
} else {
return -1;
}
} else {
return -1;
}
}
if(target==nums[right]) {
return right;
} else {
return -1;
}
} else {
left++;
right--;
}
}
return -1;
}
};
根据递归的思想,我们先要找到以根节点为开始的最大权值,然后比较以根节点的左右节点为开始的最大权值,以此来完成递归操作
leftMax:当前节点的左节点开始的最大权值
rightMax:当前节点的右节点开始的最大权值
tempMax:当前节点全部走完路径的最大权值
class Solution {
public:
int res=INT_MIN;
int maxPathSum(TreeNode* root) {
maxValue(root);
return res;
}
int maxValue(TreeNode *root) {
if(root==NULL) {
return 0;
}
int leftMax=max(maxValue(root->left),0);
int rightMax=max(maxValue(root->right),0);
int tempMax=root->val+leftMax+rightMax;
res=max(res,tempMax);
return root->val+max(leftMax,rightMax);
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
int rVal=root->val,pVal=p->val,qVal=q->val;
if(pVal>rVal && qVal>rVal) {
return lowestCommonAncestor(root->right,p,q);
} else if(pValleft,p,q);
} else {
return root;
}
}
};
ans:用来存储返回的结果
cur:当前的stiring,完成后添加到ans中返回最终结果
left,right:(和)的数量
max:一共的括号数
以一个默认的括号为基础
left:默认括号中的括号数量
right:默认括号外的括号数量
class Solution {
public:
void backtrack(vector &ans,string cur,int left,int right,int max) {
if(cur.length()==max*2) {
ans.push_back(cur);
return;
}
if(left generateParenthesis(int n) {
vector ans;
backtrack(ans,"",0,0,n);
return ans;
}
};
class Solution {
public:
vector generateParenthesis(int n) {
vector res;
if(n==0) {
res.push_back("");
} else {
for(int i=0;i
递归规律:for循环原数组,然后从first开始交换到最后一个,递归以first+1的函数规律
first:以first不变交换全部的数组,然后使用递归交换first+1,以此类推来完成数组的全排列
class Solution {
public:
vector> permute(vector& nums) {
vector> res;
vector temp;
for(int num:nums) {
temp.push_back(num);
}
int n=temp.size();
backtrack(0,res,temp,n);
return res;
}
void backtrack(int first,vector>& ivec,vector& temp,int n) {
if(first==n) {
ivec.push_back(temp);
}
for(int i=first;i
就像和全排列类似的思路,基线条件为tmp的数量是否小于nums,是则添加到结果容器res中,然后使用for循环全数组数,变化参数为i+1,在完成后要把tmp的最后一个数删除掉
first:以first为中心来完成全部的子集
class Solution {
public:
vector> subsets(vector& nums) {
vector> res;
vector tmp;
backtrack(res,tmp,nums,0);
return res;
}
void backtrack(vector> &ivec,vector vec,vector &nums,int first) {
if(vec.size()<=nums.size()) {
ivec.push_back(vec);
}
for(int i=first;i
格雷编码代表連續兩個數之間二進制只存在一個數字的差別,我們單從定義中很難找到它們之間的規律,其實就是使用鏡面思想,在前面已經的步驟中,鏡面複製全部數據,然後再在它們的前面加上0和1即可
i,j,k:分別表示要進行多少步,進行鏡面複製的索引值,進行加0和1步驟的索引值
cnt:以這個值為界限,來判斷前面加0還是1
class Solution {
public:
vector grayCode(int n) {
if(n==0) {
return {0};
}
vector res={"0","1"}; //這個也是n=1時的答案
for(int i=1;i=0;j--) {//進行鏡面複製操作
res.push_back(res[j]);
}
int cnt=pow(2,i); //界限
for(int k=0;k ivec;
for(int i=0;i=0;i--) {
res+=(str[i]-'0')*pow(2,str.size()-1-i);
}
return res;
}
};