球球速刷LC--数学类 二轮

两数相加!!!

注意进位。

    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

注意运算优先级

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值