LC 1600. 皇位继承顺序
题目描述
这是 LeetCode 上的 1600. 皇位继承顺序 ,难度为 中等。
一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。
这个王国有一个明确规定的皇位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) ,给定一个人 x 和当前的继承顺序,该函数返回 x 的下一继承人。1
2
3
4
5Successor(x, curOrder):
如果 x 没有孩子或者所有 x 的孩子都在 curOrder 中:
如果 x 是国王,那么返回 null
否则,返回 Successor(x 的父亲, curOrder)
否则,返回 x 不在 curOrder 中最年长的孩子
比方说,假设王国由国王,他的孩子 Alice 和 Bob (Alice 比 Bob 年长)和 Alice 的孩子 Jack 组成。
- 一开始, curOrder 为 [“king”].
- 调用 Successor(king, curOrder) ,返回 Alice ,所以我们将 Alice 放入 curOrder 中,得到 [“king”, “Alice”] 。
- 调用 Successor(Alice, curOrder) ,返回 Jack ,所以我们将 Jack 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”] 。
- 调用 Successor(Jack, curOrder) ,返回 Bob ,所以我们将 Bob 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”, “Bob”] 。
- 调用 Successor(Bob, curOrder) ,返回 null 。最终得到继承顺序为 [“king”, “Alice”, “Jack”, “Bob”] 。
通过以上的函数,我们总是能得到一个唯一的继承顺序。
请你实现 ThroneInheritance 类:
- ThroneInheritance(string kingName) 初始化一个 ThroneInheritance 类的对象。国王的名字作为构造函数的参数传入。
- void birth(string parentName, string childName) 表示 parentName 新拥有了一个名为 childName 的孩子。
- void death(string name) 表示名为 name 的人死亡。一个人的死亡不会影响 Successor 函数,也不会影响当前的继承顺序。你可以只将这个人标记为死亡状态。
- string[] getInheritanceOrder() 返回 除去 死亡人员的当前继承顺序列表。
示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18输入:
["ThroneInheritance", "birth", "birth", "birth", "birth", "birth", "birth", "getInheritanceOrder", "death", "getInheritanceOrder"]
[["king"], ["king", "andy"], ["king", "bob"], ["king", "catherine"], ["andy", "matthew"], ["bob", "alex"], ["bob", "asha"], [null], ["bob"], [null]]
输出:
[null, null, null, null, null, null, null, ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"], null, ["king", "andy", "matthew", "alex", "asha", "catherine"]]
解释:
ThroneInheritance t= new ThroneInheritance("king"); // 继承顺序:king
t.birth("king", "andy"); // 继承顺序:king > andy
t.birth("king", "bob"); // 继承顺序:king > andy > bob
t.birth("king", "catherine"); // 继承顺序:king > andy > bob > catherine
t.birth("andy", "matthew"); // 继承顺序:king > andy > matthew > bob > catherine
t.birth("bob", "alex"); // 继承顺序:king > andy > matthew > bob > alex > catherine
t.birth("bob", "asha"); // 继承顺序:king > andy > matthew > bob > alex > asha > catherine
t.getInheritanceOrder(); // 返回 ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"]
t.death("bob"); // 继承顺序:king > andy > matthew > bob(已经去世)> alex > asha > catherine
t.getInheritanceOrder(); // 返回 ["king", "andy", "matthew", "alex", "asha", "catherine"]
提示:
- 1 <= kingName.length, parentName.length, childName.length, name.length <= 15
- kingName,parentName, childName 和 name 仅包含小写英文字母。
- 所有的参数 childName 和 kingName 互不相同。
- 所有 death 函数中的死亡名字 name 要么是国王,要么是已经出生了的人员名字。
- 每次调用 birth(parentName, childName) 时,测试用例都保证 parentName 对应的人员是活着的。
- 最多调用 $10^5$ 次birth 和 death 。
- 最多调用 10 次 getInheritanceOrder 。
单向链表 & 标记删除
根据题意,我们需要将「新儿子」插入到「父亲」的「最后一个儿子」的「儿子们」的后面(注意这是个递归过程);如果该「父亲」还没有任何儿子,则直接插到「父亲」后面。
因此,我们需要在节点 Node
中使用一个 last
记录该节点的「最后一个儿子」,同时因为删除的时候,我们无法在 $O(1)$ 的复杂度内更新 last
信息,所以只能使用「标记删除」的方式。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45class ThroneInheritance {
class Node {
String name;
Node next;
Node last; // 记录最后一个儿子
boolean isDeleted = false;
Node (String _name) {
name = _name;
}
}
Map<String, Node> map = new HashMap<>();
Node head = new Node(""), tail = new Node("");
public ThroneInheritance(String name) {
Node root = new Node(name);
root.next = tail;
head.next = root;
map.put(name, root);
}
public void birth(String pname, String cname) {
Node node = new Node(cname);
map.put(cname, node);
Node p = map.get(pname);
Node tmp = p;
while (tmp.last != null) tmp = tmp.last;
node.next = tmp.next;
tmp.next = node;
p.last = node;
}
public void death(String name) {
Node node = map.get(name);
node.isDeleted = true;
}
public List<String> getInheritanceOrder() {
List<String> ans = new ArrayList<>();
Node tmp = head.next;
while (tmp.next != null) {
if (!tmp.isDeleted) ans.add(tmp.name);
tmp = tmp.next;
}
return ans;
}
}
- 时间复杂度:
birth
和getInheritanceOrder
操作为 $O(n)$;其余操作为 $O(1)$ - 时间复杂度:$O(n)$
最后
这是我们「刷穿 LeetCode」系列文章的第 No.1600
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!