注意进位。
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
auto op1=l1,op2=l2;
ListNode ret;
ListNode*psum=&ret;
int jinwei=0;
while(op1 || op2 || jinwei){
int add1 = op1==NULL?0:op1->val;
int add2 = op2==NULL?0:op2->val;
int curr_add = add1+add2+jinwei;
psum->next = new ListNode(curr_add>=10?curr_add-10:curr_add);
jinwei=curr_add>=10?1:0; //更新进位
//移动操作数
if(op1)op1=op1->next;
if(op2)op2=op2->next;
psum=psum->next;
}
return ret.next;
}
思路和1相同,此处注意链表操作。链表需要翻转,此处使用栈
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<int>s1;
stack<int>s2;
stack<int>sum;
auto p = l1;
while(p){
s1.push(p->val);
p = p->next;
}
p = l2;
while(p){
s2.push(p->val);
p = p->next;
}
int extra =0;
while((!s1.empty() || (!s2.empty()))){
int a=0,b=0;
if(!s1.empty()){ a = s1.top(); s1.pop();}
if(!s2.empty()) {b = s2.top(); s2.pop();}
int c = a+b+extra;
if(c >= 10) {
c -=10;
extra=1;
}else{
extra = 0;
}
sum.push(c);
}
//注意最后的进位!!!
if(extra > 0) sum.push(extra);
ListNode* ret= NULL;
if(!sum.empty()){
ret = new ListNode(sum.top());
sum.pop();
}
p = ret;
while(!sum.empty()){
p->next = new ListNode(sum.top());
sum.pop();
p = p->next;
}
return ret;
}
};
有个傻叉做法,有奇效
string intToRoman(int num) {
vector<string> _1000={"","M","MM","MMM"};
vector<string> _100={"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"};
vector<string> _10={"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"};
vector<string> _1={"","I","II","III","IV","V","VI","VII","VIII","IX"};
return _1000[num/1000]+_100[num%1000/100]+_10[num%100/10]+_1[num%10];
}
规律是:
从右向左,当前罗马数字如果比右边一位小,则要从总和中减去,否则加入总和。这是罗马数的表达方式决定的。
int romanToInt(string s) {
//从右向左发现,当前罗马数字如果比右边一位小,则要从总和中减去,否则加入总和
if(s.empty()) return 0;
//用一个map表示罗马字符与代表的数字之间的映射
map<char,int>m={
{'I',1},
{'V',5},
{'X',10},
{'L',50},
{'C',100},
{'D',500},
{'M',1000}
};
int sum=m[s.back()];
for(int i=s.size()-2;i>=0;--i){
if(m[s[i]]<m[s[i+1]]){
sum-=m[s[i]];
}else{
sum+=m[s[i]];
}
}
return sum;
}
de÷di 就是数de里面有多少个di。 de=di+di+di+…+di
为了加快数数速度,可以尝试先把di放大为自己的2倍 4倍 8倍…n倍
看是否有de>=ndi. 如果满足,则可把ndi从de里面减去,并在最终结果里加上n。而放大操作可以通过左移操作完成。
此外为了防止溢出,用long型计算
int divide(int dividend, int divisor) {
//防止溢出,用long计算
long de=dividend;
long di=divisor;
if(de==0) return 0;
//确定正负号
bool neg=false;
if((de<0&&di>0)|| (de>0&&di<0)){
neg=true;
}
//统一转换为正数操作
if(de<0) de=-de;
if(di<0) di=-di;
//de=di+di+.....di
//返回结果为de里面有多少个di
//为了加快速度,尝试把di通过左移放大1倍 2倍 4倍...n倍 来看是否小于de.如果是,先把n*di从de里面减去,并把di的个数+n
long result=0;
while(de>=di){
long shift=0;
//尝试把di通过左移放大2^shift倍
while((di<<(shift+1))<=de){
++shift;
}
//结果里加上di放大的倍数
result+=((long)1<<shift);
//把放大的di从de里面减去
de-=(di<<shift);
}
//加上符号
if(neg) result=-result;
//如果溢出,返回最大最小数
if(result>INT_MAX) return INT_MAX;
if(result<INT_MIN) return INT_MIN;
return result;
}
<1>两数乘积之和的长度不超过l1+l2
<2>加上当前两个相乘的个位数是l1[i],l2[j].则得到的乘数在最终结果里面的位置高位在[i+j],低位在[i+j+1]。因此计算得到nums[i]*nums[j]后,将其与当前结果低位[i+j+1]数字相加,将个位留在当前位置,而进位加到[i+j]上
string multiply(string num1, string num2) {
//两数乘积的长度不超过两数之和。因此声明结果的长度为l1+l2
int l1=num1.size();
int l2=num2.size();
string res(l1+l2,'0');
//num1 的第i位与 num2的第j位乘积在最终结果中其高位与地位分别在(i+j) (i+j+1)的位置。因此与这两个位置从
//i+j+1开始相加,并注意是否有进位
for(int i=l1-1;i>=0;--i){
for(int j=l2-1;j>=0;--j){
//当前两个单数字的乘积
int mul=(num1[i]-'0')*(num2[j]-'0');
int sum=mul+res[i+j+1]-'0';//与低位相加
res[i+j+1]=sum%10+'0';//获得低位数字
res[i+j]+=sum/10;//进位
}
}
for(int i=0;i<res.size();++i){
//消除前缀0
if(res[i]!='0'){
return res.substr(i);
}
}
//最终结果都是0,返回一个0
return "0";
}
主要注意:
1.对溢出处理
2.分而治之,先求出一半幂,再二者相乘
double myPow(double x, int n) {
//如果是最小负数,将其转为正数后会溢出,因此此处化为x^n=x^(n+1)/(x)计算
if(n==INT_MIN){
return myPow(x,n+1)/(x);//对溢出的处理
}
//对小于0处理
if(n<0) return 1/myPow(x,-n);
//停止条件
if(n==0) return 1;
if(n==1) return x;
if(n%2==0){
double y=myPow(x,n>>1);
return y*y;
}else{
double y=myPow(x,(n-1)>>1);
return y*y*x;
}
return 1;
}
class Solution {
string ret;
unsigned int jiecheng(unsigned int m){
unsigned int ret=1;
for(int i=1;i<=m;++i){
ret=ret*i;
}
return ret;
}
void generate1(vector<int>&canditates,vector<int>&curr,int&k){
if(canditates.size()>1){
unsigned int curr_pos=(k-1)/jiecheng(canditates.size()-1);
curr.push_back(canditates[curr_pos]);
k=k-curr_pos*jiecheng(canditates.size()-1);
canditates.erase(canditates.begin()+curr_pos);
generate1(canditates,curr,k);
}else{
curr.push_back(canditates[0]);
ret.clear();
for(auto i:curr){
ret.push_back((char)('0'+i));
}
return ;
}
}
public:
string getPermutation(int n, int k) {
if(n<1 ||n>9 || k<1) return "";
vector<int>curr;
vector<int>canditates;
for(int i=1;i<=n;++i){
canditates.push_back(i);
}
generate1(canditates,curr,k);
return ret;
}
};
二分搜索的典型应用
class Solution {
public:
int mySqrt(int x) {
if(x==0) return 0;
if(x<=3) return 1;
int l=1,r=x/2+2;//左闭右开区间
//在[l,r)区间中找到第一个数字y,有y*y>x,则该数字左边第一个数就是最后一个y*y<=x的数字,即为所求结果
while(l<r){
int mid=l+(r-l)/2;
if((long)mid*(long)mid<=x){ //注意溢出处理
l=mid+1;
}else{
r=mid;
}
}
return l-1; //返回左边第一个位置
}
};
对于每一个点p0,记录其与其他点形成的直线的斜率
则与p0斜率相同的就是同一条直线.因此,对点p0,可以用斜率来表示其他点与p0形成的直线。
注意为了防止精度损失,不采用k=dy/dx的记录方法,而是采样
因此对每个
class Solution {
//求最大公约数,使用辗转相除法
//原理:对于数对。求ab的最大公约数
//不失一般性,设a=kb+r,因此,r=a%b
//则gcd(a,b)=gcd(kb+r,b)=gcd(b,r)。不断分解下去,直到余数为0
int gcd(int a, int b){
if(b==0) return a;
return gcd(b,a%b);
}
public:
int maxPoints(vector<vector<int>>& points) {
//对于每一个点,记录其与其他点形成的直线的斜率
//斜率相同的就是同一条直线
//注意为了防止精度损失,不采用k=dy/dx的记录方法,而是采样数对来记录一个斜率
//因此对每个需要求二者的最大公约数,从而将最简化
int res=0;
for(int i=0;i<points.size();++i){
//遍历每一个点(px,py),
int px=points[i][0];
int py=points[i][1];
//记录重复的点
int samePoints=0;
//记录不同斜率的直线上点的个数
map<pair<int,int>,int>line_cnt;
int curr_max_cnt=0;
//遍历其他点p1,看p1与p0的斜率
for(int j=i+1;j<points.size();++j){
int px1=points[j][0];
int py1=points[j][1];
//与p0完全重复
if(px==px1 && py==py1){
++samePoints;
}else{
//计算斜率,由数对(dy,dx)表示
int dx=px1-px;
int dy=py1-py;
//计算最大公约数,化简数对
int g=gcd(dx,dy);
dx=dx/g;
dy=dy/g;
//检查该斜率直线是否已经存在
auto it = line_cnt.find(pair<int,int>(dx,dy));
if(it==line_cnt.end()){
line_cnt[pair<int,int>(dx,dy)]=1;
curr_max_cnt=max(curr_max_cnt,1);
}else{
++it->second;
curr_max_cnt=max(curr_max_cnt,it->second);
}
}
}
//当前点遍历完毕,更新最多共线的点
res=max(res,curr_max_cnt+samePoints+1);
}
return res;
}
};
注意防止溢出
再求小数部分,当余数重复出现时说明进入循环小数
string fractionToDecimal(int numerator, int denominator) {
if(numerator==0) return "0";
//防止溢出,使用long int
long int n=numerator,d=denominator;
string res;
//判断正负号
if(n*d<0) res="-";
//归一为正数
n=abs(n);
d=abs(d);
//求整数部分
res+=to_string(n/d);
n=(n%d)*10;
if(n==0){//没有小数部分
return res;
}
//小数部分
res+=".";
//记录当前余数出现的位置
map<int,int>m;
while(n){
//如果当前余数出现过,则标记小数循环并返回
if(m.count(n)){
res.insert(m[n],1,'(');
res+=")";
return res;
}
//当前小数位数字
res+=to_string(n/d);
m[n]=res.size()-1;//记录当前余数索引位置
n=(n%d)*10; //更新余数
}
return res;
}
主要是求相交面积
同时防止溢出
相交面积等于
int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) {
//求X轴交集投影长度,即(A,C) (E G)的相交长度
//用long型防止溢出
long x= (long)min(C,G)-(long)max(A,E);
x=x>=0?x:0;
//y轴投影相交部分长度
long y=(long)min(D,H)-(long)max(B,F);
y=y>=0?y:0;
//覆盖面积=两矩形面积-重合面积
return (long)((C-A)*(D-B))+(long)((G-E)*(H-F))-(long)(x*y);
}
使用栈实现
注意运算优先级