剑指offer2 整数

剑指 Offer II 001. 整数除法

题目描述

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xoh6Oh

题解

 由于乘法不允许使用,不是2的偶次幂又不是很“快”,把右边算法想成对左边结果的分解,是不是就了然?

  1. 初始化返回值ret = 0
  2. 如果被除数大于除数,则除数扩大一倍
  3. 若被除数仍大于除数,这除数再次扩大一倍
  4. 直到除数下一次翻倍比被除数大时,将被除数减去除数,并将ret+=除数扩大的倍数,结束这一轮循环
  5. 重复2、3、4,直到被除数小于除数,终止循环并返回ret即可。
def divide(self, a: int, b: int) -> int:
    flag = False if (a>0 and b > 0) or (a < 0 and b < 0) else True
    a,b = abs(a),abs(b)

    def caculateCore(x,y):
        # where y is divider
        n = 1
        while x > (y<<1):
            y<<=1
            n<<=1
        return n,y

    ret = 0
    while a>=b:
        # cnt 是放大倍数, val 是除数b被方法cnt倍的值
        cnt,val = caculateCore(a,b)
        ret += cnt
        a-=val
    ret = -ret if flag else ret
    return 2**31 -1 if ret >= 2**31 else ret

剑指 Offer II 002. 二进制加法

题目描述

给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。

输入为 非空 字符串且只包含数字 1 和 0

 题源:剑指 Offer II 002. 二进制加法 - 力扣(LeetCode) (leetcode-cn.com)

题解

牢记以下三点即可:

  1. 二进制的法则:逢二进一
  2. 进位的这个一,需要找个变量才存储它,才能在下一次的循环中获取
  3. 对于不等长的字符串,需要进行适当的优化与判断

 

def addBinary(self, a: str, b: str) -> str:
    #return bin(int(a,2)+int(b,2))[2:]
    ret = ''
    carryBit = 0 #用于存进位
    i, j = len(a) - 1, len(b) - 1
    while i >= 0 or j >= 0 or carryBit:
        if i >= 0:
            carryBit += ord(a[i]) - ord('0')
        if j >= 0:
            carryBit += ord(b[j]) - ord('0')
        ret += str(carryBit % 2)
        carryBit //= 2
        i, j = i - 1, j - 1
    return ret[::-1]

 因为是从低位开始,放在ret的左边,所以需要ret[::-1]字符串转置一下

ord()函数

ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。

ord(c) # c是一个字符,返回值是对应十进制整数

 

剑指 Offer II 003. 前 n 个数字二进制中 1 的个数

题目描述

给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

题源:剑指 Offer II 003. 前 n 个数字二进制中 1 的个数 - 力扣(LeetCode) (leetcode-cn.com)

题解

使用python内置函数计算汉明重量

#调用库函数  对应c++中的 ((bitset<32>)n).count()
bin(n).count('1')

使用自定义函数(位运算)计算汉明重量O(M) O(1)

def hammingWeight(n:int) -> int:
        # *巧用 n & (n-1)  把复杂度将为 O(M) O(1)  P.S. n & (n-1) 还可以用来判断n是不是2的幂
        res = 0
        while(n>0):
            res+=1
            n = n & (n-1)
        return res

对于这题来说这个方法不太好,没有动态规划快,因为不是计算单个数的汉明重量而是计算 [n] 的汉明重量

使用动态规划,汉明重量的特征

  • 如果 i 是偶数,那么他刚好是 i/2 左移,他的汉明重量和 i/2 是一样的
  • 如果 i 是奇数,那么他的汉明重量是 i-1 的汉明重量+1  
def countBits(self, n: int) -> List[int]:
    #线性时间 O(n) 内用一趟扫描做到
    #算法的空间复杂度为 O(n)
    #不使用任何内置函数(如 C++ 中的 __builtin_popcount )来执行此操作 使用动态规划
    dp = []
    dp.append(0)
    for i in range(1,n+1):
        dp.append(dp[i-1]+1) if i%2==1 else dp.append(dp[int(i/2)])
    return dp

 

剑指 Offer II 004. 只出现一次的数字

题目描述

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

题源:剑指 Offer II 004. 只出现一次的数字 - 力扣(LeetCode) (leetcode-cn.com)

题解

FSM+位运算

参考K神题解:剑指 Offer II 004. 只出现一次的数字(有限状态自动机 + 位运算,清晰图解) - 只出现一次的数字 - 力扣(LeetCode) (leetcode-cn.com)

 

 

 

上面是对每个数的单独一个bit考虑的,应用到所有比特也成立,因为各bit之间互不干扰 

而恰好这样的状态设置,就完成了 mod 3 的操作,所以最后高位 b 一定是全0, a 就是那个只出现一次的数

