探索二叉树

探索二叉树

是一种经常用到的数据结构,用来模拟具有树状结构性质的数据集合。

树里的每一个节点有一个根植和一个包含所有子节点的列表。从图的观点来看,树也可视为一个拥有N 个节点N-1 条边的一个有向无环图。

二叉树是一种更为典型的树树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。

前序遍历


前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树

请看下面的例子:

在这里插入图片描述

中序遍历


中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树

后序遍历


后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点

值得注意的是,当你删除树中的节点时,删除过程将按照后序遍历的顺序进行。 也就是说,当你删除一个节点时,你将首先删除它的左节点和它的右边的节点,然后再删除节点本身。

如果你想对这棵树进行后序遍历,使用栈来处理表达式会变得更加容易。 每遇到一个操作符,就可以从栈中弹出栈顶的两个元素,计算并将结果返回到栈中。

层序遍历 - 介绍

层序遍历就是逐层遍历树结构。

广度优先搜索是一种广泛运用在树或图这类数据结构中,遍历或搜索的算法。 该算法从一个根节点开始,首先访问节点本身。 然后遍历它的相邻节点,其次遍历它的二级邻节点、三级邻节点,以此类推。

当我们在树中进行广度优先搜索时,我们访问的节点的顺序是按照层序遍历顺序的。

运用递归解决树的问题

递归是解决树的相关问题最有效和最常用的方法之一。

树可以以递归的方式定义为一个节点(根节点),它包括一个值和一个指向其他节点指针的列表。 递归是树的特性之一。 因此,许多树问题可以通过递归的方式来解决。 对于每个递归层级,我们只能关注单个节点内的问题,并通过递归调用函数来解决其子节点问题。

通常,我们可以通过 “自顶向下” 或 “自底向上” 的递归来解决树问题。

“自顶向下” 的解决方案

“自顶向下” 意味着在每个递归层级,我们将首先访问节点来计算一些值,并在递归调用函数时将这些值传递到子节点。 所以 “自顶向下” 的解决方案可以被认为是一种前序遍历。 具体来说,递归函数 top_down(root, params) 的原理是这样的:

1. return specific value for null node
2. update the answer if needed                      // anwer <-- params
3. left_ans = top_down(root.left, left_params)      // left_params <-- root.val, params
4. right_ans = top_down(root.right, right_params)   // right_params <-- root.val, params
5. return the answer if needed                      // answer <-- left_ans, right_ans

例如,思考这样一个问题:给定一个二叉树,请寻找它的最大深度。

我们知道根节点的深度是1。 对于每个节点,如果我们知道某节点的深度,那我们将知道它子节点的深度。 因此,在调用递归函数的时候,将节点的深度传递为一个参数,那么所有的节点都知道它们自身的深度。 而对于叶节点,我们可以通过更新深度从而获取最终答案。 这里是递归函数 maximum_depth(root, depth) 的伪代码:

1. return if root is null
2. if root is a leaf node:
3.      answer = max(answer, depth)         // update the answer if needed
4. maximum_depth(root.left, depth + 1)      // call the function recursively for left child
5. maximum_depth(root.right, depth + 1)     // call the function recursively for right child

以下的例子可以帮助你理解它是如何工作的:

int answer;		       // don't forget to initialize answer before call maximum_depth
void maximum_depth(TreeNode* root, int depth) {
    if (!root) {
        return;
    }
    if (!root->left && !root->right) {
        answer = max(answer, depth);
    }
    maximum_depth(root->left, depth + 1);
    maximum_depth(root->right, depth + 1);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-idIouR7Y-1589513500589)(C:\Users\SHERRY~1\AppData\Local\Temp\1589338059349.png)]

“自底向上” 的解决方案

“自底向上” 是另一种递归方法。 在每个递归层次上,我们首先对所有子节点递归地调用函数,然后根据返回值和根节点本身的值得到答案。 这个过程可以看作是后序遍历的一种。 通常, “自底向上” 的递归函数 bottom_up(root) 为如下所示:

1. return specific value for null node
2. left_ans = bottom_up(root.left)          // call function recursively for left child
3. right_ans = bottom_up(root.right)        // call function recursively for right child
4. return answers                           // answer <-- left_ans, right_ans, root.val

让我们继续讨论前面关于树的最大深度的问题,但是使用不同的思维方式:对于树的单个节点,以节点自身为根的子树的最大深度x是多少?

如果我们知道一个根节点,以其子节点为根的最大深度为l和以其子节点为根的最大深度为r,我们是否可以回答前面的问题? 当然可以,我们可以选择它们之间的最大值,再加上1来获得根节点所在的子树的最大深度。 那就是 x = max(l,r)+ 1

这意味着对于每一个节点来说,我们都可以在解决它子节点的问题之后得到答案。 因此,我们可以使用“自底向上“的方法。下面是递归函数 maximum_depth(root) 的伪代码:

1. return 0 if root is null                 // return 0 for null node
2. left_depth = maximum_depth(root.left)
3. right_depth = maximum_depth(root.right)
4. return max(left_depth, right_depth) + 1  // return depth of the subtree rooted at root

以下的例子可以帮助你理解它是如何工作的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0lLSyrc-1589513500589)(C:\Users\SHERRY~1\AppData\Local\Temp\1589513301516.png)]

int maximum_depth(TreeNode* root) {
	if (!root) {
		return 0;                                 // return 0 for null node
	}
	int left_depth = maximum_depth(root->left);
	int right_depth = maximum_depth(root->right);
	return max(left_depth, right_depth) + 1;	  // return depth of the subtree rooted at root
}

总结

了解递归并利用递归解决问题并不容易。

当遇到树问题时,请先思考一下两个问题:

  1. 你能确定一些参数,从该节点自身解决出发寻找答案吗?
  2. 你可以使用这些参数和节点本身的值来决定什么应该是传递给它子节点的参数吗?

如果答案都是肯定的,那么请尝试使用 “自顶向下” 的递归来解决此问题。

并利用递归解决问题并不容易。

当遇到树问题时,请先思考一下两个问题:

  1. 你能确定一些参数,从该节点自身解决出发寻找答案吗?
  2. 你可以使用这些参数和节点本身的值来决定什么应该是传递给它子节点的参数吗?

如果答案都是肯定的,那么请尝试使用 “自顶向下” 的递归来解决此问题。

或者你可以这样思考:对于树中的任意一个节点,如果你知道它子节点的答案,你能计算出该节点的答案吗? 如果答案是肯定的,那么 “自底向上” 的递归可能是一个不错的解决方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章