A + B 问题 、尾部的0

题目一:给出两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符。

代码:

int aplusb(int a, int b) {
    // write your code here
    if(b == 0)
    {
        return a;
    }
    else
    {
        return aplusb(a^b,(a&b)>>1);
    }
}

看到题目第一反应就是一脸茫然,计算加法不能用加号?emmmm首选度娘,然后我见到了位运算。

嗯,从来没学过的位运算。

  • 与运算&: 对应位均为1时为1,其它为0。
  • 或运算|: 对应位均为0时为0,其它为1。
  • 异或运算^: 对应位不相同时为1,相同时为0.
  • 按位取反~: 每一位取反
  • 右移>>: 将二进制进行右移,低位丢掉,高位补零。
  • 左移<<: 将二进制进行左移,低位补零,高位丢掉

这道题目主要用的是与运算&、异或运算^,左移一位<<1

以简单为例:0011+1001
无进位的运算是1010,通过^运算实现
进位是0001,通过&运算实现
加法是本位相加再加上低位的进位,所以运算是1010+0010
0010由0001左移一位实现
当进位为0时,运算结束

我理解的思路大致如此。

emmmm中途也有点小失误的说,因为我不小心把左移打成右移,使得通过率只有44%,还以为做100+(-100)时还要把(-100)求补码(实际上就是100)再运算,当成减法了,很尴尬。

题目二:设计一个算法,计算出n阶乘中尾部零的个数

代码:long long trailingZeros(long long n) {
        // write your code here, try to do it without arithmetic operators.
        long long num = 0;
        long long temp = n;
        while(temp / 5!=0)
        {
            num = num + temp/5;
            temp = temp/5;
        }
        return num;
    }

讲真,这个题目我做的内牛满面。首先我考虑的是计算阶乘的结果,然后用这个结果不断除以10,计算0的个数,然后很荣幸的卡在了165这个数字上,结果太大了,找度娘,学了高精度计算阶乘,然后又卡在了1001171717上,后来我想每除一次10就把数据分别向前移一位,但是并没有什么用,光是末尾0的个数就有250292920个,现在很明显思路是完全错了

错代码如下:

long long trailingZeros(long long n) {
        // write your code here, try to do it without arithmetic operators.
        int f[2000];
        long long sum = 0,sum1;
        memset(f,0,2000);//为较大的数组结构体清零的函数
        f[0] = 1;//阶乘从一开始
       int i,j,k,c,q;
       for(i = 2;i<=n;i++)//这里的i是被乘数
       {
            k = 0;//这里的k是个位数之外的整数部分
            sum1 = 0;
            for(j = 0;j<2000;j++)
            {
                //用乘数的每一位去乘被乘数并且加上上一位乘被乘数的个位数之外的整数部分(k)
                c = f[j]*i+k;
                f[j] = c%10;//可以确定的结果中的数字
                k = c/10;
            }
            for(q = 0;f[q] == 0;q++)//记录每一次乘法以后末尾0的个数
            {
             sum++;
             sum1++;
   }
   for(q = sum1;q<2000;q++)
   {
    f[q-sum1] = f[q];
   }
       }//得到的f数组就是阶乘的结果(由低位向高位排列)
       return sum;
    } 

后来看到有大佬们用分解因子分解数据,没完全看懂。我的思路是计算每个数据的2的因子的个数,然后把2按个数写入数组,再计算5的个数,按个数把5写入数组,当前两个数据相乘为10,前两个元素从数组中删除,计数器加一,嗯,同样的问题,1001171717数据太大,数组越界。

错代码如下:

long long trailingZeros(long long n) {
        // write your code here, try to do it without arithmetic operators.
        int top = -1;//用栈
        int stack[90000000];
        long long num = 0,temp;
        long long i;
        for(i = 1;i<=n;i++)
        {
           
            temp = i;
            while(1)
            {
               if(temp%2 == 0)//i被2整除
                {
                    top++;
                    stack[top] = 2;
                    temp = temp/2;
                }
                else if(temp%5 == 0)//i被5整除
                {
                    top++;
                    stack[top] = 5;
                    temp = temp/5;
                }
                else
                {
                    break;
                }
                if(stack[top]*stack[top-1]==10)//栈顶以及其后一个元素相乘为10
                {
                    num++;
                    top = top-2;//元素出栈
                }
                if(top >= 20000000-1)
    {
     cout<<"越界"<<endl;
     }
            }
        }
        return num;
    }

后来看到有这种算法,以201为例,201中有40个能被5整除的数,201/5=40;num = 40,乘积之后给尾部提供一个0的因子个数。但是有个问题25,50,75,这些数与足够偶数相乘尾部有多个零,25*4=100,那这是就不能只计算201中有几个5的倍数 ,还要继续计算201中有几个25倍数,也就是201/5/5=40/5=8,乘积之后给尾部提供两个0的个数,num= 40+8=48.也许会有为什么不是40+8*2=56的疑问,因为这8个数提供的第一个0已经在40中包括了,所以只需要加上第二次的8个0就好了,以此类推。至于偶数嘛,阶乘的计算是很大的,所以偶数短缺的问题应该是可以忽略的,如果数据很小比如4!1--4中没有能被5整除的数,num= 0;如果是5!偶数有4和2,偶数短缺应该很难出现的。

提交代码:    long long trailingZeros(long long n) {
        // write your code here, try to do it without arithmetic operators.
        long long num = 0;
        long long temp = n;
        while(temp / 5!=0)
        {
            num = num + temp/5;
            temp = temp/5;
        }
        return num;
    }

emmmm,虽然思路错了两次但是还是有些收获的,学会了高精度计算阶乘算法,还有之前的位运算,打算有时间整理一下。同时我发现我的思维过于僵化,还是有点死脑筋,如果早一些想到正确的思路就不用浪费好几个小时琢磨了,哭哭。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值