leetcode笔记之数学

未完待续…

一.简单计算

7. 反转整数

反转类的经常出现,链表,数组,字符串、整数、栈等等,

while(x)
{
    int tmp = res;//1.保存前一个值用于下面的溢出判断
    res=res*10+x%10;//2.累计
    x/=10;//3.更新x
    if(res/10!=tmp)//4.溢出判断
        return 0;
}
return res;

9. 回文数判断

整数逆序,然后看是否相等就可以了

//三种特殊情况
if(x<0) return false;//1.
if(x == 0) return true;//2.
if(x % 10 == 0) return false;//3.
while(x) {//4.
    num = num * 10 + x % 10;//累计
    x /= 10;//更新
}

29. 两数相除

if (divisor == 0 || (dividend == INT_MIN && divisor == -1)) return INT_MAX;//1.溢出判断
long long m=abs((long long) dividend), n=abs((long long) divisor),res=0;//2.定义为long long类型是为了左移位
int sign=((dividend<0)^(divisor<0))?-1:1;//3.确定符号
while(m>=n)//4.只要被除数大于等于除数就继续循环
{
    long long t=n,mul=1;
    while(m>=(t<<1))//只要被除数大于等于除数就继续循环
    {
        t=t<<1;
        mul=mul<<1;//累计mul
    }
    res+=mul;//累计res
    m-=t;//减去已经被除过的部分(每次最少减去1*t,这时商的累积量为1,其余情况累积量为2^k)
}
return sign==1?res:-res;

43. 字符串相乘

1.申请一个大小为len1 + len2的数组tmp,用来保存中间值:vector<int> temp(len1 + len2, 0);
2.对两个字符串嵌套for循环,假设str1的每一个字符->数字分别 * str2的第0,1,…,n-1个字符->数字,得到的结果保存在i+j+1的位置:temp[i + j + 1] += d1 * d2
3.tmp数组中的各个位置保存的是d1 * d2直接相乘的值,有可能是大于9的,进一步处理进位:

for (int i=len1 + len2-1;i>0;--i) {//3.用数组保存中间值2
    temp[i-1] += temp[i] / 10;
    temp[i] %= 10;
}

4.找到tmp中第一个不为0的位置:while(temp[i]==0) ++i;
5.sring res,然后从i位置开始转化为字符串:for(int j=i;j<len1 + len2;++j) res+=temp[j] + '0';

66.加一

1.先把最后一位+1,digits[n-1]=sum%10;int sign=sum/10;如果sign==0,无进位则直接返回digits;
2.有进位则继续计算数组中前面的各个值,直到sign==0(则直接返回digits)

for(int i=n-2;i>=0 && sign != 0;--i){
    sum=digits[i]+sign;
    digits[i]=sum%10;
    sign=sum/10;
}

3.或者数组遍历完成,但是此时sign!=0,说明数组遍历结束此时sign==1,要在数组前面插入一个1,此时需要把数组放到新的内存位置了。

67.二进制求和

1.开一个string,大小为a,b长度中较大的
2.遍历两个string,从后往前相加,别忘了进位标志位

while(i >= 0 || j >= 0) //遍历两个string
{
     int sum = (i >= 0 ? a[i--]-'0' : 0) + (j >= 0 ? b[j--]-'0' : 0 ) + sign;
     res[max(i,j)+1] = (sum % 2) + '0';
     sign = sum/2;
}

3.如果最后标志位不为0,要在前面加个1
if (sign) res = "1" + res;

求和、积(还是求和)问题,注意进位标志位的更新,和数值的统计

1.和:

int sum = (i >= 0 ? a[i--]-'0' : 0) + (j >= 0 ? b[j--]-'0' : 0 ) + sign;//1.求当前和
res[max(i,j)+1] = (sum % 2) + '0';//2.max(i,j)+1位置处放新的值
sign = sum/2;//3.更新标志位

2.积:temp[i + j + 1] += d1 * d2;//i + j + 1位置处累积

