题目描述
这是 LeetCode 上的 1600. 皇位继承顺序 ,难度为 中等。
Tag : 「单链表」、「哈希表」
一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。
这个王国有一个明确规定的皇位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) ,给定一个人 x 和当前的继承顺序,该函数返回 x 的下一继承人。
Successor(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() 返回 除去 死亡人员的当前继承顺序列表。
示例:
输入: ["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^5105 次birth 和 death 。
- 最多调用 10 次 getInheritanceOrder 。
单向链表 & 标记删除
根据题意,我们需要将「新儿子」插入到「父亲」的「最后一个儿子」的「儿子们」的后面(注意这是个递归过程);如果该「父亲」还没有任何儿子,则直接插到「父亲」后面。
因此,我们需要在节点 Node
中使用一个 last
记录该节点的「最后一个儿子」,同时因为删除的时候,我们无法在 O(1)O(1) 的复杂度内更新 last
信息,所以只能使用「标记删除」的方式。
代码:
class 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(n);其余操作为 O(1)O(1) - 时间复杂度:O(n)O(n)
最后
这是我们「刷穿 LeetCode」系列文章的第 No.1600
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。