字节青训营|队列

队列

队列(Queue) 是一种基本的线性数据结构,它遵循先进先出(First In First Out, FIFO)的原则。这意味着最先被添加到队列中的元素将会是最先被移除的。也就是在尾部添加元素,从头部移除元素,最新添加的元素排在末尾。

双端队列:collections.deque

deque是双端队列(double-ended queue)的缩写,在出队(pop)和入队(append)时的时间复杂度是O(1)。也可以直接用列表实现。

  • from collections import deque
  • append(x) :添加 x 到右端
  • appendleft(x):添加 x 到左端
  • pop() : 移去并且返回一个元素,deque 最右侧的那一一个
  • popleft():移去并且返回一个元素,deque 最左侧的那一个
  • insert(i, x):在位置i插入x。
  • extend(iterable):扩展deque的右侧,通过添加iterable参数中的元素
  • extendleft(iterable):扩展deque的左侧,通过添加iterable参数中的元素:注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。
  • remove(value):移除找到的第一个 value。
  • clear(): 清空
  • copy():拷贝
  • count(x):计算 deque 中元素等于x的个数.
  • index(x[, start[, stop]]):返回x在deque中的位置(在索引star之后,索引stop 之前)
  • reverse():将deque逆序排列。
  • rotate(n=1):向右循环移动n步。如果n是负数,就向左循环。
  • maxlen:Deque的最大尺寸,如果没有限定的话就是None

小R的随机播放顺序

问题描述

小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。
例,给定歌单[5,3,2,1,4],真实的播放顺序是[5,2,4,1,3]。
保证歌曲中的id两两不同。

解题思路

  1. 初始化双端队列:使用deque来存储歌单。

  2. 从队列左侧出队:使用queue.popleft()来取出第一首歌播放,并加入结果列表。

  3. 左旋队列:左旋队列1次,以模拟将当前第一首歌移到最后一首。

  4. 重复操作:重复以上操作,直到队列为空。

from collections import deque
def solution(n: int, a: list) -> list:
    # 初始化队列
    queue = deque(a)
    result = []

    while queue:
        # 取出第一首歌
        song = queue.popleft()
        result.append(song)
        # 如果队列不为空,将当前第一首歌移到队列末尾
        if queue:
            queue.rotate(-1)

    return result

小s的超辣火锅挑战

问题描述

小s和朋友们一共有 n n n个人,他们决定进行一次超辣火锅挑战,来决出最能吃辣的人。他们围成一圈,从1号开始依次顺时针数 k k k个,每数到一个人,这个人就得吃一口超辣火锅。每个人对辣度的耐受值不同,第 i i i个人的耐受值为 a i a_i ai,表示他最多可以吃 a i a_i ai口火锅。一旦吃完这 a i a_i ai口火锅后,这个人就会因为承受不住而离席。
每次顺时针数 k k k个人时,离席的人不算在内。请你按顺序输出每个离席的人的编号,按照他们离席的顺序从早到晚排列。

解题思路

  1. 初始化双端队列:使用deque来存储每个人的序号和耐受度。

  2. 从队列左侧出队:使用people.popleft()来取出当前需要吃火锅的人。

  3. 重新加入队列右侧:如果耐受度大于0,使用people.append()将其重新加入队列;如果耐受度等于0,则该成员离席,将他的序号加入结果列表。

  4. 左旋队列:左旋队列k-1次,以模拟顺时针数k个。

  5. 重复操作:重复以上操作,直到队列为空。

from collections import deque
def solution(n: int, k: int, a: list) -> list:
    # 初始化双端队列,存储每个人的序号和耐受度
    people = deque([(i + 1, a[i]) for i in range(n)])
    result = []

    while people:
        # 从队列左侧出队一个人
        person = people.popleft()
        person_id, tolerance = person
        # 这个人吃一口火锅,耐受度减1
        tolerance -= 1

        # 如果这个人的耐受度仍然大于0,将其重新加入队列右侧
        if tolerance > 0:
            people.append((person_id, tolerance))
        else:
            # 如果这个人的耐受度变为0,将其序号加入结果列表
            result.append(person_id)
        
        # 左旋队列k-1次,以模拟顺时针数k个
        if people:
            people.rotate(1-k)

    return result

优先队列:heapq

优先队列(Priority Queue) 是一种特殊的队列,除了具有队列的性质(先进先出,队列头出,队列尾入),还可以实现快速得到队列中优先级最高的元素。在优先队列中,每个元素都有一定的优先级,优先级最高的元素最先被移除。优先队列可以用于任何需要元素按照一定顺序处理的场景,例如操作系统任务调度、序列合并等

优先队列通常可以使用数组、链表、堆或二叉搜索树等数据结构来实现。其中,堆是最常用的底层数据结构,因为它可以在对数时间内插入和删除元素。

在 Python 中,可以使用 heapq 模块来实现优先队列。heapq 提供了一种基于堆的优先队列实现(默认最小堆)。堆是一种特殊的二叉树,满足父节点的值总是小于或等于其子节点的值(最小堆)或大于或等于其子节点的值(最大堆)的性质。每个节点都有两个子节点2k+1,2k+2

  • import heapq
  • heapq.heapify(pq):将无序列表转换为优先队列pq(最小堆)
  • heapq.heappush(pq, (priority, value)):向优先队列pq中插入元素。这个函数会根据元素的值自动调整堆,保持堆的性质。这里 (priority, value) 是一个元组,其中 priority 是元素的优先级,value 是元素的值。优先级就是排序的一个标准。
  • heapq.heappop(pq):从优先队列中弹出具有最高优先级的元素。这将返回一个元组 (current_priority, current_value),其中 current_priority 是弹出元素的优先级,current_value 是弹出元素的值。
  • heapq.heaprepalce(pq,x): 弹出并返回最小元素,同时添加元素x。

