|
| 1 | +# [2890. Design a Todo List](https://leetcode.cn/problems/design-a-todo-list) |
| 2 | + |
| 3 | +[English Version](/solution/2800-2899/2890.Design%20a%20Todo%20List/README_EN.md) |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +<!-- 这里写题目描述 --> |
| 8 | + |
| 9 | +<p>Design a Todo List Where users can add <strong>tasks</strong>, mark them as <strong>complete</strong>, or get a list of pending tasks. Users can also add <strong>tags</strong> to tasks and can filter the tasks by certain tags.</p> |
| 10 | + |
| 11 | +<p>Implement the <code>TodoList</code> class:</p> |
| 12 | + |
| 13 | +<ul> |
| 14 | + <li><code>TodoList()</code> Initializes the object.</li> |
| 15 | + <li><code>int addTask(int userId, String taskDescription, int dueDate, List<String> tags)</code> Adds a task for the user with the ID <code>userId</code> with a due date equal to <code>dueDate</code> and a list of tags attached to the task. The return value is the ID of the task. This ID starts at <code>1</code> and is <strong>sequentially</strong> increasing. That is, the first task's id should be <code>1</code>, the second task's id should be <code>2</code>, and so on.</li> |
| 16 | + <li><code>List<String> getAllTasks(int userId)</code> Returns a list of all the tasks not marked as complete for the user with ID <code>userId</code>, ordered by the due date. You should return an empty list if the user has no uncompleted tasks.</li> |
| 17 | + <li><code>List<String> getTasksForTag(int userId, String tag)</code> Returns a list of all the tasks that are not marked as complete for the user with the ID <code>userId</code> and have <code>tag</code> as one of their tags, ordered by their due date. Return an empty list if no such task exists.</li> |
| 18 | + <li><code>void completeTask(int userId, int taskId)</code> Marks the task with the ID <code>taskId</code> as completed only if the task exists and the user with the ID <code>userId</code> has this task, and it is uncompleted.</li> |
| 19 | +</ul> |
| 20 | + |
| 21 | +<p> </p> |
| 22 | +<p><strong class="example">Example 1:</strong></p> |
| 23 | + |
| 24 | +<pre> |
| 25 | +<strong>Input</strong> |
| 26 | +["TodoList", "addTask", "addTask", "getAllTasks", "getAllTasks", "addTask", "getTasksForTag", "completeTask", "completeTask", "getTasksForTag", "getAllTasks"] |
| 27 | +[[], [1, "Task1", 50, []], [1, "Task2", 100, ["P1"]], [1], [5], [1, "Task3", 30, ["P1"]], [1, "P1"], [5, 1], [1, 2], [1, "P1"], [1]] |
| 28 | +<strong>Output</strong> |
| 29 | +[null, 1, 2, ["Task1", "Task2"], [], 3, ["Task3", "Task2"], null, null, ["Task3"], ["Task3", "Task1"]] |
| 30 | + |
| 31 | +<strong>Explanation</strong> |
| 32 | +TodoList todoList = new TodoList(); |
| 33 | +todoList.addTask(1, "Task1", 50, []); // return 1. This adds a new task for the user with id 1. |
| 34 | +todoList.addTask(1, "Task2", 100, ["P1"]); // return 2. This adds another task for the user with id 1. |
| 35 | +todoList.getAllTasks(1); // return ["Task1", "Task2"]. User 1 has two uncompleted tasks so far. |
| 36 | +todoList.getAllTasks(5); // return []. User 5 does not have any tasks so far. |
| 37 | +todoList.addTask(1, "Task3", 30, ["P1"]); // return 3. This adds another task for the user with id 1. |
| 38 | +todoList.getTasksForTag(1, "P1"); // return ["Task3", "Task2"]. This returns the uncompleted tasks that have the tag "P1" for the user with id 1. |
| 39 | +todoList.completeTask(5, 1); // This does nothing, since task 1 does not belong to user 5. |
| 40 | +todoList.completeTask(1, 2); // This marks task 2 as completed. |
| 41 | +todoList.getTasksForTag(1, "P1"); // return ["Task3"]. This returns the uncompleted tasks that have the tag "P1" for the user with id 1. |
| 42 | + // Notice that we did not include "Task2" because it is completed now. |
| 43 | +todoList.getAllTasks(1); // return ["Task3", "Task1"]. User 1 now has 2 uncompleted tasks. |
| 44 | + |
| 45 | +</pre> |
| 46 | + |
| 47 | +<p> </p> |
| 48 | +<p><strong>Constraints:</strong></p> |
| 49 | + |
| 50 | +<ul> |
| 51 | + <li><code>1 <= userId, taskId, dueDate <= 100</code></li> |
| 52 | + <li><code>0 <= tags.length <= 100</code></li> |
| 53 | + <li><code>1 <= taskDescription.length <= 50</code></li> |
| 54 | + <li><code>1 <= tags[i].length, tag.length <= 20</code></li> |
| 55 | + <li>All <code>dueDate</code> values are unique.</li> |
| 56 | + <li>All the strings consist of lowercase and uppercase English letters and digits.</li> |
| 57 | + <li>At most <code>100</code> calls will be made for each method.</li> |
| 58 | +</ul> |
| 59 | + |
| 60 | +## 解法 |
| 61 | + |
| 62 | +<!-- 这里可写通用的实现逻辑 --> |
| 63 | + |
| 64 | +**方法一:哈希表 + 有序集合** |
| 65 | + |
| 66 | +我们使用哈希表 $tasks$ 记录每个用户的任务集合,其中键为用户 ID,值为一个有序集合,按照任务的截止日期排序。另外用一个变量 $i$ 记录当前任务的 ID。 |
| 67 | + |
| 68 | +调用 `addTask` 方法时,我们将任务添加到对应用户的任务集合中,返回任务 ID。此操作的时间复杂度为 $O(\log n)$。 |
| 69 | + |
| 70 | +调用 `getAllTasks` 方法时,我们遍历对应用户的任务集合,将未完成的任务的描述添加到结果列表中,返回结果列表。此操作的时间复杂度为 $O(n)$。 |
| 71 | + |
| 72 | +调用 `getTasksForTag` 方法时,我们遍历对应用户的任务集合,将未完成的任务的描述添加到结果列表中,返回结果列表。此操作的时间复杂度为 $O(n)$。 |
| 73 | + |
| 74 | +调用 `completeTask` 方法时,我们遍历对应用户的任务集合,将任务 ID 为 $taskId$ 的任务标记为已完成。此操作的时间复杂度为 $(n)$。 |
| 75 | + |
| 76 | +空间复杂度 $O(n)$。其中 $n$ 为所有任务的数量。 |
| 77 | + |
| 78 | +<!-- tabs:start --> |
| 79 | + |
| 80 | +### **Python3** |
| 81 | + |
| 82 | +<!-- 这里可写当前语言的特殊实现逻辑 --> |
| 83 | + |
| 84 | +```python |
| 85 | +from sortedcontainers import SortedList |
| 86 | + |
| 87 | + |
| 88 | +class TodoList: |
| 89 | + |
| 90 | + def __init__(self): |
| 91 | + self.i = 1 |
| 92 | + self.tasks = defaultdict(SortedList) |
| 93 | + |
| 94 | + def addTask(self, userId: int, taskDescription: str, dueDate: int, tags: List[str]) -> int: |
| 95 | + taskId = self.i |
| 96 | + self.i += 1 |
| 97 | + self.tasks[userId].add( |
| 98 | + [dueDate, taskDescription, set(tags), taskId, False]) |
| 99 | + return taskId |
| 100 | + |
| 101 | + def getAllTasks(self, userId: int) -> List[str]: |
| 102 | + return [x[1] for x in self.tasks[userId] if not x[4]] |
| 103 | + |
| 104 | + def getTasksForTag(self, userId: int, tag: str) -> List[str]: |
| 105 | + return [x[1] for x in self.tasks[userId] if not x[4] and tag in x[2]] |
| 106 | + |
| 107 | + def completeTask(self, userId: int, taskId: int) -> None: |
| 108 | + for task in self.tasks[userId]: |
| 109 | + if task[3] == taskId: |
| 110 | + task[4] = True |
| 111 | + break |
| 112 | + |
| 113 | + |
| 114 | +# Your TodoList object will be instantiated and called as such: |
| 115 | +# obj = TodoList() |
| 116 | +# param_1 = obj.addTask(userId,taskDescription,dueDate,tags) |
| 117 | +# param_2 = obj.getAllTasks(userId) |
| 118 | +# param_3 = obj.getTasksForTag(userId,tag) |
| 119 | +# obj.completeTask(userId,taskId) |
| 120 | +``` |
| 121 | + |
| 122 | +### **Java** |
| 123 | + |
| 124 | +<!-- 这里可写当前语言的特殊实现逻辑 --> |
| 125 | + |
| 126 | +```java |
| 127 | +class Task { |
| 128 | + int taskId; |
| 129 | + String taskName; |
| 130 | + int dueDate; |
| 131 | + Set<String> tags; |
| 132 | + boolean finish; |
| 133 | + |
| 134 | + public Task(int taskId, String taskName, int dueDate, Set<String> tags) { |
| 135 | + this.taskId = taskId; |
| 136 | + this.taskName = taskName; |
| 137 | + this.dueDate = dueDate; |
| 138 | + this.tags = tags; |
| 139 | + } |
| 140 | +} |
| 141 | + |
| 142 | +class TodoList { |
| 143 | + private int i = 1; |
| 144 | + private Map<Integer, TreeSet<Task>> tasks = new HashMap<>(); |
| 145 | + |
| 146 | + public TodoList() { |
| 147 | + |
| 148 | + } |
| 149 | + |
| 150 | + public int addTask(int userId, String taskDescription, int dueDate, List<String> tags) { |
| 151 | + Task task = new Task(i++, taskDescription, dueDate, new HashSet<>(tags)); |
| 152 | + tasks.computeIfAbsent(userId, k -> new TreeSet<>(Comparator.comparingInt(a -> a.dueDate))).add(task); |
| 153 | + return task.taskId; |
| 154 | + } |
| 155 | + |
| 156 | + public List<String> getAllTasks(int userId) { |
| 157 | + List<String> ans = new ArrayList<>(); |
| 158 | + if (tasks.containsKey(userId)) { |
| 159 | + for (Task task : tasks.get(userId)) { |
| 160 | + if (!task.finish) { |
| 161 | + ans.add(task.taskName); |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + return ans; |
| 166 | + } |
| 167 | + |
| 168 | + public List<String> getTasksForTag(int userId, String tag) { |
| 169 | + List<String> ans = new ArrayList<>(); |
| 170 | + if (tasks.containsKey(userId)) { |
| 171 | + for (Task task : tasks.get(userId)) { |
| 172 | + if (task.tags.contains(tag) && !task.finish) { |
| 173 | + ans.add(task.taskName); |
| 174 | + } |
| 175 | + } |
| 176 | + } |
| 177 | + return ans; |
| 178 | + } |
| 179 | + |
| 180 | + public void completeTask(int userId, int taskId) { |
| 181 | + if (tasks.containsKey(userId)) { |
| 182 | + for (Task task : tasks.get(userId)) { |
| 183 | + if (task.taskId == taskId) { |
| 184 | + task.finish = true; |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +/** |
| 193 | + * Your TodoList object will be instantiated and called as such: |
| 194 | + * TodoList obj = new TodoList(); |
| 195 | + * int param_1 = obj.addTask(userId,taskDescription,dueDate,tags); |
| 196 | + * List<String> param_2 = obj.getAllTasks(userId); |
| 197 | + * List<String> param_3 = obj.getTasksForTag(userId,tag); |
| 198 | + * obj.completeTask(userId,taskId); |
| 199 | + */ |
| 200 | +``` |
| 201 | + |
| 202 | +### **C++** |
| 203 | + |
| 204 | +```cpp |
| 205 | + |
| 206 | +``` |
| 207 | + |
| 208 | +### **Go** |
| 209 | + |
| 210 | +```go |
| 211 | + |
| 212 | +``` |
| 213 | + |
| 214 | +### **...** |
| 215 | + |
| 216 | +``` |
| 217 | +
|
| 218 | +``` |
| 219 | + |
| 220 | +<!-- tabs:end --> |
0 commit comments