|
| 1 | +# 题目描述(中等难度) |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +给定一个满二叉树,每个节点多了一个`next`指针,然后将所有的`next`指针指向它的右边的节点。并且要求空间复杂度是`O(1)`。 |
| 8 | + |
| 9 | +# 解法一 BFS |
| 10 | + |
| 11 | +如果没有要求空间复杂度这道题就简单多了,我们只需要用一个队列做`BFS`,`BFS`参见 [102 题](<https://leetcode.wang/leetcode-102-Binary-Tree-Level-Order-Traversal.html>)。然后按顺序将每个节点连起来就可以了。 |
| 12 | + |
| 13 | +```java |
| 14 | +public Node connect(Node root) { |
| 15 | + if (root == null) { |
| 16 | + return root; |
| 17 | + } |
| 18 | + Queue<Node> queue = new LinkedList<Node>(); |
| 19 | + queue.offer(root); |
| 20 | + while (!queue.isEmpty()) { |
| 21 | + int size = queue.size(); |
| 22 | + Node pre = null; |
| 23 | + for (int i = 0; i < size; i++) { |
| 24 | + Node cur = queue.poll(); |
| 25 | + //从第二个节点开始,将前一个节点的 pre 指向当前节点 |
| 26 | + if (i > 0) { |
| 27 | + pre.next = cur; |
| 28 | + } |
| 29 | + pre = cur; |
| 30 | + if (cur.left != null) { |
| 31 | + queue.offer(cur.left); |
| 32 | + } |
| 33 | + if (cur.right != null) { |
| 34 | + queue.offer(cur.right); |
| 35 | + } |
| 36 | + |
| 37 | + } |
| 38 | + } |
| 39 | + return root; |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +# 解法二 迭代 |
| 44 | + |
| 45 | +当然既然题目要求了空间复杂度,那么我们来考虑下不用队列该怎么处理。只需要解决三个问题就够了。 |
| 46 | + |
| 47 | +* 每一层怎么遍历? |
| 48 | + |
| 49 | + 之前是用队列将下一层的节点保存了起来。 |
| 50 | + |
| 51 | + 这里的话,其实只需要提前把下一层的`next`构造完成,到了下一层的时候就可以遍历了。 |
| 52 | + |
| 53 | +* 什么时候进入下一层? |
| 54 | + |
| 55 | + 之前是得到当前队列的元素个数,然后遍历那么多次。 |
| 56 | + |
| 57 | + 这里的话,注意到最右边的节点的`next`为`null`,所以可以判断当前遍历的节点是不是`null`。 |
| 58 | + |
| 59 | +* 怎么得到每层开头节点? |
| 60 | + |
| 61 | + 之前队列把当前层的所以节点存了起来,得到开头节点当然很容易。 |
| 62 | + |
| 63 | + 这里的话,我们额外需要一个变量把它存起来。 |
| 64 | + |
| 65 | +三个问题都解决了,就可以写代码了。利用三个指针,`start` 指向每层的开始节点,`cur`指向当前遍历的节点,`pre`指向当前遍历的节点的前一个节点。 |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +如上图,我们需要把 `pre` 的左孩子的 `next` 指向右孩子,`pre` 的右孩子的`next`指向`cur`的左孩子。 |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +如上图,当 `cur` 指向 `null` 以后,我们只需要把 `pre` 的左孩子的 `next` 指向右孩子。 |
| 74 | + |
| 75 | +```java |
| 76 | +public Node connect(Node root) { |
| 77 | + if (root == null) { |
| 78 | + return root; |
| 79 | + } |
| 80 | + Node pre = root; |
| 81 | + Node cur = null; |
| 82 | + Node start = pre; |
| 83 | + while (pre.left != null) { |
| 84 | + //遍历到了最右边的节点,要将 pre 和 cur 更新到下一层,并且用 start 记录 |
| 85 | + if (cur == null) { |
| 86 | + //我们只需要把 pre 的左孩子的 next 指向右孩子。 |
| 87 | + pre.left.next = pre.right; |
| 88 | + |
| 89 | + pre = start.left; |
| 90 | + cur = start.right; |
| 91 | + start = pre; |
| 92 | + //将下一层的 next 连起来,同时 pre、next 后移 |
| 93 | + } else { |
| 94 | + //把 pre 的左孩子的 next 指向右孩子 |
| 95 | + pre.left.next = pre.right; |
| 96 | + //pre 的右孩子的 next 指向 cur 的左孩子。 |
| 97 | + pre.right.next = cur.left; |
| 98 | + |
| 99 | + pre = pre.next; |
| 100 | + cur = cur.next; |
| 101 | + } |
| 102 | + } |
| 103 | + return root; |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +分享下 `leetcode` 的高票回答的代码,看起来更简洁一些,`C++` 写的。 |
| 108 | + |
| 109 | +```C++ |
| 110 | +void connect(TreeLinkNode *root) { |
| 111 | + if (root == NULL) return; |
| 112 | + TreeLinkNode *pre = root; |
| 113 | + TreeLinkNode *cur = NULL; |
| 114 | + while(pre->left) { |
| 115 | + cur = pre; |
| 116 | + while(cur) { |
| 117 | + cur->left->next = cur->right; |
| 118 | + if(cur->next) cur->right->next = cur->next->left; |
| 119 | + cur = cur->next; |
| 120 | + } |
| 121 | + pre = pre->left; |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | +
|
| 126 | +我的代码里的变量和他的变量对应关系如下。 |
| 127 | +
|
| 128 | +```java |
| 129 | +我的 start pre cur |
| 130 | + | | | |
| 131 | +他的 pre cur cur.next |
| 132 | +``` |
| 133 | + |
| 134 | +除了变量名不一样,算法本质还是一样的。 |
| 135 | + |
| 136 | +# 总 |
| 137 | + |
| 138 | +题目让我们初始化 `next` 指针,初始化过程中我们又利用到了`next`指针,很巧妙了。 |
0 commit comments