刷穿剑指offer-Day01-整数I

前序

这是刷穿剑指offer系列的第一篇文章,在这里需要简单和大家说明下关于这个系列的一些说明。

数据结构学习顺序

如果大家购买了剑指offer-专项突破,那么大家肯定看到了关于这本书目录对应的数据结构。但前文也说了,这本书的价值更多的在于题目上,如果知道了题目,那么就没有太大的必要去买这本书了,那么怎么获取本书的目录与所有题目呢?这里和大家介绍下。

  1. 首先打开力扣首页并进入题库专栏
  1. 选择剑指offer专项突击版,即可获取到本书大纲与所有题目列表。

关于文章和解题

昨天的公众号文章介绍了咱们的刷题学习策略,再这里简单复述下:

  1. 每种数据结构、每道解题,都会讲解Python、Java对应的知识内容。
  2. 针对每种数据结构,大概会花 4 天的时间进行学习。
  3. 第一天介绍这个数据结构对应的Python、Java语言涉及到的知识点,然后给出一道简单题目完成入门。
  4. 之后的两天,每天会讲解本书中的两道题目,并在讲解题目的同时,针对这道题涉及到的知识点进行总结
  5. 最后一天,针对该数据结构以及这三天的题目,进行一个总结,并推荐力扣上同类型题目列表,供大家深入学习与验证这种数据结构的学习成果。

具体每天文章的更新时间,现在还不能确定,因为没有存稿,都是每天学习、总结后写文章,所以想固定一个时间比较难。更新时会在刷穿剑指offer的微信群通知大家,针对文章或者当天的题目有什么问题也欢迎大家在群里相互讨论。

如果现在还没有加入群的朋友,可以通过公众号、微信联系我,但为了避免打广告的马甲号,入群前请向我提供你的力扣用户名、个人主页链接、或者力扣个人二维码。我会关注你确认身份后,拉你入群。

好了,要说明的就这些内容了,让我们开始今天的算法学习之旅吧!

Day01-整数I

本以为二进制的考题很少,毕竟有些偏底层,但最近不管周赛还是群友提问,二进制的题目如雨后春笋的都冒出来了,所以借着本章的机会好好复习下这方面的知识吧。

对于Python玩家来说,数字只分两种:整型、浮点型,不是小数的都是整数真简单啊!但这里有一个Python的面试考点:复数(大家仅做了解即可。)

复数(Complex)是Python的内置类型,直接书写即可,复数由实部(real)和虚部(imag)构成,在 Python 中,复数的虚部以j或者J作为后缀,具体格式为: a + bj a 表示实部,b 表示虚部。

然而,对于Java来说,单说整数就细分为了byte、short、int、long。

这里简单总结下Java的整数类型:

数据类型 范围(^ 代表次方) 字节数
byte 类型 -128(-2 ^ 7) ~ 127(2 ^ 7 - 1) 1字节,8位
short 类型 -32768(-2 ^ 15)~ 32767(2 ^ 15 - 1) 2字节,16位
int 类型 -2,147,483,648(-2 ^ 31)~ 2,147,483,647(2 ^ 31 - 1) 4字节,32位
long 类型 -2 ^ 63 ~ 2 ^ 63 -1 (数值太长没必要记了...) 8字节,64位

那么,在做整数题目时,Python与Java的差别是什么呢?

  • Python: 放心大胆无脑冲!
  • Java:在越界的边缘战战兢兢...

二进制转换

在讲解二进制的与、或、异或方法前,首先需要复习下Java和Python十进制和二进制的互转的api操作。

转换方式 语言 api(函数) 结果
十进制转二进制 Java Integer.toBinaryString(int: 100) "1100100"
十进制转二进制 Python bin(100) "0b1100100"
二进制转十进制 Java Integer.valueOf(s: "1100100",radix: 2) 100
二进制转十进制 Python int('0b1100100',2) 100

不管是Java还是Python在进行二进制转换后,得到的结果都是字符串,只是python会多一个"0b"的前缀。然而只有这么一点区别么?错了!

Java的二进制是存在符号位的,而Python是不存在符号位的,我们以-10举例:

Integer.toBinaryString(-10) -> "11111111111111111111111111110110"

bin(-10) -> "-0b101"

另外负数的二进制Python可以直接转化为十进制,但Java则不行…需要按位取反+1后再进行转化。

这里的语言差异需要大家留意!

与、或、异或

与、或、异或的运算都是将两个数字转化为二进制数后,按位比较的,运算规律Java与Python一致,具体如下图:

与 ( & ) 0 & 0 = 0 1 & 0 = 0 0 & 1 = 0 1 & 1 = 1
或( | ) 0 | 0 = 0 1 | 0 = 1 0 | 1 = 1 1 | 1 = 1
异或( ^ ) 0 ^ 0 = 0 1 ^ 0 = 1 0 ^ 1 = 1 1 ^ 1 = 0