50. Pow(x, n)

1024=(2^5)^2 = ((2^2)^2*2)^2 =((2*2) * (2*2) 2) ((2*2) * (2*2) *2)
1.采用递归的方法,double myPow(double x, int n)截止条件如下:

if( n==0 )
  return 1;
if( n==1 )
    return x;

2.递归过程中,指数没次减半int t = n/2;
3.if(n<0) { t = -t;x = 1/x; }
4.递归:result = myPow(x,t);
5.最后,如果指数为偶数,就result * result,否则result * result * x:return (n%2==0)?result*result:result*result*x;

69.x 的平方根

367. 有效的完全平方数

1.牛顿迭代法:
p=x^2;求x
f(x)=x^2-p;找到f(x)的根xn(xn>0),
(1)求f(x)的切线:坐标点:(xn,f(xn))
y=f’(xn)(x-xn)+f(xn)=0;
(2)更新:令x=xn+1整理得:
xn+1=xn-f(xn)/f’(xn);=> xn+1=xn-(xn^2-p)/2xn;
=>xn+1=xn/2 + P/2xn; =>r = (r + x/r) / 2
(3)这样跌待下去就找到了根值xn

int mySqrt(int x) {
    long r = x;//r即为xn
    while (r*r > x)
        r = (r + x/r) / 2;
    return r;//有效完全平方数则返回即可:return r*r == x;
}

2.二分法:

int mySqrt(int x) {
if(x<=1)    
    return x;
int l=1,r=x;
while(l<=r)//1.二分
{
    int mid=l+(r-l)/2;//2.找到中间位置,防止溢出
    if (mid == x/mid)/3.1/恰好找到,返回
        return mid;
    if (mid < x/mid)//3.2 l=mid+1,防止溢出
        l=mid+1;
    else if (mid > x/mid)//3.3 r=mid-1,防止溢出
        r=mid-1;
}
return r;//因为l为需要返回的值时,它是小于根号x的,会右移,而r恰好大于根号x时,会左移,正好得到要返回的值*/
}

二.性质数,公式找规律

231. 判断2的幂

保证n>0 && n&(n-1)各个位置恰好为0

 return n > 0 && !(n&(n-1));//n&(n-1)==0说明恰好为2的幂

326. 判断3的幂

1.//判断一个浮点数恰好为整数,fmod(f,1)==0,即没有小数部分
2.log10(n)/log10(3)=log3(n) <=> 指数 k=log3(n) <=> n=3^k,
3.log是e为底的对数;log10是10为底的对数;但是为啥用log就不行呢??????

//log3(n)没有小数部分,log10(n)/log10(3)返回的是浮点数
return fmod(log10(n)/log10(3) , 1) ==0;

258.给一个非负整数 num,反复添加所有的数字,直到结果只有一个数字

对num一直对9取余就可以了,但是要注意值得范围是0 ~ 9,因此 num=9时,返回9
1.(x + y) % z = (x % z + y % z) % z;
2.x % z % z = x % z;
3.对n % 9 做一些变形,注意,这里已经不是等价变形了,而是:=>
n % 9 = [(n - 1) + 1] % 9 =[ (n - 1) % 9 + 1 % 9 ] % 9 变形=> 1+(n - 1) % 9

return 1 + (num - 1) % 9;//对num % 9做变形

172.阶乘后的零

这里我们要求n!末尾有多少个0,因为我们知道0是2和5相乘得到的,而在1到n这个范围内,2的个数要远多于5的个数,所以这里只需计算从1到n这个范围内有多少个5就可以了。
除了计算n/5, 还要计算n/5/5, n/5/5/5, n/5/5/5/5, …, n/5/5/5,,,/5直到商为0,然后就和,就是最后的结果。

int trailingZeroes(int n) {
    int res=0;
    while(n)//1到n这个范围内有多少个5^k(k=0,1,...只要5^k<=n)
    {
        res+=n/5;//累积5的个数
        n/=5;//更新n值
    }
    return res;
}

