最近在网上看到一帖子,一网友在理想汽车面试的时候,聊到二叉树的后序遍历,该网友说是:左->右->中,但面试官坚持说不是,把他给整蒙了。
我们这里来回顾一下二叉树的遍历方式,我们听过最多的就是前序遍历,中序遍历和后序遍历,其实这三种遍历方式都是根据根节点的位置来判断的。先遍历根节点就是先序遍历,最后遍历根节点就是后续遍历,如果根节点放在左右子节点中间就是中序遍历,如下所示:
前序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后序遍历:左子树->右子树->根节点
这三种遍历方式无论是哪种,都是左子树在右子树之前遍历。我们再来看下该网友的回复,他说的是左->右->中,这个中应该就是根节点,所以他说的是没问题的,但面试官坚持说不是,可以肯定一点的是这个面试官要么是水平有问题要么是故意找茬。
我们来看下其他网友的回复,一网友调侃说:你和面试官面对面,你俩左右是相反的。
还有的说面试官大脑内存可能存在的是左右根,你说的和他大脑内存存储的不匹配。
还有的说理想汽车和我争了两个问题,全是错的,又菜又犟。
其实这种情况我也遇到过类似的,至少遇到两次,明明我说的是对,面试官非说不对,但当时并没有给我整蒙,因为我知道我说的是对的,只是感到挺无语的,怎么会有这么二百五的面试官。
我之前也面试过很多人,如果真的遇到我的知识盲区的话,我绝对不会说一个错误的答案。知道就是知道,不知道就是不知道。明明自己是错的,非说自己是对的,不知道这种人是什么心态。
我们来看一下二叉树的后序遍历,写法也比较多,有递归的还有非递归的,具体可以看下《算法秘籍》的第4章。当然还有一种比较经典的写法就是 Morris遍历,我们前面也讲过,大家可以看下:Morris的后序遍历,这里就不在重复介绍。因为当时只提供了java语言的代码,这里再来提供其他语言的代码。
C++:
public:
vector<int> postorderTraversal(TreeNode *root) {
vector<int> posList;
TreeNode *cur = root;// 记录当前访问的节点。
while (cur) {
if (cur->left == nullptr) {// 左子节点为空不用管。
cur = cur->right;
} else {
TreeNode *pre = cur->left;
while (pre->right && pre->right != cur)
pre = pre->right;
if (pre->right == nullptr) {// 第一次到当前节点。
pre->right = cur;
cur = cur->left;
} else {// 第二次到当前节点。
pre->right = nullptr;
// 1,当前节点第二次访问的时候逆序打印。
printList(cur->left, posList);
cur = cur->right;
}
}
}
// 2,根节点往右的逆序要单独打印。
printList(root, posList);
return posList;
}
// 逆序打印
void printList(TreeNode *node, vector<int> &posList) {
int count = 0;
while (node) {
++count;
posList.emplace_back(node->val);
node = node->right;
}
// 反转
reverse(posList.end() - count, posList.end());
}
C:
// 逆序打印
void printList(struct TreeNode *node, int *res, int *returnSize) {
int count = 0;
while (node) {
++count;
res[(*returnSize)++] = node->val;
node = node->right;
}
// 反转
for (int i = (*returnSize) - count, j = (*returnSize) - 1; i < j; i++, j--) {
int tmp = res[i];
res[i] = res[j];
res[j] = tmp;
}
}
int *postorderTraversal(struct TreeNode *root, int *returnSize) {
*returnSize = 0;
// 题中树的节点的数目在范围 [0, 100] 内
int *res = malloc(sizeof(int) * 201);
struct TreeNode *cur = root;// 记录当前访问的节点。
while (cur) {
if (cur->left == NULL) {// 左子节点为空不用管。
cur = cur->right;
} else {
struct TreeNode *pre = cur->left;
while (pre->right && pre->right != cur)
pre = pre->right;
if (pre->right == NULL) {// 第一次到当前节点。
pre->right = cur;
cur = cur->left;
} else {// 第二次到当前节点。
pre->right = NULL;
// 1,当前节点第二次访问的时候逆序打印。
printList(cur->left, res, returnSize);
cur = cur->right;
}
}
}
// 2,根节点往右的逆序要单独打印。
printList(root, res, returnSize);
return res;
}
Python:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
posList = []
cur = root # 记录当前访问的节点。
while cur:
if not cur.left: # 左子节点为空不用管。
cur = cur.right
else:
pre = cur.left
while pre.right and pre.right != cur:
pre = pre.right
if not pre.right: # 第一次到当前节点。
pre.right = cur
cur = cur.left
else: # 第二次到当前节点。
pre.right = None
# 1,当前节点第二次访问的时候逆序打印。
self.printList(cur.left, posList)
cur = cur.right
# 2,根节点往右的逆序要单独打印。
self.printList(root, posList)
return posList
# 逆序打印
def printList(self, node, posList):
count = 0
while node:
count += 1
posList.append(node.val)
node = node.right
# 反转
length = len(posList)
posList[length - count:length] = posList[length - count:length][::-1]
笔者简介
博哥,真名:王一博,毕业十多年,《算法秘籍》作者,专注于数据结构和算法的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解700多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以下载我整理的1000多页的PDF算法文档。