您的位置:首页 > Web前端 > Node.js

Leetcode 117 Populating Next Right Pointers in Each Node II 二叉树填充next指针指向右侧结点 II

2015-07-01 16:36 831 查看

原题地址

https://leetcode.com/problems/populating-next-right-pointers-in-each-node-ii/

题目描述

Follow up for problem “Populating Next Right Pointers in Each Node”.

116题树填充next指针指向右侧结点的的延伸。

What if the given tree could be any binary tree? Would your previous solution still work?

如果给出的树可以是任意树呢?你之前给出的解决方法还能正常工作吗?

Note:

注意:

You may only use constant extra space.

只能使用常数级的额外空间。

For example,

例如,

Given the following perfect binary tree,

给出如下完全树,

1
/  \
2    3
/ \    \
4  5     7


After calling your function, the tree should look like:

在调用方法之后,树应该看起来像下面这样:

1 -> NULL
/  \
2 -> 3 -> NULL
/ \    \
4-> 5 -> 7 -> NULL


解题思路

116题的解题过程请见Leetcode 116 Populating Next Right Pointers in Each Node 树填充next指针指向右侧结点

116题是117题一个特例,特殊在树的结构上。116题中树是一个完全树,在那种特殊情况下我们可以很轻松的找到一个递归解决方案。然而对于任意一棵树,那样的方案是行不通的。

经过观察和思考,我大概想到了三种可能的方案。

方案一

使用额外空间,用队列什么的。然而题目要求只能使用常数级的额外空间。所以此方案不可。

方案二

依然按照116题相似的思路。即:对于某一个树的根结点来说,将其左右子树的每一层的边界结点连接起来,然后递归处理左右子树。这个过程大概看起来是这样子的:

procedure connect(root)

for every level l of root

—-find rightest node leftNode in level l of root->left

—-find leftest node rightNode in level l of root->right

—-leftNode->next = rightNode

end for

call connect(root->left)

call connect(root->right)

在上述过程中,较116题中有所变化的是找到某一层的边界结点的过程。由于116题中树结构的特殊性,找到边界点的过程比较简单,然而对于任意一个树来说,这个过程就变得比较麻烦了。因此次方案不是不可行,也符合使用常数级的额外空间的要求,但是时间复杂度过高。

实际上,我一开始实现了这种解决方案,但是此处就不具体描述了,有兴趣的可以看一下代码

方案三

第三种方案还是挺好理解的。请看如下解释。

我们按层序来处理树。假设我们有如下一棵树:
1
/ \
2   3
/ \   \
4   5   6
/   / \   \
7   8   9  10
\         /
11       12
/
13

(1)假设某一时间点上,我们已经处理好了前3层,如下:
1->null
/ \
2-> 3->null
/ \   \
4-> 5-> 6->null   <--- 第3层以前已经处理好了
/   / \   \
7   8   9  10       <--- 接下来要处理第4层
\         /
11       12
/
13

(2)我们可以发现,要处理第四层的各个结点,我们可以根据第三层这条横着的链表来处理。为了方便处理,我们把第三层和第四层拆出来处理。
4-> 5-> 6->null
/   / \   \
7   8   9  10

使用两个指针pre和next分别指向第四层中的两个结点,目的是要把pre->next和next连接起来。
我们从第3层的最左侧结点开始往右前进(设为p),然后根据p的左右孩子情况来更新pre和next的指向。过程如下:

(3)初始情况下:
p
↓
4-> 5-> 6->null
/   / \   \
7   8   9  10
↑
pre

(4)找到下一个next
p
↓
4-> 5-> 6->null
/   / \   \
7   8   9  10
↑  ↑
pre next

(5)连接pre和next
p
↓
4-> 5-> 6->null
/   / \   \
7-> 8   9  10
↑  ↑
pre next

(6)移动p、pre和next,并连接
p
↓
4-> 5-> 6->null
/   / \   \
7-> 8-> 9  10
↑  ↑
pre next

(7)移动p、pre和next,并连接
p
↓
4-> 5-> 6->null
/   / \   \
7-> 8-> 9->10->null
↑  ↑
pre next

这样,我们处理好第四层之后,可以再根据第四层横向的单链表来处理第五层,直至所有层都处理结束。


这种方案满足使用常数级额外空间的要求,空间复杂度O(c);同时也是one-pass,时间复杂度O(n)。

代码

/**
* 填充树的next指针
* input root : 树某一层的最左侧结点
*/
void connect(struct TreeLinkNode *root) {
if (root == NULL) return;

struct TreeLinkNode *begin = NULL, *upperNode = root, *pre;

/* 找到当前层的第一个结点 */
while (upperNode != NULL) {
if (upperNode->left != NULL) {
begin = upperNode->left;
break;
}
else if (upperNode->right != NULL) {
begin = upperNode->right;
break;
}
else upperNode = upperNode->next;
}

/* 当前层没有任何结点,直接返回 */
if (begin == NULL) return;

/* 为了方便统一处理,判断是否需要处理当前upperNode指向的结点 */
pre = begin;
if (begin == upperNode->left && upperNode->right != NULL) {
pre->next = upperNode ->right;
pre = pre->next;
}
upperNode = upperNode->next; // upperNode指向下一个结点

/* 处理上一层中剩余的所有结点 */
while ( upperNode != NULL) {
if(upperNode->left != NULL) { // 如果结点的left不为空,则连接
pre->next = upperNode->left;
pre = pre->next;
}
if (upperNode->right != NULL) { // 如果结点的right不为空,则连接
pre->next = upperNode->right;
pre = pre->next;
}
upperNode = upperNode->next; // upperNode指向下一个结点
}

/* 递归处理下一层 */
connect(begin);
}


完整代码 https://github.com/Orange1991/leetcode/blob/master/117/c/solution2.c

运行结果

Lauguage  Status  Time  Comment
c         Accept  48ms  Violence
c         Accept  8ms
cpp       Accept  40ms


2015/7/1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: