两数相加!!!
注意进位。
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;
}
两数相加2
思路和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。而放大操作可以通过左移操作完成。
此处有个重点是防止溢出,一个是INT_MIN转成正数溢出,还有移位溢出
class Solution {
public:
int divide(int dividend, int divisor) {
//最主要就是防止溢出,一个是INT——MIN转成正数溢出,还有移位溢出
if(dividend==divisor) return 1;
if(divisor==1) return dividend;
if(divisor==-1){
if(dividend==INT_MIN) return INT_MAX; //防止溢出【-2^31---2^31-1
else return -dividend;
}
if(dividend==0) return 0;
if(divisor==INT_MIN) return 0; //防止INT_MIN 转换溢出
int di=divisor<0?-divisor:divisor;
int addOne=0;
if(dividend==INT_MIN){
dividend+=di; //加一个正数防止负max转正数溢出问题
addOne=1;
}
int de=dividend<0?-dividend:dividend;
//de/di 二者都是正数
int dee=de,dii=di;
int num=1;
while(true){
bool r=false;
while((dee>>1) >=dii){ //此处分母右移可防止将分子左移导致的溢出
dii<<=1;
num<<=1;
r=true;
}
if(dee>=dii &&r){
dee-=dii;
dii=di;
addOne+=num;
num=1;
}
if(!r){
break;
}
}
while(dee>=di){
++addOne;
dee-=di;
}
if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
return addOne;
}else{
return -addOne;
}
}
};
字符串乘法!!!
<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的记录方法,而是采样<dy,dx>数对来记录一个斜率
因此对每个<dy,dx>需要求二者的最大公约数,从而将<dy,dx>最简化,使得<dy,dx>唯一表示一个斜率
两个重点:<1>gcd算法 <2>pair<int,int>可以直接做map的key
class Solution {
//求最大公约数,使用辗转相除法
//原理:对于数对<a,b>。求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的记录方法,而是采样<dy,dx>数对来记录一个斜率
//因此对每个<dy,dx>需要求二者的最大公约数,从而将<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;
}
求两个矩形总的覆盖面积
主要是求相交面积
同时防止溢出
相交面积等于 <x轴投影相交长度>*<y轴投影相交长度>
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);
}
基本计算器
使用栈实现
基本计算器2
注意运算优先级