3408. Design Task Manager | LeetCode | Swift

vc7-avatar-img
發佈於LeetCode
更新 發佈閱讀 11 分鐘

每日一題

日期

2025/9/18

前置

LeetCode 有引入 swift-collections 1.1.4 ,這篇就會用其中的 Heap 資料結構而不自己實作。

Ref: https://support.leetcode.com/hc/en-us/articles/360011833974-What-are-the-environments-for-the-programming-languages

經過多次改善、簡化後最快有到 118ms

raw-image

想法與資料結構

swift-collections 提供的 Heap 無法有效率地移除,像是需要使用 unordered 陣列、找到目標物、最後再重新生成 Heap 。時間複雜度和空間複雜度都可觀之外,寫出來的程式碼也變得複雜難讀。

於是根據題意: taskId 是唯一,這篇的資料結構就可以這樣應用:

  • HashMap: 維持 taskId: task pairs,可以幫助我們快速以 taskId 取得 task 。 (O(1))
  • Heap: 維持排序,無論是刪或加, task 都只進不出。當要進行 execTop() 過程,只要 pop 到過時的 task 時直接忽略即可。
class TaskManager {
// 用來 Heap 排序的容器,其中的元素只增不減。在 pop 時和 taskMap 對照當下 task 的合法性
private var tasks = Heap<Task>()
// 識別現有 Task 的 Map, [taskId: Task]
private var taskMap = [Int: Task]()

/* To be continue */
}

Task 構造

根據題意,

  • priority 相同時,taskId 較大者為大
struct Task: Comparable {
let userId: Int
let taskId: Int
let priority: Int

// MARK: Comparable

static func < (lhs: Task, rhs: Task) -> Bool {
guard lhs.priority != rhs.priority else {
return lhs.taskId < rhs.taskId
}

return lhs.priority < rhs.priority
}
}

新增的核心邏輯

init 裡面、edit 都會用到生成 Task ,插入 Heap, HashMap 的邏輯就集中到這裡

class TaskManager {
/* 省略 */

/// 生成 Task 並插入 HashMap 和 Heap
func add(_ userId: Int, _ taskId: Int, _ priority: Int) {
let task = Task(
userId: userId,
taskId: taskId,
priority: priority
)
tasks.insert(task)
taskMap[task.taskId] = task
}
/* 省略 */
}

初始化

走訪完美一個子陣列,直接呼叫 add 即可

class TaskManager {
/* 省略 */
init(_ tasks: [[Int]]) {
for task in tasks {
add(task[0], task[1], task[2])
}
}
/* 省略 */
}

編輯

在編輯的時候,依據前面提到的處理方式, Heap 裡的 task 只出不進。這邊找到對應的 task 、取用 userId 後新增一個新的 task 。

class TaskManager {
/* 省略 */
func edit(_ taskId: Int, _ newPriority: Int) {
guard let task = taskMap[taskId] else { return }
add(task.userId, taskId, newPriority)
}
/* 省略 */
}

移除

因為 Heap 裡的 task 只出不進,這邊就只需要把 HashMap 裡面 key 為 taskId 的值清除就好了

class TaskManager {
/* 省略 */
func rmv(_ taskId: Int) {
taskMap[taskId] = nil
}
/* 省略 */
}

回傳最大 task 的 userId

因為 Heap 裡的 task 只出不進,所以我們這邊有兩個步驟

  1. 呼叫 popMax 彈出一個 task
  2. 彈出的 task 有在 HashMap 裡,否則忽略, 繼續 1. 彈出下一個最大值
class TaskManager {
/* 省略 */
func execTop() -> Int {
while let top = tasks.popMax() { // 1.
if taskMap[top.taskId] == top { // 2.
taskMap[top.taskId] = nil
return top.userId
}
}
return -1
}
/* 省略 */
}

最終程式碼

class TaskManager {
/// 用來 Heap 排序的容器,其中的元素只增不減
private var tasks = Heap<Task>()
/// 識別現有 Task 的 Map, [taskId: Task]
private var taskMap = [Int: Task]()

init(_ tasks: [[Int]]) {
for task in tasks {
add(task[0], task[1], task[2])
}
}

/// 生成 Task 並插入 HashMap 和 Heap
func add(_ userId: Int, _ taskId: Int, _ priority: Int) {
let task = Task(
userId: userId,
taskId: taskId,
priority: priority
)
tasks.insert(task)
taskMap[task.taskId] = task
}

func edit(_ taskId: Int, _ newPriority: Int) {
guard let task = taskMap[taskId] else { return }
add(task.userId, taskId, newPriority)
}

/// 只移除 HashMap 裡面的元素
func rmv(_ taskId: Int) {
taskMap[taskId] = nil
}

func execTop() -> Int {
while let top = tasks.popMax() {
if taskMap[top.taskId] == top {
taskMap[top.taskId] = nil
return top.userId
}
}
return -1
}
}

struct Task: Comparable {
let userId: Int
let taskId: Int
let priority: Int

// MARK: Comparable

static func < (lhs: Task, rhs: Task) -> Bool {
guard lhs.priority != rhs.priority else {
return lhs.taskId < rhs.taskId
}

return lhs.priority < rhs.priority
}
}



留言
avatar-img
萱寫寫
3會員
19內容數
讀書心得、活動參加心得
萱寫寫的其他內容
2025/09/17
LeetCode 每日一題: 2025/09/17
2025/09/17
LeetCode 每日一題: 2025/09/17
2025/09/16
2025/09/16
2025/09/15
2025/09/15
看更多
你可能也想看
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
利用文字紀錄,明確寫下自己的採購項目......
Thumbnail
利用文字紀錄,明確寫下自己的採購項目......
Thumbnail
這篇文章描述了作者從兼職開發轉為全職開發的過程,並分享了從混進學界指日可待的積極態度。作者也提及自己在專案製作與個人生活上的矛盾與感想,最後分享了專案管理和敏捷開發相關的文章與影片。
Thumbnail
這篇文章描述了作者從兼職開發轉為全職開發的過程,並分享了從混進學界指日可待的積極態度。作者也提及自己在專案製作與個人生活上的矛盾與感想,最後分享了專案管理和敏捷開發相關的文章與影片。
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
題目敘述 題目會給我們一個定義好的類別和function介面,要求我們實作建構子和ping() function來滿足指定的需求。 RecentCounter類別的建構子 建構子應該初始化來電紀錄,內容為空(零筆資料) int ping(int t) t代表來電時刻,單位是毫秒m
Thumbnail
題目敘述 題目會給我們一個定義好的類別和function介面,要求我們實作建構子和ping() function來滿足指定的需求。 RecentCounter類別的建構子 建構子應該初始化來電紀錄,內容為空(零筆資料) int ping(int t) t代表來電時刻,單位是毫秒m
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News