202. 快乐数

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。如果可以变为 1,那么这个数就是快乐数。
在一个while(1)循环中:
1.n==1时,返回true;
2.申请一个数组vector<int> ans,ans中不包含n,则在ans中加入n
3.ans中包含n,返回false,(此时说明计算的平方和又回到n值了,说明它不是快乐数)
4.得到每一轮的新的n值

bool isHappy(int n) {
//unordered_map<int,int> ans;//也可以用hash_table
vector<int> ans;
    while(1)
    {
       if(n == 1)//n==1时,返回true
           return true;         
       if(find(ans.begin(), ans.end(), n) == ans.end())//ans中不包含n,则在ans中加入n
           ans.push_back(n);
       else//ans中包含n,返回false
           return false;

       //每一轮得到一个新的n值
       int sum = 0;
       while(n)
       {
           sum += (n % 10) * (n % 10);
           n = n /10;               
       }
       n = sum;
    }
}

223.矩形面积

在二维平面上计算出两个由直线构成的矩形叠加覆盖后的面积。
1.先求两个矩形的总面积
2.把四个横坐标和四个纵坐标分别放在数组x和y中,并且在分别x,y中做排序
3.计算中间两个坐标值的差的乘积就是重叠的面积
total = total - (x[2] - x[1]) * (y[2] - y[1]);

268. 缺失数字

相比于[0…n]数组,该数组缺少了一个数,
那么先计算出[0…n]的数组的和,再减去待计算数组的和,那么缺少的数就出来了。

int ans=0;
int n=nums.size();
int sum=(1+n)*n/2;//1.0~n的和sum
for(int i=0;i<n;++i)//2.待计算数组的和ans
    ans+=nums[i];
return sum-ans;//3.sum-ans 即为所求

263.丑数

丑数就是只包含质因子 2, 3, 5 的正整数。1 也可以被当做丑数。
1.从2开始(2,3,4,5):
2.如果恰好可以整除num则继续整除,否则下一个(2,3,4,5)
3.最后num恰好为1,返回true,否则返回false

bool isUgly(int num) {
    for (int i=2; i<6 && num; i++)//从2开始:2,3,4,5,//4可以分解成2*2,不影响,实际在这里4是不会有机会整除的,因为2都不行了
        while (num % i == 0)//如果恰好整除,则继续除
            num /= i;
    return num == 1;//最后num恰好为1,返回true:说明确实是只包含质因子 2, 3, 5;否则就是false
}

264.丑数 II

编写程序找第 n 个丑数。例如, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 就是前10个丑数。1 一般也被当做丑数。

如果我们把所有的丑数依次按升序存储到一个列表里面,那么这个列表后面的某个元素,一定是前面的某个元素乘2、乘3、或乘5得到的;
所以,假设现在我们已知这样一个列表的一部分,想要往列表里面继续添加新元素的话,因为这个列表已经是排好序的,所以我们可以设定三个指针index2, index3, index5,他们所指向的元素,乘2,乘3,乘5分别为此时的M2, M3, M5;
每次遍历结束之后,只需要从这三个指针的位置(含)开始向后扫描即可。DP

int nthUglyNumber(int n) {
    if(n <= 0) 
        return false; 
    if(n == 1) 
        return true; 
    int t2 = 0, t3 = 0, t5 = 0; //pointers for 2, 3, 5
    vector<int> v(n,0);
    v[0] = 1;
    for(int i  = 1; i < n ; i ++)
    {
        v[i] = min(v[t2]*2,min(v[t3]*3,v[t5]*5));//找到2,3,5上一位置处 *2,3,5 后最小的数
        if(v[i] == v[t2]*2) t2++; 
        if(v[i] == v[t3]*3) t3++;
        if(v[i] == v[t5]*5) t5++;
    }
    return v[n-1];
}

猜你喜欢

转载自blog.csdn.net/qiangzhenyi1207/article/details/80220851
今日推荐