跳到主要内容

子树中是否有另一棵子树

力扣官方题解: 深度优先搜索序列上做串匹配

这个方法需要我们先了解一个「小套路」:一棵子树上的点在深度优先搜索序列(即先序遍历)中是连续的。了解了这个「小套路」之后,我们可以确定解决这个问题的方向就是:把 sss 和 ttt 先转换成深度优先搜索序列,然后看 ttt 的深度优先搜索序列是否是 sss 的深度优先搜索序列的「子串」。

这样做正确吗? 假设 sss 由两个点组成,111 是根,222 是 111 的左孩子;ttt 也由两个点组成,111 是根,222 是 111 的右孩子。这样一来 sss 和 ttt 的深度优先搜索序列相同,可是 ttt 并不是 sss 的某一棵子树。由此可见「sss 的深度优先搜索序列包含 ttt 的深度优先搜索序列」是「ttt 是 sss 子树」的必要不充分条件,所以单纯这样做是不正确的。

为了解决这个问题,我们可以引入两个空值 lNull 和 rNull,当一个节点的左孩子或者右孩子为空的时候,就插入这两个空值,这样深度优先搜索序列就唯一对应一棵树。处理完之后,就可以通过判断「sss 的深度优先搜索序列包含 ttt 的深度优先搜索序列」来判断答案。

在判断「sss 的深度优先搜索序列包含 ttt 的深度优先搜索序列」的时候,可以暴力匹配,也可以使用 KMP 或者 Rabin-Karp 算法,在使用 Rabin-Karp算法的时候,要注意串中可能有负值。

这里给出用 KMP 判断的代码实现。

class Solution {
public:
vector <int> sOrder, tOrder;
int maxElement, lNull, rNull;
// 获取树中的最大值.
void getMaxElement(TreeNode *node) {
if (!node) return;
maxElement = max(maxElement, node->val);
getMaxElement(node->left);
getMaxElement(node->right);
}
//深度优先搜索遍历
void getDfsOrder(TreeNode *node, vector <int> &tar) {
if (!node) return;
tar.push_back(node->val);
if (node->left) getDfsOrder(node->left, tar);
else tar.push_back(lNull);
if (node->right) getDfsOrder(node->right, tar);
else tar.push_back(rNull);
}

bool kmp() {
int sLen = sOrder.size(), tLen = tOrder.size();
vector <int> next(tOrder.size(), -1);
// 生成模式串跳转数组
for (int i = 1, j = -1; i < tLen; ++i) {
while (j != -1 && tOrder[i] != tOrder[j + 1])
j = next[j];
if (tOrder[i] == tOrder[j + 1]) ++j;
next[i] = j;
}
for (int i = 0, j = -1; i < sLen; ++i) {
while (j != -1 && sOrder[i] != tOrder[j + 1])
j = next[j];
if (sOrder[i] == tOrder[j + 1]) ++j;
if (j == tLen - 1) return true;
}
return false;
}

bool isSubtree(TreeNode* s, TreeNode* t) {
// 初始化lNull和rNull, 为深度遍历做准备
maxElement = INT_MIN;
getMaxElement(s);
getMaxElement(t);
lNull = maxElement + 1;
rNull = maxElement + 2;

getDfsOrder(s, sOrder);
getDfsOrder(t, tOrder);

return kmp();
}
};

Loading Comments...