两数相除,面试题解答

By admin in 4858.com on 2019年4月23日

近3个月直接在写作业,空闲时间刷刷leetcode,刷题进程中相遇了壹道比较风趣的难点,和豪门享用。

leetcode笔记:Divide Two Integers

1. 难点叙述

Divide two integers without using multiplication, division and mod
operator.

If it is overflow, return MAX_INT.

贰. 标题分析

标题的情趣轻松明了,正是供给不利用乘法、除法和取余mod,输入四个整数,输出除法操作的结果。

出去乘除法,剩下的唯有加减法和位运算,那是一见倾心想到的,而平昔行使减法,对被除数逐次减去除数大小的值,记录被减次数,明确是能够汲取和除法操作同样的结果,但该方法比较傻瓜,且会晚点,时间复杂度为O(n)。

利用位运算能够做到O(logn)的复杂度,但要一步想到具体操作却也不那么简单,首先,大家领略其余叁个平头都能够表示成以2的幂为底的1组基的线性组合,即num = flag0 * 2^0 + flag1 * 2^1 + flag2 * 2^2 + ... + flagn * 2^n
其中,flag0, flag1, flag2, ..., flagn 取值为0 & 1

基于以上实际,固然令:dividend / divisor = num,则有:

dividend = divisor * num = divisor * (flag0 * 2^0 + flag1 * 2^1 + flag2 * 2^2 + ... + flagn * 2^n)

对此除数,使用移动操作<<使其每便翻倍,从而收缩减法求商的次数。以下是手续:

当被除数大于除数时,对除数乘二(代码中央银行使变量step用于记录每趟除数乘2),直到step大于被除数甘休。记录移位操作的次数i。
如若被除数大于除数,那么被除数减去step。直到被除数小于除数。保存结果。
输出结果result。

注:

byte:128~127 (1Byte)
short :32768~32767 (2Bytes)
int:-2147483648~2147483647 (4Bytes)
long:-9223372036854774808~9223372036854774807 (8Bytes)

叁. 示例代码

class Solution {
public:
    int divide(int dividend, int divisor) {
        if (dividend == 0 || divisor == 0) return 0;
        if (dividend == INT_MIN && divisor == -1) return INT_MAX; // 溢出
        bool negative = (dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0);
        long positiveDividend = abs(long(dividend));
        long positiveDivisor = abs(long(divisor));
        long result = 0;

        while (positiveDividend >= positiveDivisor) // 被除数大于除数
        {
            long step = positiveDivisor;
            for (int i = 0; positiveDividend >= step; ++i, step <<= 1)
            {
                positiveDividend = positiveDividend - step;
                result += 1 << i;
            }
        }
        return negative ? -result : result;
    }
};

四. 小结

使用位运算来减轻此类主题素材,是挺轻松想到的,不过实际哪些操作又是别的3遍事。

Two Integers 1. 标题叙述
Divide two integers without using multiplication, division and mod
operator. If it is overflow, return MAX_INT. 二. 标题分析…

基础知识

两数相除,面试题解答。带余数除法涉及上面是总计公式和概念:

被除数 / 除数 = 商......余数
  • 被除数 dividend :除数 * 商 + 余数
  • 除数 divisor :(被除数 – 余数)/ 商
  • 商 quotient :(被除数 – 余数)/ 除数
  • 余数 remainder :被除数 – 除数 * 商
    计算公式中涉嫌伍个数字,知道了内部的一个,能够求出别的二个。
    除此以外一个器重的关系是:余数要比除数小

目录

 

Scratch编程

在微型Computer语言中,常常用 /
来代表“除以”得到,在不可能整除的境况下默认会获得二个小数(关于整数和浮点数未来再讲);
用%来来总计余数
在scratch中也是要分别总计出两片段的。
先定义出大家须要的6个变量

  1. 计算商
    先是选取运算中的除法“/”
这样还不行,在不能整除的情况下得到的是小数,所以还需要使用**向下取整**取出整数部分  

2.乘除余数
余数的测算就直接使用运算中的余数总计就能够。

3个完整的scratch程序:

舞台和角色

scratch猫做除法题

  • 铺天盖地简单介绍
  • 不用*和/总计整数除法
  • 贰伍匹马找寻前5名
  • 推断题样例

难题叙述:

总结

这一次学习了含蓄余数的除法总结。并且使用scratch中的block达成2个除法总结。学习了那些今后,我们能够试1试:让scratch猫出除法题,然后我们应对,scratch猫再剖断回答是不是科学。

