【leetcode】数学相关刷题tricks

1.脑筋急转弯问题

LeetCode292题 Nim游戏

如果一个人拿的时候只剩4颗石子,那么无论他怎么拿,总会剩下1~3颗石子,对手都能赢

class Solution:
    def canWinNim(self, n: int) -> bool:
        if n % 4 == 0:
            return False
        else:
            return True

LeetCode319题 灯泡开关

只有平方数对应的灯泡,乘法因子个数是奇数,最后是亮的

class Solution:
    def bulbSwitch(self, n: int) -> int:
        return int(pow(n, 0.5))

 LeetCode877题 石子游戏

注意到石头堆数量为偶数,石头的总数为奇数。

如果我们把石头按索引的奇偶分为两组,那么这两组石头的数量一定不同,也就是说一堆多一堆少。因为石头的总数是奇数,不能被平分。而作为第一个拿石头的人,你可以控制自己拿到所有偶数堆/奇数堆。

比如一共有四堆石头,你最开始可以选择第 1 堆或第 4 堆。如果你想要偶数堆,你就拿第 4 堆,这样留给对手的选择只有第 1、3 堆,他不管怎么拿,第 2 堆又会暴露出来,你就可以拿。如果你想拿奇数堆也是同理。

class Solution:
    def stoneGame(self, piles: List[int]) -> bool:
        return True

2.位运算

原码、反码、补码都是计算机存储具体数字的编码方式,以二进制进行编码。原码的最左侧位是符号位,0代表正数,1代表负数。

反码:主要针对负数的处理,非负数的反码与原码相同;负数的反码在原码基础上,符号位不变,其他位取反

补码:非负数的补码与原码相同;负数的补码在反码基础上+1

7-7
原码0000 01111000 0111
反码0000 01111111 1000
补码0000 01111111 1001

补码的提出是为了方便计算,计算机底层以补码形式保存和处理所有整数,所有位运算符都是以补码形式操作的

例如我们用补码做减法,结果就是对的:7-7=7+(-7)=0000 0111 + 1111 1001=0000 0000=0

位运算符:

&判断奇偶:1 & x 等于1,x为奇数;等于0,x为偶数
|
异或^

相同为0,不同为1

取反~

7二进制为 0000 0111,

~7=1111 1000(是补码形式,需要再取补码得原码)

->1000 0111(反码)

->1000 1000(补码)

->-8(转换为十进制)

左移<<

a<<x,运算数a所有二进制位向左移x位,高位丢弃,低位补零,

7二进制为 0000 0111,左移1位后变成 0000 1110,对应十进制数为14,即7<<1=14,左移1位相当于乘2

右移>>

a>>x,运算数a所有二进制位向右移x位,低位丢弃,高位按符号位补全,

-7>>1,-7补码为1111 1001,右移1位后为1111 1100,

再取补码为原码1111 1100->1000 0011->1000 0100,对应十进制为-4,即-7>>1=-4

n&(n-1)运用

n&(n-1)这个操作在算法中比较常见,作用是消除数字n的二进制表示中的最后一个 1

LeetCode191题 位1的个数

class Solution:
    def hammingWeight(self, n: int) -> int:
        res = 0
        while n != 0:
            res += 1
            n = n & n - 1
        return res

异或运算应用

异或运算的性质:一个数和它本身做异或运算结果为0,即a^a=0;一个数和0做异或运算的结果为它本身,即a^0=a

LeetCode136题 只出现一次的数字

成对的数字异或就会变成0,落单的数字和0做异或还是它本身,所以最后异或的结果就是只出现一次的元素

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for i in nums:
            res ^= i
        return res

3.随机算法

LeetCode382题 链表的随机节点

如何从包含未知大小的数据流中随机选取k个数据,并且要保证每个数据被抽取到的概率相等。当k=1时,即此题的情况

