一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。
这个王国有一个明确规定的王位继承顺序,第一继承人总是国王自己。我们定义递归函数 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
对应的人员是活着的。
最多调用 105
次birth
和 death
。
最多调用 10
次 getInheritanceOrder
。
方法一:多叉树的前序遍历 思路与算法
我们可以发现,题目中定义的 Successor(x, curOrder) 函数,与多叉树的前序遍历过程是一致的:
「返回 x 不在 curOrder 中最年长的孩子」对应着选择 x 在树中的一个子节点,递归地进行遍历操作;
「返回 Successor(x} ~的父亲\texttt{, curOrder)」对应着当我们将以 x 为根的子树遍历完成后,回溯到 x 的父节点继续进行遍历;
「返回 null」对应着我们将整棵树遍历完成。
因此,对于题目中需要实现的每一个函数,我们可以分别设计出如下的算法:
ThroneInheritance(kingName):我们将 kingName 作为树的根节点;
birth(parentName, childName):我们在树中添加一条从 parentName 到 childName 的边,将 childName 作为 parentName 的子节点;
death(name):我们使用一个哈希集合记录所有的死亡人员,将 name 加入该哈希集合中;
getInheritanceOrder():我们从根节点开始对整棵树进行前序遍历。需要注意的是,如果遍历到死亡人员,那么不能将其加入继承顺序列表中 。
细节
那么我们如何存储这棵树呢?
一种可行的方法是使用哈希映射。记哈希映射为 edges,那么对于 edges 中的每一个键值对 (k, v),键 k 表示一个人,值 v 以列表的形式存放了这个人所有的孩子,列表可以为空。
这样一来,对于 birth(parentName, childName) 操作,我们只需要将 childName 加入 parentName 在哈希映射中的列表末尾即可。
代码
[sol1-C++] 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 class ThroneInheritance {private : unordered_map<string, vector<string>> edges; unordered_set<string> dead; string king; public : ThroneInheritance (string kingName): king{move (kingName)} {} void birth (string parentName, string childName) { edges[move (parentName)].push_back (move (childName)); } void death (string name) { dead.insert (move (name)); } vector<string> getInheritanceOrder () { vector<string> ans; function<void (const string&)> preorder = [&](const string& name) { if (!dead.count (name)) { ans.push_back (name); } if (edges.count (name)) { for (const string& childName: edges[name]) { preorder (childName); } } }; preorder (king); return ans; } };
[sol1-Java] 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 class ThroneInheritance { Map<String, List<String>> edges; Set<String> dead; String king; public ThroneInheritance (String kingName) { edges = new HashMap <String, List<String>>(); dead = new HashSet <String>(); king = kingName; } public void birth (String parentName, String childName) { List<String> children = edges.getOrDefault(parentName, new ArrayList <String>()); children.add(childName); edges.put(parentName, children); } public void death (String name) { dead.add(name); } public List<String> getInheritanceOrder () { List<String> ans = new ArrayList <String>(); preorder(ans, king); return ans; } private void preorder (List<String> ans, String name) { if (!dead.contains(name)) { ans.add(name); } List<String> children = edges.getOrDefault(name, new ArrayList <String>()); for (String childName : children) { preorder(ans, childName); } } }
[sol1-C#] 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 public class ThroneInheritance { Dictionary<string , IList<string >> edges; ISet<string > dead; string king; public ThroneInheritance (string kingName ) { edges = new Dictionary<string , IList<string >>(); dead = new HashSet<string >(); king = kingName; } public void Birth (string parentName, string childName ) { IList<string > children; if (edges.TryGetValue(parentName, out children)) { children.Add(childName); edges[parentName] = children; } else { children = new List<string >(); children.Add(childName); edges.Add(parentName, children); } } public void Death (string name ) { dead.Add(name); } public IList<string > GetInheritanceOrder () { IList<string > ans = new List<string >(); Preorder(ans, king); return ans; } private void Preorder (IList<string > ans, string name ) { if (!dead.Contains(name)) { ans.Add(name); } IList<string > children = edges.TryGetValue(name, out children) ? children : new List<string >(); foreach (string childName in children) { Preorder(ans, childName); } } }
[sol1-Python3] 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 class ThroneInheritance : def __init__ (self, kingName: str ): self.edges = defaultdict(list ) self.dead = set () self.king = kingName def birth (self, parentName: str , childName: str ) -> None : self.edges[parentName].append(childName) def death (self, name: str ) -> None : self.dead.add(name) def getInheritanceOrder (self ) -> List [str ]: ans = list () def preorder (name: str ) -> None : if name not in self.dead: ans.append(name) if name in self.edges: for childName in self.edges[name]: preorder(childName) preorder(self.king) return ans
[sol1-JavaScript] 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 var ThroneInheritance = function (kingName ) { this .edges = new Map (); this .dead = new Set (); this .king = kingName; }; ThroneInheritance .prototype .birth = function (parentName, childName ) { if (!this .edges .has (parentName)) { this .edges .set (parentName, []); } this .edges .get (parentName).push (childName); }; ThroneInheritance .prototype .death = function (name ) { this .dead .add (name); }; ThroneInheritance .prototype .getInheritanceOrder = function ( ) { const ans = []; const preorder = (name ) => { if (!this .dead .has (name)) { ans.push (name); } if (this .edges .has (name)) { for (const childName of this .edges .get (name)) { preorder (childName); } } } preorder (this .king ); return ans; };
[sol1-Golang] 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 type ThroneInheritance struct { king string edges map [string ][]string dead map [string ]bool } func Constructor (kingName string ) (t ThroneInheritance) { return ThroneInheritance{kingName, map [string ][]string {}, map [string ]bool {} } } func (t *ThroneInheritance) Birth(parentName, childName string ) { t.edges[parentName] = append (t.edges[parentName], childName) } func (t *ThroneInheritance) Death(name string ) { t.dead[name] = true } func (t *ThroneInheritance) GetInheritanceOrder() (ans []string ) { var preorder func (string ) preorder = func (name string ) { if !t.dead[name] { ans = append (ans, name) } for _, childName := range t.edges[name] { preorder(childName) } } preorder(t.king) return }
复杂度分析
时间复杂度:
ThroneInheritance(kingName):O(1);
birth(parentName, childName):O(1);
death(name):O(1);
getInheritanceOrder():O(n),其中 n 是当前树中的总人数。我们需要对整棵树进行一次前序遍历,时间复杂度为 O(n)。
空间复杂度:
n 个节点的树包含 n-1 条边,因此我们需要 O(n) 的空间(即哈希映射 edges)存储整棵树;
我们需要 O(n) 的空间(即哈希集合)存储所有的死亡人员;
在 getInheritanceOrder() 中前序遍历的过程中,我们使用的是递归,需要一定的栈空间,栈空间的大小与树的高度成正比。由于树的高度不会超过树中的节点个数,因此栈空间最多为 O(n)。
在上述的时空复杂度分析中,我们默认了所有字符串(即人名)的操作时间以及存储空间都是 O(1) 的。如果读者希望将字符串的长度也看作变量,那么只需要将除了栈空间以外的所有项由 O(1)/O(n) 变为 O(l)/O(nl) 即可,其中 l 是字符串的最大长度。