上一篇:教孩子学Scratch编程之0x七:星型与星型
下一篇:教孩子学Scratch编程之0x拾:圣诞喜悦!关于scratch.mit.edu

 

给定七个整数,被除数 dividend 和除数 divisor。将两数相除,供给不利用乘法、除法和
mod 运算符。重返被除数 dividend 除以除数 divisor4858.com, 得到的商。

一日千里简单介绍:

示例 1:

  《算法设计手册》(The Algorithm Design
马努al)是本比较杰出的算法书了。如若说《算法导论》偏向于数学,那么《算法设计手册》更偏向于工程采取(至于《Computer程序设计格局》,近扶桑身是没时间通读,只是有时候当工具书查查,就不提了)。前者的课后题中的面试题部分挺潮的,假诺在google上搜索一下,发掘多数都以名企考过的,恐怕是因为第二版问世时间比较近的来头?笔者非常的小相信是笔者本人出的下一场被大商场拿去面试的,而是我引用的考过的面试题。有了那一层筛选,这么些面试题品质有保管啊。

输入: dividend = 10, divisor = 3
输出: 3

  由于看的是英文版,大多数题都以自家翻译过来的,个人英文水准有限,有的糟糕驾驭的地方尽量参照相关解答来精晓,并发问了在海外留学的情人,也许仍不怎么措辞不可靠赖或有误的地方,乞求谅解并接待建议。同时,有的难点用到的可比偏僻的文化恐怕会和正文有提到,那种情状会确定注解。

示例 2:

  虽然官网上有个wiki
answer提供了绝大大多答案,可是有的不很确切,小编写的和整理的都以相比好的解答。

输入: dividend = 7, divisor = -3
输出: -2

  原著者的wiki
answer页面曾经失效。

说明:

  另附上在线修正表:

  • 被除数和除数均为
    3二 位有暗记整数。
  • 除数不为 0。
  • 若是我们的情状只可以存储3贰 位有标识整数,其数值范围是 [−2**31,  2**31 −
    1]。本题中,假若除法结果溢出,则赶回 二**31 − 1。

 

 

第1章:

率先感应是那道题依然挺轻松的,用减法达成除法不就好了,python刷题达成以致足以一贯使用range()来兑现除法,要求注意的点如下:

1-28

1.提早判定结果的正负号

  不用*和/计算整数除法。请寻觅最快的办法。

2.结果在[-2**31,2**31-1]中,要判别结果是不是移除

解答:

叁.应用range()来计量除法时,一旦除法能够整除大家要对结果+一,因为len(range(三,七,叁))的结果是二,len(range(三,九,三))的结果也是2

  纵然起始化1个计数变量,每当被除数减去除数的一回就自增一向到被除数小于除数那些暴力解法可行,但显然比相当慢。那是wiki
answer答案,但它在无数景况下都难熬,举例100/一。其实践的次数正好和相除的结果一律,用m表示除数,n表示被除数,时间复杂度是O(m/n)。

 

// Note: This only works for positive values!
int divide(int numerator, int denominator) {
  int quotient = 0;

  while(numerator >= denominator) {
      numerator -= denominator;
      quotient++;
  }
  return quotient;
}

代码如下:

   上边看看另一种解法。

class Solution(object):
    def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        below = 1
        if dividend < 0 < divisor or divisor < 0 < dividend:
            below = -1

        dividend,  divisor = abs(dividend),  abs(divisor)
        if dividend < divisor:
            return 0
        elif divisor == 1:
            result = dividend * below
            if result >= 2**31-1:
                return 2**31-1
            return result


        result = len(range(divisor, dividend, divisor))
        if (result+1) * divisor == dividend:
            result += 1 
        return result * below

  一般限制使用*和/时,很轻巧思索动用位移运算来代表,因为对于无符号数,左移1位(在不溢出时)也正是乘以二,右移1个人相当于除以2。假若在纸上拓展除法的笔算,是只用到了乘法和减法的。可是一般的10进制整数除法和位运算有何关联吗?为了将双方建立联系,必须把10进制数转化成二进制数,观望除法的拓展情况来找规律。举例100/7,写成二进制来实行笔算,总括进程如下图:

友好写了无数case来测试都是没难点的,代码提交到leetcode,正剧了。。。内部存储器错误,看来是内部存款和储蓄器超了。难点出在基本语句len(range(divisor,
dividend,
divisor))上,怎么既能保障近日代码的简洁性又能减低内部存款和储蓄器使用呢。作者解决办法是运用xrange代替range,简单的讲range重临的对象是个list,会开荒八个十分的大的上空,而xrange不一致,再次回到的是生成器,所以对内部存款和储蓄器的行使获得了第二手的优化。重新提交,果然通过了。

4858.com 1

 

  那样就大约了,从那一个姿势能够看出,2进制除法笔算只提到了减法和包括的移位与大小相比较,原先的乘法已经被活动所代表。因而,具体的编码,正是把用笔算除法的进度转化成代码而已。

最终代码如下:

  可是,一般思量选拔除法的情状,必然要思量除数是不是为0。除数为0时以此除法是地下的,无法三番五次进行,要求报错。

class Solution(object):
    def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        below = 1
        if dividend < 0 < divisor or divisor < 0 < dividend:
            below = -1

        dividend,  divisor = abs(dividend),  abs(divisor)
        if dividend < divisor:
            return 0
        elif divisor == 1:
            result = dividend * below
            if result >= 2**31-1:
                return 2**31-1
            return result


        result = len(xrange(divisor, dividend, divisor))
        if (result+1) * divisor == dividend:
            result += 1 
        return result * below

  既然涉及了编码,借使选取C语言来产生,要专注的是:在C规范中,带符号数右移的结果在C语言里是落成相关的,具体结果有赖于落成,而不鲜明是用符号位补、用一补或然用0补最高位。为了制止那一个陷阱,提出先明确结果——也便是商的标记,然后把被除数和除数都转载为无符号数,那样位移时就不会出错。

 

  但是,那又涉及了有带符号数与无符号数的转变,它们贰者的象征范围的主题材料是不一样的。万幸被除数和除数从带符号数转化为无符号数时并不会丢掉数据,而且商的相对化值必然小于被除数的相对值(因为除数是整数,为0时报错,大于等于壹时才继续拓展),这时把商转化回带符号数时也不会丢掉数据,能够放心的打开。可是那一点最佳在面试时告知面试官你早已注意到了那些主题素材,肯定会为您的记念加分。

愿意对大家享有协助~

 

int division(int m,int n) {
    //calculate m/n without * and /
    unsigned int rest,divisor, op,result = 0;
    int flag;
    int bits = 0;
    //bits用于记录商的1在哪一位
    assert(n!=0);
    if((m<0 && n>0) || ( m>0 && n<0 ))
        flag = -1;
    else
        flag = 1;
    rest = m>0?m:-m;
    divisor = n>0?n:-n;
    if(rest < divisor)
        return 0;

    op = divisor;

    /*            2013.8.30           */
    /*经过博客园园友infinityu的提醒重写 */
    while(op<=rest) {
        bits++; 
        op=op<<1;
    }
    op=op>>1;
    bits--;

    while(op>=divisor) {
        if(rest>=op) {
            rest-=op;
            result += 1<<bits;
        }
        op = op>>1;
        bits--;

    }
    /*      重写部分结束         */

    return flag * result;
}

 

 

 

  由于须求把被除数转化为二进制举办测算,最多做了其2进制表示位多次的减法,由此对于被除数m,算法复杂度为O(logm)。

  稍作修改,把最后的小于除数divisor的result收取便是余数,这样就能把除法运算改写为取模运算%了。假使把参数表修改为传送结果地址,同时获取商和余数也是足以的。

  可知,那①道面试题考到了算法优化、除法除数为0那几个广阔错误、将除法从10进制引申到二进制、二进制的位运算、语言特征中的无符号数和带符号数的运动、无符号数和带符号数的互相转变,你还能更进一步商量算法复杂度、以及算法的增添性,确实很能体察被面试者对算法的精晓情状。

  p.s.经过园友infinityu的提醒,开采源代码中有bug,重写之后已经对壹~一千之间具备整数互相相除的测试。为了有利于记录商的一应有在哪一人,使用变量bits来提醒。

 

1-29:

  2五匹马,一遍最多5匹马比赛,怎么着用最少的竞技次数找到最快的前3匹马?(如若全体马的速度在每场比赛的发挥都同样且各匹马之间不等同,比赛时不知所可记录具体每匹马跑完全程的年月)