蓄水池抽样算法:每次只保留一个数,当遇到第 i 个数时,以 1/i的概率保留它,(i-1)/i的概率保留原来的数。举例说明: 

  • 遇到1,概率为1,保留第一个数
  • 遇到2,概率为1/2,这个时候,1和2各1/2的概率被保留
  • 遇到3,3被保留的概率为1/3,(之前剩下的数假设1被保留),2/3的概率 1 被保留,(此时1被保留的总概率为 2/3 * 1/2 = 1/3)
  • 遇到4,4被保留的概率为1/4,(之前剩下的数假设1被保留),3/4的概率 1 被保留,(此时1被保留的总概率为 3/4 * 2/3 * 1/2 = 1/4)
  • 以此类推,每个数被保留的概率都是1/N
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:

    def __init__(self, head: Optional[ListNode]):
        self.head = head

    def getRandom(self) -> int:
        count = 0
        reserve = 0
        cur = self.head
        while cur:
            count += 1
            rand = random.randint(1, count)
            if rand == count:
                reserve = cur.val
            cur = cur.next
        return reserve


# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()

 LeetCode384题 打乱数组

洗牌算法,对于下标x而言,我们从[x,n−1]中随机出一个位置与x进行值交换,当所有位置都进行这样的处理后,我们便得到了一个公平的洗牌方案

lass Solution:

    def __init__(self, nums: List[int]):
        self.nums = nums

    def reset(self) -> List[int]:
        return self.nums

    def shuffle(self) -> List[int]:
        nums = self.nums[:]
        for i in range(len(nums)):
            import random
            rand_num = random.randint(i, len(nums) - 1)
            nums[i], nums[rand_num] = nums[rand_num], nums[i]
        return nums

# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.reset()
# param_2 = obj.shuffle()

4.阶乘相关

LeetCode172题 阶乘后的零

阶乘后的零的个数=乘法因子中5的个数,时间复杂度为log n

class Solution:
    def trailingZeroes(self, n: int) -> int:
        p = 1
        res = 0
        while p * 5 <= n:
            p *= 5
            res += n // p
        return res

LeetCode793题 阶乘函数后K个零

假定n是阶乘数,k是阶乘后尾部零个数

上一题是给定n求k,这题是给定k,问k的合法性,如果k合法,那么符合k的n有5种;如果k不合法,那么符合k的n有0种

那么可以结合上题的代码,可以用二分法搜索k是否合法,搜索的n范围是[0, 5 * k],合法返回5,不合法返回0

class Solution:
    def preimageSizeFZF(self, k: int) -> int:
        left = 0
        right = 5 * k
        while left <= right:
            mid = (left + right) // 2
            mid_res = self.trailingZeroes(mid)
            if mid_res == k:
                return 5
            elif mid_res < k:
                left = mid + 1
            else:
                right = mid - 1
        return 0

    def trailingZeroes(self, n: int) -> int:
        p = 1
        res = 0
        while p * 5 <= n:
            p *= 5
            res += n // p
        return res

5.高效寻找素数

LeetCode204题 计数质数

用最小的素数,去进行扩散,排除掉非素数

class Solution:
    def countPrimes(self, n: int) -> int:
        if n <= 2:
            return 0
        is_primes = [False if i % 2 == 0 and i > 2 else True for i in range(n)]
        for i in range(2, int(n**0.5) + 1):
            if is_primes[i]:
                for j in range(i * i, n, i):
                    is_primes[j] = False
        res = 1
        for i in range(3, n, 2):
            if is_primes[i]:
                res += 1
        return res

6.高效进行模幂运算

LeetCode372题 超级次方

乘法在取模的意义下满足分配律:

(a*b) \mod m = ((a \mod m) * (b \mod m)) \mod m

可以推导出:

a^{p*q} \mod m = ({a^p \mod m}) ^ q \mod m

然后pow(x,n)的运算可以退化为pow(x,n/2) * pow(x,n/2)...,时间复杂度为logn

class Solution:
    def superPow(self, a: int, b: List[int]) -> int:
        res = 1
        for i in range(len(b) - 1, -1, -1):
            res *= self.pos_pow_mod(a, b[i])
            a = self.pos_pow_mod(a, 10)
        return res % 1337

    def pos_pow_mod(self, x, n):
        if n == 1:
            return x % 1337
        if n == 0:
            return 1
        if n % 2 == 0:
            tmp = self.pos_pow_mod(x, n / 2)
            return (tmp * tmp) % 1337
        else:
            tmp = self.pos_pow_mod(x, (n - 1) / 2)
            return ((x % 1337) * tmp * tmp) % 1337

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值