3无重复字符的最长子串

链接

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        max_l = 0
        l = 0
        temp = ''
        for i in s:
            if i in temp:
                temp = temp[temp.index(i) + 1:]
                l = len(temp)
            l = l + 1
            max_l = max(l, max_l)
            temp = temp + i
        return max_l

7整数反转

链接

class Solution:
    def reverse(self, x: int) -> int:
        if x == 0:
            return 0
        x = str(x)
        prefix = ''
        if x[0] == '-':
            prefix = '-'
            x = x[1:]
        temp = ''
        for i in range(len(x) - 1, -1, -1):
            temp = temp + x[i]
        temp = temp.lstrip('0')
        res = int(prefix + temp)
        if res >= 2147483647 or res <= -2147483648:
            return 0
        return res

8字符串转换整数(atoi)

链接

class Solution:
    def myAtoi(self, s: str) -> int:
        s = s.lstrip()
        if len(s) == 0:
            return 0
        temp = ''
        nums = '0123456789'

        if s[0] in ['+', '-']:
            temp = s[0]
            s = s[1:]

        for i in range(len(s)):
            if s[i] not in nums:
                break
            temp = temp + s[i]

        if temp == '' or temp == '-' or temp == '+':
            return 0
        temp = int(temp)
        if temp <= -2147483648:
            temp = -2147483648
        elif temp >= 2147483647:
            temp = 2147483647
        return temp

11盛最多水的容器

链接

class Solution:
    def maxArea(self, height: list) -> int:
        if len(height) < 2:
            return 0
        left = 0
        right = len(height) - 1
        res = 0
        while True:
            w = right - left
            h = min(height[left], height[right])
            res = max(res, w * h)
            if height[left] > height[right]:
                right = right - 1
            else:
                left = left + 1
            if left == right:
                break
        return res

14最长公共前缀

链接

class Solution:
    def longestCommonPrefix(self, strs: list[str]) -> str:
        m1 = max(strs)
        m2 = min(strs)
        if m1 == m2:
            return m2
        l1 = len(m1)
        l2 = len(m2)
        l = l1 if l1 < l2 else l2
        for i in range(l):
            if m1[i] != m2[i]:
                return m2[0:i]
        return m2

35搜索插入位置

链接