与、或 操作符合日常逻辑,异或操作大家只要记住,相同为0 ,不同为1即可。

PS: Java 在遇到与或是经常会顺带问问短路与 ( && ) 短路或( || )的区别,即遇到前半部分符合条件立即终止判断。

位移操作

位运算分 左位移、右位移,符号对应<< 和 >>,简单来说就是将数字 m 向左、右移动n位。

如果是左移,最左边的n位将被丢弃,同时使用n个0进行补充至字符串末尾。

如果是右移,字符串末尾的n位被丢弃,但处理左边位时默认补0?No,针对Java等有符号位的语言,需要补充符号位,即正数补0,负数补1!

以上就是针对整数这章,初步涉及到的算法知识,后续会根据题目,再进一步补充,好了,开始做题吧!

剑指Offer001.整数除法

https://leetcode-cn.com/problems/xoh6Oh/

难度:简单

题目:

给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 '*'、除号 '/' 以及求余符号 '%' 。

注意:

  • 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2 ^ 31, 2 ^ 31−1]。本题中,如果除法结果溢出,则返回 2 ^ 31 − 1

提示:

  • 2 ^ 31 <= a, b <= 2 ^ 31 - 1
  • b != 0

示例:

示例 1:
输入:a = 15, b = 2
输出:7
解释:15/2 = truncate(7.5) = 7

示例 2:
输入:a = 7, b = -3
输出:-2
解释:7/-3 = truncate(-2.33333..) = -2

示例 3:
输入:a = 0, b = 1
输出:0

示例 4:
输入:a = 1, b = 1
输出:1

分析

首先这种不能使用xxx方法的题目,一般机试时是不会出的,因为限制不了编辑器,人工阅卷又需要耽误时间。
但正因为不会出现在机试中,所以在手撕算法时却会成为热门,所以请不要违背题意的直接使用乘、除提交题目。

拿示例一来说,一个最直观的方法就是循环使用a-b,计算减了多少次,即可得到结果。
但请注意a、b的取值范围,如果a为2 ^ 31−1,b为1那么仅一次就需要计算2 ^ 31−1次,所以这个思路是不可行的。
那么,既然不能从被除数上下手,能否通过除数进行操作呢?如果大家对二分搜索熟悉,相信很快能找到思路。

  1. 初始化返回值ret = 0
  2. 如果换成如果被除数大于除数,则除数扩大一倍
  3. 若被除数仍大于除数,这除数再次扩大一倍
  4. 直到除数下一次翻倍比被除数大时,将被除数减去除数,并将ret+=除数扩大的倍数,结束这一轮循环
  5. 重复2、3、4,直到被除数小于除数,终止循环并返回ret即可。

另外:在计算开始时,我们需要注意判断除数和被除数的正负号问题。如果是Python,只需要记录正负号即可。但对于Java而言,
由于负数的取值比正数大1,转换成正数计算会造成溢出,所以简单的方式是将数字转化为负数来进行比较。

解题:

class Solution:
    def divide(self, a: int, b: int) -> int:
        ret = 0
        flag = False if (a > 0 and b > 0) or (a < 0 and b < 0) else True
        a, b = abs(a), abs(b)

        def calc(x, y):
            n = 1
            while x > y << 1:
                y <<= 1
                n <<= 1
            return n, y

        while a >= b:
            cnt, val = calc(a, b)
            ret += cnt
            a -= val
        ret = -ret if flag else ret
        return ret - 1 if ret >= 2 ** 31 else ret
class Solution {

    public int divide(int a, int b) {
        int flag = 0;
        if (a > 0) {
            a = -a;
            flag += 1;
        }

        if (b > 0) {
            b = -b;
            flag += 1;
        }
        int ret = calc(a, b);
        if (flag != 1 && ret == Integer.MIN_VALUE) {
            ret++;
        }
        return flag == 1 ? ret : -ret;
    }

    private int calc(int a, int b) {
        int ret = 0;
        while (a <= b) {
            int cnt = 1;
            int val = b;
            while (val >= Integer.MIN_VALUE >> 1  && a <= val << 1) {
                cnt += cnt;
                val += val;
            }
            ret -= cnt;
            a -= val;
        }
        return ret;
    }
}

欢迎关注我的公众号: 清风Python,带你每日学习Python算法刷题的同时,了解更多python小知识。

我的个人博客:https://qingfengpython.cn

力扣解题合集:https://github.com/BreezePython/AlgorithmMarkdown

你可能感兴趣的:(刷穿剑指offer-Day01-整数I)