1:水分子生成
多线程
题目链接:1117. H2O 生成 - 力扣(LeetCode)
在这个代码中,我们可以定义一个H2O
类,它包含两个信号量h_semaphore
和o_semaphore
以及一个屏障barrier
。氢线程会尝试获取两个氢信号量,而氧线程会尝试获取一个氧信号量。屏障确保了每次只有三个线程(两个氢线程和一个氧线程)可以继续执行并释放它们对应的字符。当一个水分子被成功创建后,信号量会被释放,允许更多的线程继续执行。
import threading
class H2O:
def __init__(self):
self.h_semaphore = threading.Semaphore(2) # 控制两个氢线程
self.o_semaphore = threading.Semaphore(0) # 控制一个氧线程
self.barrier = threading.Barrier(3) # 当三个线程到达屏障时释放
def hydrogen(self, releaseHydrogen: 'Callable[[], None]') -> None:
self.h_semaphore.acquire() # 获取氢信号量
self.barrier.wait() # 等待屏障
releaseHydrogen()
self.h_semaphore.release() # 释放氢信号量
def oxygen(self, releaseOxygen: 'Callable[[], None]') -> None:
self.o_semaphore.acquire() # 获取氧信号量
self.barrier.wait() # 等待屏障
releaseOxygen()
self.o_semaphore.release() # 释放氧信号量
def releaseHydrogen():
print('H', end='')
def releaseOxygen():
print('O', end='')
# 示例使用
def main():
water = input()
h2o = H2O()
threads = []
for char in water:
if char == 'H':
t = threading.Thread(target=h2o.hydrogen, args=(releaseHydrogen,))
elif char == 'O':
t = threading.Thread(target=h2o.oxygen, args=(releaseOxygen,))
threads.append(t)
t.start()
for t in threads:
t.join()
if __name__ == "__main__":
main()
以下是上述代码的详细解释:
类定义 H2O
class H2O:
def __init__(self):
self.h_semaphore = threading.Semaphore(2) # 控制两个氢线程
self.o_semaphore = threading.Semaphore(0) # 控制一个氧线程
self.barrier = threading.Barrier(3) # 当三个线程到达屏障时释放
这个类是代码的核心,它定义了如何同步氢和氧线程来形成水分子。
__init__
方法是类的构造函数,它初始化三个对象属性:
h_semaphore
是一个信号量,初始值为2,表示最多允许两个氢线程同时执行。o_semaphore
是一个信号量,初始值为0,表示没有氧线程可以立即执行。barrier
是一个屏障,它要求3个线程到达屏障后才能继续执行。
氢线程方法 hydrogen
def hydrogen(self, releaseHydrogen: 'Callable[[], None]') -> None:
self.h_semaphore.acquire() # 获取氢信号量
self.barrier.wait() # 等待屏障
releaseHydrogen() # 释放氢
self.h_semaphore.release() # 释放氢信号量
这个方法用于控制氢线程的行为:
- 首先尝试获取氢信号量。如果当前已经有两个氢线程在运行,那么新的氢线程将等待。
- 接着,线程等待屏障。屏障确保了只有两个氢线程和一个氧线程同时到达时,它们才能继续执行。
- 然后,调用
releaseHydrogen
函数来打印氢原子。- 最后,释放氢信号量,允许其他氢线程继续。
氧线程方法 oxygen
def oxygen(self, releaseOxygen: 'Callable[[], None]') -> None:
self.o_semaphore.acquire() # 获取氧信号量
self.barrier.wait() # 等待屏障
releaseOxygen() # 释放氧
self.o_semaphore.release() # 释放氧信号量
这个方法用于控制氧线程的行为:
- 尝试获取氧信号量。由于初始值为0,氧线程将等待直到氢线程释放信号量。
- 线程等待屏障,与其他两个氢线程一起等待。
- 调用
releaseOxygen
函数来打印氧原子。- 最后,释放氧信号量。
辅助函数 releaseHydrogen
和 releaseOxygen
def releaseHydrogen():
print('H', end='')
def releaseOxygen():
print('O', end='')
这两个函数分别用于打印氢和氧原子。
主函数 main
def main():
water = input()
h2o = H2O()
threads = []
for char in water:
if char == 'H':
t = threading.Thread(target=h2o.hydrogen, args=(releaseHydrogen,))
elif char == 'O':
t = threading.Thread(target=h2o.oxygen, args=(releaseOxygen,))
threads.append(t)
t.start()
for t in threads:
t.join()
这是程序的入口点:
- 从用户那里获取输入字符串
water
,代表需要形成的水分子序列。- 创建
H2O
类的实例。- 对于输入字符串中的每个字符,创建一个对应的线程(氢线程或氧线程)并启动它。
- 将所有线程添加到
threads
列表中。- 使用
join
方法等待所有线程完成。
运行主程序
if __name__ == "__main__":
main()
当这个脚本作为主程序运行时,它将调用main
函数。这是Python中的一个常见习惯用法,确保当脚本被导入时,不会立即执行main
函数。
2:每日温度
单调栈
可以通过维护一个单调递减的栈来找到每个温度之后第一个更高的温度。当遍历到一个新的温度时,如果它比栈顶索引对应的温度高,那么我们就知道栈顶索引对应的天数之后会有一个更高的温度。通过这种方式,我们可以高效地解决问题,而不需要对每个温度进行多次比较。
def dailyTemperatures(temperatures):
# 初始化结果数组,全部填充为0
result = [0] * len(temperatures)
# 初始化一个单调栈,用于存储温度的索引
stack = []
for i, temp in enumerate(temperatures):
# 当栈不为空,且当前温度大于栈顶索引对应的温度时
while stack and temp > temperatures[stack[-1]]:
# 弹出栈顶元素,即之前较低温度的索引
idx = stack.pop()
# 计算当前索引与之前较低温度索引的差值,即等待天数
result[idx] = i - idx
# 当前温度的索引入栈
stack.append(i)
return result
3:黑名单中的随机数
题目链接:710. 黑名单中的随机数 - 力扣(LeetCode)
随机化
import random
class Solution:
def __init__(self, n: int, blacklist: list):
self.mapping = {}
self.bound = n - len(blacklist)
blacklist_set = set(blacklist)
# 映射黑名单外的数字到[0, bound)范围内的数字
last = n - 1
for b in blacklist:
if b >= self.bound:
continue
while last in blacklist_set:
last -= 1
self.mapping[b] = last
last -= 1
def pick(self) -> int:
# 随机选择一个数字
idx = random.randint(0, self.bound - 1)
# 如果该数字在映射中,返回映射后的数字,否则返回原数字
return self.mapping.get(idx, idx)
这个算法的核心思想是将黑名单中的数字映射到有效范围内的数字,这样每次调用 pick
方法时,都可以直接从有效范围内随机选择一个数字,而不需要担心它会落在黑名单中。通过这种方式,算法最小化了调用内置随机函数的次数,并确保了每个有效数字的选择概率相等。