加(大数运算) 减(大数运算) 乘(竖式 龟速乘) 除(二分) 幂(快速幂)...待补充

加 减 乘 除 幂

    • 复用加法
    • 竖式乘法(优化)
    • 龟速乘
  • 除法(优化)
  • 幂操作(快速幂)

题目链接
题目描述
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。


思路

  1. 创建一个进位位,从两个字符串尾部开始遍历
  2. 只要有一个字符串不小于0或进位位不等于0,进行-‘0’记录此个位数字,然后三数相加(进位,num1,num2)
  3. 大于10就把进位位置为sum/10,再往复循环

代码如下:

class Solution {
public:
   string addStrings(string num1, string num2) {
       string s1;
       int add=0,size1=num1.length()-1,size2=num2.length()-1;
       while(size1>=0||size2>=0||add!=0)
       {
           int x=size1>=0?num1[size1]-'0':0;
           int y=size2>=0?num2[size2]-'0':0;
           int sum=x+y+add;
           s1.push_back('0'+sum%10);
           add=sum/10;
           size1--;
           size2--;
       }
       reverse(s1.begin(),s1.end());
       return s1;
   }
};

注意:在c++中insert非常不推荐使用,在这里反转字符串的时候用insert会把时间复杂度提到O(n^2)


思路

  1. 两数会有大小,为保证永远是大数-小数,可以写一个比较函数
  2. 设置一个结果正负标志位,先判断两输入字符大小,小数减大数标志位设为负,同时变换为大减小(swap(num1,num2))
  3. 按位减 (参考加法) 如果减后的数小于0,需要借位,所以需要一个借位标志位,同时小于0的数加10,最后append到字符串后面
  4. 总体逆置一下

题目链接
题目描述
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

复用加法

思路 时间复杂度:O(mn+n^2) m,n两字符串的长度,空间复杂度:O(m+n))

  1. 做加法,以num2为乘数便利每一位,以乘法处理方式(注意加0问题)
  2. 每一次乘出的值复用加法逻辑相加

代码如下:

class Solution {
public:
   string addStrings(string num1, string num2) {
       string s1;
       int add=0,size1=num1.length()-1,size2=num2.length()-1;
       while(size1>=0||size2>=0||add!=0)
       {
           int x=size1>=0?num1[size1]-'0':0;
           int y=size2>=0?num2[size2]-'0':0;
           int sum=x+y+add;
           s1.push_back('0'+sum%10);
           add=sum/10;
           size1--;
           size2--;
       }
       reverse(s1.begin(),s1.end());
       return s1;
   }
   string multiply(string num1, string num2) {
       if(num1=="0"||num2=="0")
       {
           return "0";
       }
      else
       {
         int carry=0,i1=num1.size()-1,i2=num2.size()-1;
           string sum="";
           for(int i=num2.size()-1;i>=0;--i)
           {
               string ret;
               i1 = num1.size() - 1;
               while(i1>=0||carry!=0)
               {
                   int x=i1>=0?num1[i1]-'0':0;
                   int y=(num2[i]-'0');
                   int multi=x*y+carry;
                   ret.push_back((multi%10)+'0');
                   carry=multi/10;
                   i1--;
               }
               reverse(ret.begin(),ret.end());//注意这里不是.end()-1
               int count=num2.size()-1-i;//计数添几个0
               while(count--)
               {
                   ret.push_back('0');
               }
               sum=addStrings(ret,sum);
           }
           return sum;
       }
               
   }
};

竖式乘法(优化)

思路

  1. 做乘法,由于两数相乘必为位数m+n或m+n-1,创建一个m+n大小的数组来存储,提高效率
  2. 通过两数相乘时,乘数某位与被乘数某位相乘,与产生结果的位置的规律来完成

加(大数运算) 减(大数运算) 乘(竖式 龟速乘) 除(二分) 幂(快速幂)...待补充_第1张图片
代码如下

class Solution {
public:
   string multiply(string num1, string num2) {
       if(num1=="0"||num2=="0")
           return "0";
       int max_size=num1.length()+num2.length();//两种情况 m+n,m+n-1
       int i=max_size-1,j=max_size-2;
       vector<int> arr(max_size,0);
       for(int i2=num2.size()-1;i2>=0;i2--)
       {
           i=j+1;
           for(int i1=num1.size()-1;i1>=0;i1--)
           {
               int tmp=(num1[i1]-'0')*(num2[i2]-'0');
               arr[i]+=tmp;
               if(arr[i]>=10)
               {
                   arr[i-1]+=arr[i]/10;
                   arr[i]=arr[i]%10;
               }
               i--;
           }
           j--;
       }
       string s="";
       for(int i=0;i<max_size;i++)
       {
           if(0==i&&arr[i]==0)
               continue;
           else
               s+=(arr[i]+'0');
       }
       return s;
   }
};

龟速乘

使用场景 爆long long的时候比如两个long long相乘再取模a×b超过long long能表示的范围(a×b%c)
思路如下 类似快速幂(二进制位)
代码如下

long long cheng(long long a, long long b,int c)
{
	long long ret = 0;
	while (b)
	{
		if (b & 1) 
			c = (ret + a) % c;
		a = (a + a) % c;
		b >>= 1;
	}
	return ret;
}

除法(优化)


幂操作(快速幂)

题目链接
题目描述
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn),要求时间复杂度小于O(n)

思路一从二进制的角度---迭代(时间复杂度O(logn) 空间复杂度O(1))
( 例子 求pow(x,77) )

  1. 要求时间复杂度小于O(n),必然不可以连乘x n次
  2. 77的二进制为(1001101)2,77=20*1 + 21*0 +22*1 + 23*1+…+ 26*1
  3. 将上面表达式带入x77:x77=x * x4 * x8 * x64 (1,4,8,64就是77二进制为1对应十进制)
  4. 当指数n为负数时,我们可以计算 x{-n} 再取倒数得到结果

代码如下
注意:

  1. int负数转 正数需要精度更高的来表示
  2. x*=x;表示x->x^2->x^4->x^8
  3. 注意x*=x;语句可能爆long long我们可以复用上面龟速乘的函数
class Solution {
public:
   double myPow(double x, int n) {
       if(x==0)
           return 0;
       else if(n==0)
           return 1;
       else{
           long n1=n;//int负数转 正数需要精度更高的来表示
           double ret=1.0;
           if(n1<0)//负数处理
           {
               n1*=(-1);
               x=1/x;
           }
           while(n1!=0)//不断右移,直到二进制中无1
           {
               if((n1)&1==1)
                   ret*=x;
               x*=x;//x->x^2->x^4->x^8
               n1>>=1;//不断右移
           }
           return ret;
       }
   }
};

思路二从分治的角度---递归(时间复杂度O(logn) 空间复杂度O(logn))

  1. 我们可以这样(除2向下取整):77/2=38,38/2=19,19/2=9,9/2=4,4/2=2,2/2=1
    指数每次乘以2
    x1 ——> x2 ——> x4+1—> x9+1—> x19 ——> x38+1—> x77
  2. 分为两种情况:奇数和偶数,奇数/2就多出一个x,偶数就不用
  3. 用递归,每次指数会减少一半

代码如下(leetcode 官方解法一)

class Solution {
public:
   double quickMul(double x, long long N) {
       if (N == 0) {
           return 1.0;
       }
       double y = quickMul(x, N / 2);
       return N % 2 == 0 ? y * y : y * y * x;
   }

   double myPow(double x, int n) {
       long long N = n;
       return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
   }
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/powx-n/solution/powx-n-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(数据结构(题),leetcode,算法,职场和发展)