|
| 1 | +# 题目地址(面试题 17.12. BiNode) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/binode-lcci/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。 |
| 9 | +
|
| 10 | +返回转换后的单向链表的头节点。 |
| 11 | +
|
| 12 | +注意:本题相对原题稍作改动 |
| 13 | +
|
| 14 | + |
| 15 | +
|
| 16 | +示例: |
| 17 | +
|
| 18 | +输入: [4,2,5,1,3,null,6,0] |
| 19 | +输出: [0,null,1,null,2,null,3,null,4,null,5,null,6] |
| 20 | +提示: |
| 21 | +
|
| 22 | +节点数量不会超过 100000。 |
| 23 | +``` |
| 24 | + |
| 25 | +## 前置知识 |
| 26 | + |
| 27 | +- 二叉查找树 |
| 28 | +- 递归 |
| 29 | +- [二叉树的遍历](../thinkings/binary-tree-traversal.md) |
| 30 | + |
| 31 | +## 公司 |
| 32 | + |
| 33 | +- 暂无 |
| 34 | + |
| 35 | +## 思路 |
| 36 | + |
| 37 | +实际上这就是一个考察二叉树遍历 + 二叉查找树性质的题目。需要注意的是指针操作,这一点和链表反转系列题目是一样的。 |
| 38 | + |
| 39 | +首先我们要知道一个性质: 对于一个二叉查找树来说,其中序遍历结果是一个有序数组。 而题目要求你输出的恰好就是有序数组(虽然没有明说, 不过从测试用例也可以看出)。 |
| 40 | + |
| 41 | +因此一个思路就是中序遍历, 边遍历边改变指针即可。 这里有两个注意点: |
| 42 | + |
| 43 | +1. 指针操作小心死循环 |
| 44 | +2. 你需要返回的是最左下角的节点,而不是题目给的 root |
| 45 | + |
| 46 | +对于第一个问题, 其实只要注意操作指针的顺序即可。对于第二个问题,我用了一个黑科技,让代码看起来简洁又高效。如果不懂的话, 你也可以换个朴素的写法。 |
| 47 | + |
| 48 | +让我们进入正题。 |
| 49 | + |
| 50 | +其中绿色是我们要增加的连线,而黑色是是原本的连线。 |
| 51 | + |
| 52 | + |
| 53 | + |
| 54 | +我们再来看一个复杂一点的: |
| 55 | + |
| 56 | + |
| 57 | + |
| 58 | +实际上,不管多么复杂。 我们只需要进行一次**中序遍历**,同时记录前驱节点。然后操作修改前驱节点和当前节点的指针即可,整个过程就好像是链表反转。 |
| 59 | + |
| 60 | +核心代码(假设 pre 我们已经正确计算出了): |
| 61 | + |
| 62 | +```py |
| 63 | +cur.left = None |
| 64 | +pre.right = cur |
| 65 | +pre = cur |
| 66 | +``` |
| 67 | + |
| 68 | +剩下的就是如何计算 pre,这个也不难,直接看代码: |
| 69 | + |
| 70 | +```py |
| 71 | +self.pre = None |
| 72 | +def dfs(root): |
| 73 | + dfs(root.left) |
| 74 | + # 上面的指针改变逻辑写到这里 |
| 75 | + self.pre = root |
| 76 | + dfs(root.right) |
| 77 | + |
| 78 | +``` |
| 79 | + |
| 80 | +问题得以解决。 |
| 81 | + |
| 82 | +这里还有最后一个问题就是返回值,题目要返回的实际上是最左下角的值。而我用了一个黑科技的方法(注意看注释): |
| 83 | + |
| 84 | +```py |
| 85 | +self.pre = self.ans = TreeNode(-1) |
| 86 | +def dfs(root): |
| 87 | + if not root: return |
| 88 | + dfs(root.left) |
| 89 | + root.left = None |
| 90 | + self.pre.right = root |
| 91 | + # 当第一次执行到下面这一行代码,恰好是在最左下角, 这个时候 self.pre = root 就切断了 self.pre 和 self.ans 的联系 |
| 92 | + # 之后 self.pre 的变化都不会体现到 self.ans 上。 |
| 93 | + # 直观上来说就是 self.ans 在遍历到最左下角的时候下车了 |
| 94 | + # 因此最后返回 self.ans 即可 |
| 95 | + self.pre = root |
| 96 | + dfs(root.right) |
| 97 | +dfs(root) |
| 98 | +return self.ans.right |
| 99 | +``` |
| 100 | + |
| 101 | +## 关键点 |
| 102 | + |
| 103 | +- 指针操作 |
| 104 | +- 返回值的处理 |
| 105 | + |
| 106 | +## 代码 |
| 107 | + |
| 108 | +```py |
| 109 | +class Solution: |
| 110 | + def convertBiNode(self, root): |
| 111 | + self.pre = self.ans = TreeNode(-1) |
| 112 | + def dfs(root): |
| 113 | + if not root: return |
| 114 | + dfs(root.left) |
| 115 | + root.left = None |
| 116 | + self.pre.right = root |
| 117 | + self.pre = root |
| 118 | + dfs(root.right) |
| 119 | + dfs(root) |
| 120 | + return self.ans.right |
| 121 | + |
| 122 | +``` |
| 123 | + |
| 124 | +**复杂度分析** |
| 125 | + |
| 126 | +- 时间复杂度:$O(N)$,其中 N 为树的节点总数。 |
| 127 | +- 空间复杂度:$O(h)$,其中 h 为树的高度。 |
| 128 | + |
| 129 | +## 相关题目 |
| 130 | + |
| 131 | +- [206.reverse-linked-list](./206.reverse-linked-list.md) |
| 132 | +- [92.reverse-linked-list-ii](./92.reverse-linked-list-ii.md) |
| 133 | +- [25.reverse-nodes-in-k-groups-cn](./25.reverse-nodes-in-k-groups-cn.md) |
| 134 | + |
| 135 | +## 扩展 |
| 136 | + |
| 137 | +大家也可以关注我的公众号《脑洞前端》获取更多更新鲜的 LeetCode 题解 |
| 138 | + |
| 139 | + |
0 commit comments