解答:

  新瓶装旧酒的难点,关键是寻觅每趟的正确性候选以及尽只怕选取上次比赛获得的消息。

  先分伍组A、B、C、D、E,组内比赛,若是A壹为A组第3。一共5场。

  将A1~E一举行比赛,不要紧设第二是A一,那么最快正是A一。

  第三快不得不在A二、B一~E1中出现。同时,那时知道了B一~E一的速度,无妨B一>C壹>D一>E一,那样D一、E一以及1切D组和E组能够被拔除出第一和第三的候选。同时,C二必将不容许是第二快。那时候选为A贰、A三、B1、B贰、C一,竞技壹回,前两名即为第一和第一。

  (注意:那里分析时未有”丰硕”利用全数已知音信。更进一步运用已知新闻的方法请看扩充一。)

  综上,1共竞赛了八回。

 

扩展1:

  64匹马,每一次最多八匹竞赛,须要用最少场次获得前4名。别的标准化同上题。

 解答:

  根据上题的剖析方法并不能够收获最少竞赛次数,上面看看怎样充裕利用已知音讯来到达至少竞赛次数。

  首先分8组A~H决出各组顺序,共需八场,并且组内顺序排列为A一>A2>…>A捌。

  头名在A一~H1中决出,不要紧设为A一>B一>…>H一,须要1场较量。

  此时第贰名只可以是A二和B一当中之1(分化于上题分析,C一~H壹实在能够一向丢掉),但决出第1名只用五个赛道太浪费了。为此进一步分析,纵然A二>B一,那么第1名只好是A三、B1之1;假诺B一>A贰,那么第三名只好是A二、B2、C一。那三种处境都只涉嫌5匹马依然不满八匹。用那种思路打开宏观剖析,表示为树状并把叶子处须求相比较的马的号码举办标注:

  角逐第2名时A2>B1

    第3名候选A3,B1

    角逐第3名时A3>B1

      第4名候选A4,B1    
————(A2,A3,A4,B1)

    角逐第3名时A3<B1

      第4名候选A3,B2,C1   
————(A2,A3,B1,C1)

  角逐第2名时A2<B1

    第3名候选A2,B2,C1

    角逐第3名时A2>B2>C1

      第4名候选B2,A3     ————(A2,A3,B1,B2,C1)

    角逐第3名时A2>C1>B2

      第4名候选C1,A3     ————(A2,A3,B1,B2,C1)

    角逐第3名时B2>A2>C1

      第4名候选A2,B3     ————(A2,B1,B2,B3,C1)

    角逐第3名时B2>C1>A2

      第4名候选B3,C1     ————(A2,B1,B2,B3,C1)

    角逐第3名时C1>A2>B2

      第4名候选A2,C2,D1    ————(A2,B1,B2,C1,C2,D1)

    角逐第3名时C1>B2>A2

      第4名候选C2,B2,D1    ————(A2,B1,B2,C1,C2,D1)

  可知,假若能够三次交锋收获A二,A三,Copac,B1,B贰,B3,C1,C2,D壹的完整排列次序才具分晓前二~四名。很心痛1共是九匹马,不可能一场交锋一定获得结果。那么求最优方案,正是将以上各支行出现最晚现身的马去掉,即C2或D一,进行一场较量。运气好的话那壹轮能够决出前二~四,1共竞赛10轮,运气倒霉的话还索要加赛一轮。而去掉C2或D壹能够确认保证只比赛10轮的可能率最大。

 

扩展2:

  25匹马,5个赛道,决出前5。

解答:

  分析和“扩充壹”类似,留给读者本人产生。如若想核对答案,能够查阅:

 

=============================================================

  1-30~一.3四是多少个估计题,当年google确实考过其中的主题素材。不过那里不做解答了。

  关于猜想标题标思绪和平化解法,能够参照《编制程序珠玑》《编制程序珠玑(续)》和自家写的连带文章:[珠玑之椟]测度的使用与Little定律

  另外,“毫不再去算壹辆校车上得以装多少个高尔夫球了。”因为
谷歌(Google)已确认,这个用来测试求职者的智力题/脑筋急转弯(满世界有个别许钢琴调音师?为啥井盖是圆的?),就不能够预测出求职者是不是会化为一个人好职工。
“今后相信更平常办法去面试潜在职员和工人”。相关链接

1.30

  世界上有多少个钢琴调音师?

1.31

  U.S.A.有稍许个加油站?

1.32

  曲棍球场上的雪有多种?

1.33

  美利哥公路累计有多少长度?

1.34

  平均来看,你查看一本曼哈顿电话簿时,你供给自由翻开多少次技能找到1个加以的名字?

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有