数字替换后的最大和计算

问题描述

小C在白板上写了 n n n个数字,分别为 a_1,a_2,... ,a_n。随后小R对这些数字进行了 m m m次修改操作。在每次操作中,小
R可以将白板上的某个数字替换为新数字 b j b_j bj。每次修改的数字不需要是相同的,但只能替换一次。小C想知道,经过这 m m m次修改后,白板上所有数字之和的最大值是多少。

import heapq
def solution(n: int, m: int, a: list[int], b: list[int]) -> int:
    # 将 a 转换为最小堆
    heapq.heapify(a)
    # 替换操作
    for j in range(m):
        current_b = b[j]             #取出b中当前元素b_j
        min_a = heapq.heappop(a)      # 取出 a 中的最小元素
        heapq.heappush(a, current_b)  # 将 b 中的元素加入 a
    return sum(a)

最小代价问题

问题描述

给定一个数组 a,每个元素的值代表当前的数字,数组中的第i个数加1的代价是b[i]。你需要找出最小的代价,使得数组a中的所有元素都各不相同。

解题思路

  1. 初始化优先队列

    • 首先,将数组 ab 中的元素组合成元组 (value, cost),并将其放入一个列表 pq 中。
    • 然后,使用 heapq.heapify(pq) 将这个列表转换为一个最小堆。最小堆会根据 value 的大小来排序。
  2. 处理重复值

    • 使用一个集合 seen 来记录已经处理过的元素。
    • 从最小堆中弹出堆顶元素 (value, cost)
    • 如果 value 已经在 seen 中,说明这个值已经处理过,需要将其值加1,并将其重新放回最小堆,同时累加代价 cost
    • 如果 value 不在 seen 中,且当前堆中仍有元素,检查堆顶元素 pq[0] 是否与当前 value 相同。如果相同,说明有重复值,需要处理这些重复值。
  3. 处理重复值

    • 当发现 value 与最小堆的堆顶元素 pq[0] 相同时,比较当前 cost 和堆顶元素的 cost,选择较小的代价进行累加,并将值加1后重新放回最小堆。
    • 这个过程会一直持续,直到当前 value 不再与堆顶元素相同为止。
  4. 返回总代价

    • 最终,当最小堆为空时,返回累加的总代价 total_cost
import heapq
def solution(n: int, a: list, b: list) -> int:
    pq = [(a[i],b[i]) for i in range(n)]
    heapq.heapify(pq)
    total_cost = 0
    seen = set()  # 用于记录已经处理过的元素
    
    while pq:
        value,cost = heapq.heappop(pq)
        if value in seen:
            # 如果当前值已经处理过,直接将值+1放回最小堆,并累加代价
            total_cost += cost
            heapq.heappush(pq, (value + 1, cost))
        else:
            #如果当前值未处理过
            if pq:
                next_value, next_cost = pq[0]               
                #比较当前元素和最小堆的堆顶元素pq[0]的值
                while value == next_value:
                    min_cost = min(cost,next_cost)
                    total_cost += min_cost
                    heapq.heappop(pq)
                    heapq.heappush(pq, (value + 1, min_cost))
                    cost = max(cost,next_cost)
                    next_value, next_cost = pq[0]
                seen.add(value)

    return total_cost

384 小C大作战得分计算

问题描述

在游戏"小C大作战"中,每个角色都有一个能量值a,,能量大的角色可以击败能量小的角色,并获得对应的分
数。每个角色被击败后,击败者可以获得b得分。为了维持游戏平衡,每个角色最多只能击败m个其他角
色。请计算出每个角色最终可以获得的最大分数。
保证这些球的能量值两两不同。

解题思路

  1. 初始化优先队列

    • 使用列表characters存储每个角色的能量值a_i、得分b_i和原始索引i
    • characters转换为最小堆,以便按能量值从小到大处理角色。
  2. 初始化结果列表

    • 创建一个长度为n的结果列表result,用于存储每个角色的最终得分。
    • 创建scores列表,用于存储之前处理过的角色的得分。
  3. 处理每个角色

    • 遍历每个角色,从最小堆中弹出当前角色。

    • 计算当前角色的最大得分,并按照原始索引存入结果列表:对scores列表进行排序,并取前m个最大得分进行求和。

    • 将当前角色的得分加入scores列表。

  4. 返回结果

    • 最终返回result列表,其中包含每个角色的最大得分。
import heapq 
def solution(n: int, m: int, a: list, b: list) -> list:
    # 将角色和他们的能量值、得分存储在一个列表中
    characters = [(a[i], b[i],i) for i in range(n)]
    

    heapq.heapify(characters)
    # 初始化结果列表
    result = [0]*n
    scores = []
    for i in range(n):
        # 计算当前角色可以击败的其他角色的最大得分
        # 将当前角色的得分加入结果
        energy, score, index = heapq.heappop(characters)
        result[index] = sum(sorted(scores, reverse=True)[:m])
        # 将当前角色的得分加入得分列表
        scores.append(score)

    return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值