class Solution:
    def searchInsert(self, nums: list[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        while left < right:
            if target > nums[right]:
                return right + 1
            elif target < nums[left]:
                return left
            mid = (right + left) // 2
            if nums[mid] > target:
                right = mid
            elif nums[mid] < target:
                left = mid + 1
            else:
                return mid
        return left if nums[left] >= target else left + 1

45跳跃游戏II

链接

class Solution:
    def jump(self, nums: list[int]) -> int:
        endPoint = len(nums) - 1
        position = 0
        step = 0

        while position < endPoint:
            temp = 0
            step = step + 1
            r = range(position + 1, nums[position] + position + 1)
            for i in r:
                if i >= endPoint:
                    return step
                if nums[i] + i >= temp:
                    temp = nums[i] + i
                    position = i

        return step

48旋转图像

链接

class Solution:
    def rotate(self, matrix: list[list[int]]) -> None:
        l = len(matrix)
        # 每次交换4个位置
        # 当长度为偶数时,交换l^2/4次,等于(l/2)*(l/2)次,l//2==(l+1)//2(偶数和偶数+1的地板除结果一样)
        # 当长度为奇数时,中间点不交换,交换(l^2-1)/4次,等于((l+1)/2) * ((l-1)/2),(l+1)//2==(l+1)/2、l//2==(l-1)/2
        for i in range(l // 2):
            for j in range((l + 1) // 2):
                temp = matrix[i][j]
                matrix[i][j] = matrix[l - 1 - j][i]
                matrix[l - 1 - j][i] = matrix[l - 1 - i][l - 1 - j]
                matrix[l - 1 - i][l - 1 - j] = matrix[j][l - 1 - i]
                matrix[j][l - 1 - i] = temp

102二叉树的层序遍历

链接

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:

    def levelOrder(self, root: TreeNode) -> list[list[int]]:
        res = dict()
        if root is None or root.val is None:
            return []

        res[0] = [root.val]

        self._levelOrder(root, res, 1)

        return [v for k, v in res.items()]

    def _levelOrder(self, root: TreeNode, res, level):
        if root.left is not None and root.right is not None:
            if level not in res:
                res[level] = [root.left.val, root.right.val]
            else:
                res[level].append(root.left.val)
                res[level].append(root.right.val)
            self._levelOrder(root.left, res, level + 1)
            self._levelOrder(root.right, res, level + 1)
        elif root.left is not None:
            if level not in res:
                res[level] = [root.left.val]
            else:
                res[level].append(root.left.val)
            self._levelOrder(root.left, res, level + 1)
        elif root.right is not None:
            if level not in res:
                res[level] = [root.right.val]
            else:
                res[level].append(root.right.val)
            self._levelOrder(root.right, res, level + 1)

136只出现一次的数字

链接

你的算法应该具有线性时间复杂度。你可以不使用额外空间来实现吗?

class Solution:
    def singleNumber(self, nums: list[int]) -> int:
        res = 0
        for i in nums:
            # n 异或 n == 0
            res = res ^ i
        return res

137只出现一次的数字II

链接

class Solution:
    def singleNumber(self, nums: list[int]) -> int:
        d = dict()
        c = 3
        for i in nums:
            if i not in d:
                d[i] = 1
            else:
                if d[i] + 1 >= c:
                    d.pop(i)
                else:
                    d[i] = d[i] + 1
        return d.popitem()[0]

148排序链表

链接

2591将钱分给最多的儿童

链接

class Solution:
    def distMoney(self, money, children):
        # 钱少于人数,必有人分不到钱,返回-1
        if money < children:
            return -1
        # 钱=8*人数,全部分到8元
        elif 8 * children == money:
            return children
        # 钱>8*人数,n-1人分到8元,剩下一人分到8+元
        elif 8 * children < money:
            return children - 1
        # 钱=(n-1)*8+4,n-2人分到8元,剩下2人分掉12元(不能4+8)
        elif (children - 1) * 8 + 4 == money:
            return children - 2
        else:
            # 设x人分到8元,但是要保证剩下的人都能分到钱:money-8x >= children-x
            # 调整后得:x <= (money-children)/7
            return (money - children) // 7

460LFU缓存

链接

from collections import defaultdict
from typing import Optional


class Node:
    __slots__ = ('prev', 'next', 'key', 'value', 'freq')

    def __init__(self, key=0, val=0):
        self.key = key
        self.value = val
        self.freq = 1


class LFUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.data_map = dict()

        # 哨兵节点
        def new_list() -> Node:
            dummy = Node()
            dummy.prev = dummy
            dummy.next = dummy
            return dummy

        self.freq_map = defaultdict(new_list)

    def get(self, key: int) -> int:
        node = self._get_node(key)
        return node.value if node else -1

    def put(self, key: int, value: int) -> None:
        node = self._get_node(key)
        # 已存在
        if node:
            node.value = value
        else:
            # 超过字典长度限制
            if len(self.data_map) >= self.capacity:
                dummy = self.freq_map[self.min_freq]
                # 找到最低频率最后加入的节点并删除它(哨兵节点的上一个,其实就是最后一个)
                back_node = dummy.prev
                del self.data_map[back_node.key]
                self._remove(back_node)
                # 清空一下,节省一点点内存
                if dummy.prev == dummy:
                    del self.freq_map[self.min_freq]
            # 把key放入字典
            self.data_map[key] = node = Node(key, value)
            # 设置key对应的使用频率
            self._push_front(self.freq_map[1], node)
            self.min_freq = 1
        return None

    def _get_node(self, key: int) -> Optional[Node]:
        # key不存在,直接返回
        if key not in self.data_map:
            return None
        node = self.data_map[key]
        # 把节点从当前使用频率上面移除
        self._remove(node)
        dummy = self.freq_map[node.freq]
        # 如果当前使用频率没有节点了,清空列表
        if dummy.prev == dummy:
            del self.freq_map[node.freq]
            # 将最小频率往上移动一格
            if self.min_freq == node.freq:
                self.min_freq += 1
        # 把节点往更高的使用频率上移动
        node.freq += 1
        # 放在更高频率的最前面(最晚移除)
        self._push_front(self.freq_map[node.freq], node)
        return node

    # 删除一个节点
    def _remove(self, x: Node) -> None:
        x.prev.next = x.next
        x.next.prev = x.prev

    # 在链表头添加一个节点
    def _push_front(self, dummy: Node, x: Node) -> None:
        x.prev = dummy
        x.next = dummy.next
        x.prev.next = x
        x.next.prev = x

2582递枕头

链接

class Solution:
    # 到达端点需要n-1时间
    def passThePillow(self, n: int, time: int) -> int:
        l_r = (time // (n - 1)) % 2
        # 奇数,右往左
        if l_r == 1:
            return n - time % (n - 1)
        else:   # 偶数,左往右
            return 1 + time % (n - 1)

1333餐厅过滤器

链接

利用bisect

import bisect

class Solution:
    def filterRestaurants(self, restaurants, veganFriendly: int, maxPrice: int, maxDistance: int):
        res = []
        for id, rating, veganFriendly1, price, distance in restaurants:
            if ((
                        veganFriendly == 1 and veganFriendly1 == 1) or veganFriendly == 0) and price <= maxPrice and distance <= maxDistance:
                # 1.题目已知id最大为10000 2.bisect.insort默认升序,转成负数
                bisect.insort(res, rating * -100000 - id)
        return [x % -100000 * -1 for x in res]

先过滤,再排序,然后剔除rating

class Solution:
    def filterRestaurants(self, restaurants, veganFriendly: int, maxPrice: int, maxDistance: int):
        res = []
        for id, rating, veganFriendly1, price, distance in restaurants:
            if ((
                        veganFriendly == 1 and veganFriendly1 == 1) or veganFriendly == 0) and price <= maxPrice and distance <= maxDistance:
                res.append([id, rating])
        res.sort(key=lambda x: [x[1], x[0]], reverse=True)
        return [x[0] for x in res]

2251花期内花的数目

链接

from typing import List
import bisect


# 第n天去看,花的数量= (开始时间<=n的数量) - (结束时间<n的数量)
class Solution:
    def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]:
        starts = sorted(s for s, _ in flowers)
        ends = sorted(e for _, e in flowers)
        # 因为排序了,索引可以用来表示数量
        # 最右边的插入位索引(开始时间<=n的数量) - 最左边的插入位索引 (结束时间<n的数量)
        return [bisect.bisect_right(starts, p) - bisect.bisect_left(ends, p) for p in people]

差分数组

链接

对于数组a,定义其差分数组(difference array)为

d[i]={a[0]a=0a[i]a[i1]a>=1d[i] = \begin{cases} a[0] \quad a=0\\ a[i] - a[i - 1] \quad a>=1 \end{cases}

性质1:从左到右累加d中的元素,可以得到数组a

性质2:如下两个操作是等价的

  • 区间操作:把a的子数组a[i],a[i+1],...,a[j]都加上x
  • 单点操作:把d[i]增加x;如果j+1小于a的长度,把d[j+1]减少x
from typing import List

# 你有一个长为 n 的数组 a,一开始所有元素均为 0。
# 给定一些区间操作,其中 queries[i] = [left, right, x],
# 你需要把子数组 a[left], a[left+1], ... a[right] 都加上 x。
# 返回所有操作执行完后的数组 a。
def solve(n: int, queries: List[List[int]]) -> List[int]:
    diff = [0] * n  # 差分数组
    for left, right, x in queries:
        diff[left] += x
        if right + 1 < n:
            diff[right + 1] -= x
    for i in range(1, n):
        diff[i] += diff[i - 1]  # 直接在差分数组上复原数组 a
    return diff

queries = [[1, 3, 2], [6, 6, 6]]
print(solve(7, queries))

605种花问题

链接

题解1

from typing import List

class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        l = len(flowerbed)
        c = 0
        if l == 1:
            c = 1 if flowerbed[0] == 0 else 0
            return True if c >= n else False
        # 可以在flowerbed左右各拼一个[0]来简化判断逻辑,但是会有额外的内存消耗
        # 题解2的那种判断方式更好
        for i in range(l):
            if i == 0:
                if flowerbed[i] == 0 and flowerbed[i + 1] == 0:
                    flowerbed[i] = 1
                    c += 1
            elif i == l - 1:
                if flowerbed[i] == 0 and flowerbed[i - 1] == 0:
                    flowerbed[i] = 1
                    c += 1
            else:
                if flowerbed[i] == 0 and flowerbed[i + 1] == 0 and flowerbed[i - 1] == 0:
                    flowerbed[i] = 1
                    c += 1
            if c >= n:
                return True

        return False

题解2

from typing import List

class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        # n == 0的情况在头部判断,这样循环中的return True的判断可以提到if内
        if n == 0:
            return True
        i = 0
        l = len(flowerbed)
        while i < l:
            # i直接+2,不要赋值1,稍微省一点损耗
            if (i == 0 or flowerbed[i - 1] == 0) and flowerbed[i] == 0 and (i == l - 1 or flowerbed[i + 1] == 0):
                if n <= 1:
                    return True
                n -= 1
                i += 2
            else:
                i += 1
        return False

2136全部开花的最早一天

链接

from typing import List

class Solution:
    def earliestFullBloom(self, plantTime: List[int], growTime: List[int]) -> int:
        ans = days = 0
        # 根据生长天数降序,尽最大可能在生长的日子同时播种
        for p, g in sorted(zip(plantTime, growTime), key=lambda z: -z[1]):
            # 累加播种天数
            days += p
            # 更新全部开花的天数(至少要保证所有花开,所以取max)
            ans = max(ans, days + g)
        return ans

121买卖股票的最佳时机

链接

只能买卖一次,遍历prices,prices[i] - min_price即可

from typing import List

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        res = 0
        min_price = prices[0]
        for i in prices:
            res = max(i-min_price, res)
            min_price = min(i, min_price)
        return res

122买卖股票的最佳时机II

链接

from typing import List

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        res = 0
        for i in range(len(prices)-1):
            if prices[i+1] > prices[i]:
                res += prices[i+1]-prices[i]
        return res

123买卖股票的最佳时机III

链接

from typing import List
import math

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 没有开盘前就持有了股票,是非法的,初始化为负无穷大
        buy1 = buy2 = -math.inf
        # 没有开盘前没有持有股票,初始化为0
        sell1 = sell2 = 0
        for p in prices:
            # 第2次卖出和第2次买入+当前利润的最大值
            sell2 = max(sell2, buy2+p)
            # 第2次买入和第一次卖出-当前买入后的最大值
            buy2 = max(buy2, sell1 - p)
            # 第1次卖出和第1次买入+当前利润的最大值
            sell1 = max(sell1, buy1+p)
            # 第1次买入和第0次卖出-当前买入后的最大值
            buy1 = max(buy1, -p)
        return sell2

188买卖股票的最佳时机IV

链接

from typing import List
import math

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        l = len(prices)
        # 初始化dp数组,默认全为负无穷,防止数组越界,全部+1
        # 因为最多k次(包含k),所以本来就是range(k+1),所以k要再+1
        # 第三维:0表示未持有股票,1表示持有股票
        mp = [[[-math.inf]*2 for _ in range(k+2)] for _ in range(l+1)]
        # 初始化第0天的未持股的情况全为0,j>=1
        for j in range(1, k+2):
            mp[0][j][0] = 0
        for i, p in enumerate(prices):
            for j in range(1, k+2):
                mp[i+1][j][0] = max(mp[i][j][0], mp[i][j][1] + p)
                mp[i+1][j][1] = max(mp[i][j][1], mp[i][j-1][0]-p)
        return mp[l][k+1][0]

状态压缩,优化空间复杂度

from typing import List
import math

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        mp = [[-math.inf]*2 for _ in range(k+2)]
        for j in range(1, k+2):
            mp[j][0] = 0
        for p in prices:
            for j in range(k+1, 0, -1):
                mp[j][1] = max(mp[j][1], mp[j-1][0]-p)
                mp[j][0] = max(mp[j][0], mp[j][1] + p)
        return mp[k+1][0]

309买卖股票的最佳时机含冷冻期

链接

因为含冷冻期,所以当i>0买入时,需要往前再推一轮

from typing import List
import math

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        l = len(prices)
        mp = [[-math.inf]*2 for _ in range(l+1)]
        mp[0][0] = 0
        for i, p in enumerate(prices):
            mp[i+1][0] = max(mp[i][0], mp[i][1] + p)
            if i > 0:
                mp[i+1][1] = max(mp[i][1], mp[i-1][0] - p)
            else:
                mp[i+1][1] = max(mp[i][1], mp[i][0] - p)
        return mp[l][0]

714买卖股票的最佳时机含手续费

链接

因为含有手续费,所以还是要用动态规划,不能像122那样用贪心算法了

from typing import List
from math import inf

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        f = [[-inf]*2 for _ in range(n+1)]
        f[0][0] = 0
        for i, p in enumerate(prices):
            f[i+1][0] = max(f[i][0], f[i][1]+p-fee)
            f[i+1][1] = max(f[i][1], f[i][0]-p)
        return f[n][0]

空间优化

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        f0, f1 = 0, -inf
        for p in prices:
            f0, f1 = max(f0, f1 + p - fee), max(f1, f0 - p)
        return f0

901股票价格跨度

链接

from math import inf


class StockSpanner:
    def __init__(self):
        # 初始化值,简化判断
        self.spanner = [(-1, inf)]
        self.current = -1

    def next(self, price: int) -> int:
        # 只要比当前价格低的,全部pop掉
        while price >= self.spanner[-1][1]:
            self.spanner.pop()
        self.current += 1
        self.spanner.append((self.current, price))
        # 因为比当前价格低的全部pop掉了,所以只要当前-上一个即可
        return self.current - self.spanner[-2][0]

2034股票价格波动

链接

from bisect import insort, bisect_right

class StockPrice:

    def __init__(self):
        self.t = {}
        self.p = []
        self.cur = [-1, -1]

    def update(self, timestamp: int, price: int) -> None:
        # 维护一个有序数组,如果时间已存在,剔除掉
        if timestamp in self.t:
            del self.p[bisect_right(self.p, self.t[timestamp]) - 1]

        # 更新时间对应的价格
        self.t[timestamp] = price
        insort(self.p, price)

        # 更新最新价
        if timestamp >= self.cur[0]:
            self.cur = [timestamp, price]
        return None

    def current(self) -> int:
        return self.cur[1]

    def maximum(self) -> int:
        return self.p[-1]

    def minimum(self) -> int:
        return self.p[0]

使用SortedList,删除元素的时候比bisect要快些

from sortedcontainers import SortedList

class StockPrice:

    def __init__(self):
        self.t = {}
        self.p = SortedList()
        self.max_time = -1

    def update(self, timestamp: int, price: int) -> None:
        if timestamp in self.t:
            self.p.remove(self.t[timestamp])

        self.t[timestamp] = price
        self.p.add(price)

        if timestamp > self.max_time:
            self.max_time = timestamp
        return None

    def current(self) -> int:
        return self.t[self.max_time]

    def maximum(self) -> int:
        return self.p[-1]

    def minimum(self) -> int:
        return self.p[0]

2578最小和分割

链接

class Solution:
    def splitNum(self, num: int) -> int:
        a = b = ''
        num = sorted(str(num))
        for i, v in enumerate(num):
            if i % 2 == 1:
                a += v
            else:
                b += v
        return int(a) + int(b)

更优雅的写法

class Solution:
    def splitNum(self, num: int) -> int:
        s = sorted(str(num))
        return int(''.join(s[::2])) + int(''.join(s[1::2]))

2731移动机器人

链接

from typing import List

class Solution:
    def sumDistance(self, nums: List[int], s: str, d: int) -> int:
        # 题目描述机器人无差别,所以可以无视碰撞,直接穿透过去就行了
        for i, c in enumerate(s):
            nums[i] += d if c == 'R' else -d
        # 题目要的答案是距离的累加和,所以可以无视顺序
        # 排序后更便于累加所有距离
        nums.sort()
        ans = s = 0
        # 把nums[i]提取出来,nums[0...i-1]累加
        for i, x in enumerate(nums):
            ans += i * x - s
            s += x
        return ans % (10 ** 9 + 7)

2512奖励最顶尖的K名学生

链接

from typing import List

class Solution:
    def topStudents(self, positive_feedback: List[str], negative_feedback: List[str], report: List[str],
                    student_id: List[int], k: int) -> List[int]:
        # 反馈列表转成字典,便于匹配评价
        positive_feedback = dict(zip(positive_feedback, [1] * len(positive_feedback)))
        negative_feedback = dict(zip(negative_feedback, [1] * len(negative_feedback)))
        res = []
        for i in range(len(student_id)):
            score = 0
            # 根据题目描述,直接用" "拆分
            for rp in report[i].split(" "):
                if rp in positive_feedback:
                    score += 3
                if rp in negative_feedback:
                    score -= 1
            res.append((student_id[i], score))
        # 排序分数、ID
        res.sort(key=lambda x: [x[1], -x[0]], reverse=True)
        return [x[0] for x in res[:k]]

更优雅的实现方式

from typing import List
from collections import defaultdict


class Solution:
    def topStudents(self, positive_feedback: List[str], negative_feedback: List[str], report: List[str],
                    student_id: List[int], k: int) -> List[int]:
        # 正负合并成一个字典
        score = defaultdict(int)
        for w in positive_feedback: score[w] = 3
        for w in negative_feedback: score[w] = -1
        a = sorted((-sum(score[w] for w in r.split()), i) for r, i in zip(report, student_id))
        return [i for _, i in a[:k]]

2562找出数组的串联值

链接

from typing import List

class Solution:
    def findTheArrayConcVal(self, nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1
        res = 0

        while left <= right:
            if left == right:
                res += nums[left]
                break
            res += int(str(nums[left]) + str(nums[right]))
            left += 1
            right -= 1
        return res

不进行字符串转换,通过数学计算方式累加

from typing import List

class Solution:
    def findTheArrayConcVal(self, nums: List[int]) -> int:
        ans = 0
        i = 0
        j = len(nums) - 1
        while i < j:
            x = nums[i]
            y = nums[j]
            # 通过y不断除10,来知道x需要乘以10的几次方
            while y:
                x *= 10
                y //= 10
            ans += x + nums[j]
            i += 1
            j -= 1
        # 相等的情况移到循环外面
        if i == j:
            ans += nums[i]
        return ans

1488避免洪水泛滥

链接

from typing import List
from collections import defaultdict
from bisect import bisect_right


class Solution:
    def avoidFlood(self, rains: List[int]) -> List[int]:
        res = [-1] * len(rains)

        def return_value():
            return -1

        temp = defaultdict(return_value)
        zero = []
        for i, v in enumerate(rains):
            if v != 0:
                # 当前湖泊有水并且可以抽水(0的索引大于当前湖泊之前下雨的索引)
                if len(zero) > 0 and temp[v] > -1 and zero[-1] > temp[v]:
                    # 找到最小满足的0,使用它抽干湖泊
                    zero_index = bisect_right(zero, temp[v])
                    res[zero[zero_index]] = v
                    zero.pop(zero_index)
                    temp[v] = -1
                # 连续2次下雨在某个湖泊,中间没有满足的0进行抽干,返回[]
                if temp[v] > -1:
                    return []
                temp[v] = i
            else:
                zero.append(i)
        # 把剩余的0全填为1,不然提交会失败
        for i in zero:
            res[i] = 1
        return res

更优雅的实现方式

from typing import List
from sortedcontainers import SortedList

class Solution:
    def avoidFlood(self, rains: List[int]) -> List[int]:
        n = len(rains)
        ans = [-1] * n
        sunny = SortedList()
        rainy = {}
        for i, v in enumerate(rains):
            if v:
                if v in rainy:
                    idx = sunny.bisect_right(rainy[v])
                    if idx == len(sunny):
                        return []
                    ans[sunny[idx]] = v
                    sunny.discard(sunny[idx])
                rainy[v] = i
            else:
                sunny.add(i)
                ans[i] = 1
        return ans

136只出现一次的数字

链接

根据约束条件,以及空间复杂度要求,本题可以用异或计算

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

137只出现一次的数字II

链接

from typing import List


class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        '''
            异或本质上就是先相加再%2,也可以叫模2加法(把一个bit不断地异或1,相当于在0和1之间不断转换)
            nums = [6,6,3]
                110
                110
                011 +
                -----
                231 %2
                -----
                011 -> res = 3

            同理:本题就是先相加再%3,也可以叫模3加法(相当于在0,1,2之间不断转换)
            nums = [6,6,6,3]
                110
                110
                110
                011 +
                -----
                341 %3
                -----
                011 -> res = 3
            由于0,1,2需要2个比特位才能表示,设这2个比特分别为:a和b
            a=0,b=0表示0
            a=0,b=1表示1
            a=1,b=1表示2
            那么转换规则是:(0,0)->(0,1)->(1,0)->(0,0)...

            # (0,0)、(0,1)
            if a == 0:
                if x == 0:  # 输入0,状态不变
                    b = b
                else:       # 输入1,状态变化
                    b = ~b
            else:   # (1,0)
                b = 0       # 当a等于1时,b永远等于0

            引入异或运算,PS: n ^ 0 = n, n ^ 1 = ~n

            if a == 0:
                b = b ^ x    # x等于0保持不变,x等于1取反
            else:
                b = 0

            引入与运算,PS: n & 0 = 0, n & 1 = n

            b =  ~a & b ^ x    # a取反后等于1,走异或逻辑;a取反后等于0和任意数与预算结果还是为0

            b计算好后,基于b再计算a(此时b等于1,状态为:(0, 1))

            a = ~b & a ^ x    # b取反后等于1,走异或逻辑;b取反后等于0和任意数与预算结果还是为0
        '''
        a = b = 0
        for x in nums:
            b = (b ^ x) & ~a
            a = (a ^ x) & ~b
        return b

260只出现一次的数字III

链接

from typing import List

'''
    nums = [1,2,1,3,2,5]

    先进行一次全员异或,那么相同的都会消除,结果为:3^5=6

    因为这2个数都只出现一次,即:3 != 5

    所以结果值6,必定有一位为1,3和5在这个比特位上,必定有一个为1,另一个为0

    110  ->  6
    011  ->  3
    101  ->  5

    我们可以直接取最后一位1即可,6 & -6

    再次遍历nums,把这个比特位上值为0的放一组,值为1的放一组,进行分组异或

    那么3和5会被放到2组中,其他的相同的值要么和3一组,要么和5一组,不过最终都会被异或消除

    返回每组的唯一值,即:3和5
'''
class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor_all = nums[0]
        for i in range(1, len(nums)):
            xor_all ^= nums[i]
        low_bit = xor_all & -xor_all
        res = [0, 0]
        for x in nums:
            '''
                if x & low_bit:
                    res[0] = res[0] ^ x
                else:
                    res[1] = res[1] ^ x
            '''
            res[(x & low_bit) != 0] ^= x
        return res

2652倍数求和

链接

class Solution:
    def sumOfMultiples(self, n: int) -> int:
        def s(m:int) -> int:
            # 等差数列求和公式
            # SumAk = (a_1 + a_k) * k / 2
            # k = n // m(有k项), a_1 = m(首项), a_k = mk(末项=首项*k)
            return n // m * (n // m + 1) // 2 * m

        # 容斥原理:公共的倍数只保留1份
        return s(3) + s(5) + s(7) - s(15) - s(21) - s(35) + s(105)

2530执行K次操作后的最大分数

链接

排序实现,较慢

from sortedcontainers import SortedList

class Solution:
    def maxKelements(self, nums: List[int], k: int) -> int:
        nums = SortedList(nums)
        res = 0
        for _ in range(k):
            m = nums.pop()
            res += m
            nums.add(ceil(m / 3))
        return res

堆实现,更优

from typing import List
from heapq import heapify, heapreplace


class Solution:
    def maxKelements(self, nums: List[int], k: int) -> int:
        for i in range(len(nums)):
            # 小顶堆,所以转成负数
            nums[i] = -nums[i]
        # 原地堆化
        heapify(nums)

        ans = 0
        for _ in range(k):
            # 取出最小值,并替换
            # 因为转成了负值,所以:累加->累减,向上取整->向下取整
            ans -= heapreplace(nums, nums[0] // 3)
        return ans

1726同积元祖

链接

class Solution:
    def tupleSameProduct(self, nums: List[int]) -> int:
        d = defaultdict(int)
        res = 0
        n = len(nums)
        for i in range(n):
            for j in range(i + 1, n):
                p = nums[i] * nums[j]
                d[p] += 1
                if d[p] > 1:
                    res += (d[p] - 1) * 8
        return res

2316统计无向图中无法互相到达点对数

链接



1402做菜顺序

链接

```

# 1155掷骰子等于目标和的方法数

[链接](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/)

```python

2698求一个整数的惩罚数

链接

python

results matching ""

    No results matching ""