def singleNumber(self, nums: List[int]) -> int:
    # 对于最低位而言 这个位上有多少个1记为wt(b_0)  wt(b_0) mod 3 \in {0,1,2}    三种状态     00 01 10 分别对应表示0,1,2  
    # 直接应用在所有的位上逻辑同样成立
    a,b = 0,0
    for num in nums:
        a = ~b &(num^a)
        # 状态高位是在状态地位修改后的基础上修改的
        b = ~a &(num^b)
    # 因为状态的缘故  只关心状态啊=的地位就可以  就是说当所有的数加上之后,状态的高位肯定是0
    return a

hash表

  • 创建一个哈希表
  • 循环数组,将每个元素挨个加入哈希表中
  • 遍历哈希表中的数据,查找哪个数字只出现了一次返回。
def singleNumber(self, nums: List[int]) -> int:
    mydict = {}
    for num in nums:
        if num in mydict:
            mydict[num] += 1  
        else:
            mydict[num] = 1
    for key, value in mydict.items():
        if 1 == value:return key

 

剑指 Offer II 005. 单词长度的最大乘积

题目描述

给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。

  • 单个字符串有重复没关系,主要是两个字符串不能出现任何一个相同的字符

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/aseY1I

题解

暴力搜+集合

def maxProduct(self, words: List[str]) -> int:
    # 暴力搜O(n^2) 
    def twoStrProduct(str1,str2)->int:
        set1,set2 = set(str1),set(str2)
        set3 = set1.union(str2)
        #if len(set1)+len(set2)==len(str1)+len(str2) and len(set3)== len(set1)+len(set2):
        if len(set3)== len(set1)+len(set2):
            return len(str1)*len(str2)
        else:
            return 0
    ret = 0
    for i in range(len(words)-1):
        for j in range(i+1,len(words)):
            tmp = twoStrProduct(words[i],words[j])
            if tmp>ret:
                ret = tmp
    return ret

二进制特性

  • 利用二进制特性,将字符是否出现作为标志位,通过按位与判断是否有重复字符,比较操作的复杂度低
  • 第一个循环为统计26个字母是否出现在word中,并存在一个数中。例如,如果 'a' 在 word[0] 中,那么 res[0] 的 LSB 便为1。
    def maxProduct(self, words) -> int:
        '''二进制思路
        26个字母对应26位长度的二进制数字,1表示含对应字母
        位运算的一些trick可以快速计算是否重合
        '''
        res = [0]*len(words)
        max_res = 0

        for i in range(len(words)):
            for c in words[i]:
                res[i] |= 1 << ord(c) - ord('a')

        for i in range(len(res)):
            for j in range(i, len(res)):
                if res[i] & res[j] == 0:
                    # 按位与判断是否有重合,若结果为0,说明没有重合
                    max_res = max(max_res, len(words[i])*len(words[j]))

        return max_res

作者:frovvn
链接:https://leetcode-cn.com/problems/aseY1I/solution/li-yong-er-jin-zhi-te-xing-huo-counterji-zhpg/

 

剑指 Offer II 006. 排序数组中两个数字之和

题目描述

给定一个已按照 升序排列  的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 0 开始计数 ,所以答案数组应当满足 0 <= answer[0] < answer[1] < numbers.length 。

假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kLl5u1

题解

对撞(双)指针

def twoSum(self, numbers: List[int], target: int) -> List[int]:
    #假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。
    #升序排列  
    #双指针
    i,j = 0,len(numbers)-1
    while i<j:
        sum = numbers[i]+numbers[j]
        if sum<target:
            i+=1
        elif sum > target:
            j-=1
        else:
            return [i,j]

hash表

  • 遍历 enumerate(numbers) ,即遍历 numbers
  • 如果 num不在字典 d 中,字典新增键值对:键是该元素的目标匹配元素,值是该元素的下标
  • 如果 遇到新的元素num 在字典中,意味着这个元素和之前某个元素匹配,返回 [之前元素的下标,当前元素的下标]
def twoSum(self, numbers: List[int], target: int) -> List[int]:
    #假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。
    #升序排列  
    #hash表
    d = {}
    for i, num in enumerate(numbers):
        if num not in d:
            d[target - num] = i
        else:
            return [d[num], i]

enumerate(list)用法
tetslist = [0,0,3,4]
valuelist = list(range(len(tetslist)))
mydict = enumerate(tetslist)
for i in mydict:
    print(i)

output:
(0, 0)
(1, 0)
(2, 3)
(3, 4)

 

posted @ 2021-12-20 22:15  PiaYie  阅读(80)  评论(0编辑  收藏  举报