复习Morris遍历
一般来讲,我们在进行树的遍历的时候
会进行递归
它的时间复杂度是O(N²),空间复杂度O(N)
那么有没有一套从天而降的掌法能优化它的复杂度呢?
有
它的名字叫 MorrisTraversal
它用几个变量就遍历完整棵树,使空间复杂度降为O(1)
Morris遍历图示:
Morris遍历的次数:
树的递归遍历,每一个节点有三个操作时机,分别在:
//第一个时机
recursion(node.left)
//第二个时机
recursion(node.right)
//第三个时机
也就是会来到每个节点三次
而Morris遍历会来到有左树的节点两次,叶子节点一次
Morris遍历的步骤:
- 判断是否有左节点,没有cur右移
- 有左节点,取到左节点的最右节点
判断最右节点的右指针指向:- 若指向null,则让它指向cur,cur左移
- 若指向cur,则让它指向null,cur右移
- 当 cur 为 null 时,游戏结束(这个时候cur来到了最右位置,遍历完整棵树)
Morris遍历实现:
通用代码实现:
const morrisTraversal = (root) => {
let cur = root
let res = []
while (cur) {
let mostRight = cur.left
if (mostRight) {
while (mostRight.right !== null && mostRight.right !== cur) {
mostRight = mostRight.right
}
if (mostRight.right === null) {
//第一次访问有左树节点
mostRight.right = cur
cur = cur.left
continue
} else {
//第二次访问有左树节点
//第一次访问叶子节点
mostRight.right = null
}
}
//第二次访问有左树节点
//第一次访问叶子节点
//cur左为null
cur = cur.right
}
return res
}
前序遍历:
//前序遍历
const morrisTraversal = (root) => {
let cur = root
let res = []
while (cur) {
let mostRight = cur.left
if (mostRight) {
while (mostRight.right !== null && mostRight.right !== cur) {
mostRight = mostRight.right
}
if (mostRight.right === null) {
res.push(cur.val)
mostRight.right = cur
cur = cur.left
continue
} else {
mostRight.right = null
}
} else {
res.push(cur.val)
}
cur = cur.right
}
return res
}
中序遍历
//中序遍历
const morrisTraversal = (root) => {
let cur = root
let res = []
while (cur) {
let mostRight = cur.left
if (mostRight) {
while (mostRight.right !== null && mostRight.right !== cur) {
mostRight = mostRight.right
}
if (mostRight.right === null) {
mostRight.right = cur
cur = cur.left
continue
} else {
mostRight.right = null
}
}
res.push(cur.val)
cur = cur.right
}
return res
}
后序遍历
这个比较有意思,比前面两个需要多加两个函数
它们的功能是:
printEdge:反向打印树的一条边,那么就需要 反转 打印 然后再反转回去
reverseEdge:实现边的反转
const printEdge = (node) => {
let tail = reverseEdge(node)
let res = []
node = tail
while (node) {
res.push(node.val)
node = node.right
}
reverseEdge(tail)
return res
}
const reverseEdge = (node) => {
let pre = null
while (node) {
let next = node.right
node.right = pre
pre = node
node = next
}
return pre
}
const morrisTraversal = (root) => {
let cur = root
let res = []
while (cur) {
let mostRight = cur.left
if (mostRight) {
while (mostRight.right !== null && mostRight.right !== cur) {
mostRight = mostRight.right
}
if (mostRight.right === null) {
mostRight.right = cur
cur = cur.left
continue
} else {
mostRight.right = null
//第二次来到有左树节点打印其左子节点及其右边
res.push(...printEdge(cur.left))
}
}
cur = cur.right
}
//最后还剩下根节点及其右边没打,给它打印了
res.push(...printEdge(root))
return res
}