Prufer 编码的相关概念
Prufer 编码: Prufer 编码是无根树的一种编码方式,Prufer编码和无根树之间可以相互转化。
无根树转化成 Prufer 编码
对于一颗无根树,每次找到一个编号最小的叶节点(度数为 1 的节点),把和编号最小的叶节点相邻的节点输出,然后把编号最小的叶节点删去,继续循环这样的操作直到只剩两个节点,由于最后一个输出的节点是唯一确定的,一定是 n,因此不需要输出,到此即得到 Prufer 编码
对于代码实现,可以用 O(n) 的时间复杂度求出一颗无根树的 Prufer 编码。
取一个变量 j 从 1 开始枚举,每次找到第一个叶节点,此时 j 存的就是所有叶节点中编号最小的一个叶节点。把相邻的节点输出,并把编号最小的叶节点删掉。这时最多只会多出一个新的叶节点。
这个新的叶节点会有两种情况,一种是编号大于 j,这时最小的叶节点还是 j,不用管。另一种是编号小于 j,此时 j 就变成新的叶节点。
这样就能在 O(n) 的时间复杂度以内求出无根树的 Prufer 编码。
注意: 在实现过程中,我们将无根树看作一个以 n 为根节点的有根树,由于 n 一定是最后一个,因此结果依然正确且实现更简单。
Prufer 编码转化成无根树
对于一个 Prufer 编码,可以发现每个编号出现几次,就说明它有多少个儿子。虽然 n 没有出现,但是由于 Prufer 编码最后一位固定是 n,所以 n 也有一个儿子。然后左往右考虑 Prufer 编码,例如 3 5 4 5,第一个是 3,最开始叶节点只有 1 和 2,输出 3 时是从小到大找叶节点,所以 3 应该是编号最小的叶节点的父亲,此时编号最小的叶节点是 1,因此 1 的父亲就是 3。此时已经找到 3 的一个儿子,因此 3 的儿子数量 −1,然后找 5 的儿子。以此类推,就能找出所有节点的父节点,这样就根据 Prufer 编码转化出一颗无根树。
对于代码实现,同样可以做到 O(n) 的时间复杂度,还是用一个变量 j,从前往后枚举当前编号最小的叶节点,然后看 Prufer 编码的每一位 x,每次其实就是要把 j 的父节点设成 x。然后要把 j 删掉,删掉之后最多只会多一个新的叶节点。
对于这个新的叶节点,一种情况是新的叶节点的编号大于 j,那就不用管,继续去枚举 j,另一种情况是新的叶节点的编号小于 j,由于 j 已经是当前最小值,而新的叶节点的编号更小,说明删掉 j 后,这个新的叶节点就是新的 j,可以直接处理新点。
可以发现两种转化方式都是类似的,且都是 O(n) 的时间复杂度,非常简洁迅速。
可以讲讲策略的构造问题吗?qq
没怎么听明白,具体是指什么呀
就是直接做可能是指数级/高复杂度,我们可以通过构造特殊形式达到简化目的的题
我看了一下,一般是线性构造就够了,如果是指数级的可能要考虑用他的性质来求解,这类题可能不用将他构造出来