參考:https://www.cnblogs.com/blzm742624643/p/10021388.html
一、算法介紹
Morris算法充分利用了二叉樹葉子結點下的空間,從而可以在時間復雜度為O(N),空間復雜度為O(1)的條件下,前中后序遍歷二叉樹(不是完全二叉樹也可以使用)。
而常見的遍歷二叉樹的方法為遞歸和棧迭代,這兩種方法的時間復雜度雖然也為O(N),但是空間復雜度需要O(N),因此Morris算法可以極大節省空間。
二、算法原理
首先來到當前結點記為cur。
1.如果cur無左孩子,cur向右移動。
2.如果cur有左孩子,那么找到cur左子樹的最右的結點,記為mostRight。
2.1如果mostRight的right指針指向空,則讓其指向cur,然后cur向左移動
2.2如果mostRight的right指針指向cur,讓其指回空,然后cur向右移動。
三、算法實現
困難之處在於在遍歷的子結點的時候如何重新返回其父結點?在Morris遍歷算法中,通過修改葉子結點的左右空指針來指向其前驅或者后繼結點來實現的。
1. 中序遍歷
如果當前結點pNode的左孩子為空,那么輸出該結點,並把該結點的右孩子作為當前結點;
如果當前結點pNode的左孩子非空,那么就找出該結點在中序遍歷中的前驅結點pPre
當第一次訪問該前驅結點pPre時,其右孩子必定為空,那么就將其右孩子設置為當前結點,以便根據這個指針返回到當前結點pNode中,並將當前結點pNode設置為其左孩子;
當該前驅結點pPre的右孩子為當前結點,那么就輸出當前結點,並把前驅結點的右孩子設置為空(恢復樹的結構),將當前結點更新為當前結點的右孩子
重復以上兩步,直到當前結點為空。
var inOrder3= function (root) { if(root === null) return; let cur = root; let res = []; while(cur){ if(cur.left === null){ res.push(cur.val); cur = cur.right; }else{ let node = cur.left; while(node!==null && node.right !== null && node.right !== cur){ node=node.right; } if(node.right === null){ node.right = cur; cur = cur.left; }else{ node.right = null; res.push(cur.val); cur = cur.right; } } } return res; };
2.前序遍歷
與中序遍歷類似,區別僅僅是輸出的順序不同。
var preOrder3= function (root) { if(root == null) return; let cur = root; let res = []; while(cur){ if(cur.left === null){ res.push(cur.val); cur = cur.right; }else{ let node = cur.left; while(node!==null && node.right !== null && node.right !== cur){ node=node.right; } if(node.right === null){ res.push(cur.val); node.right = cur; cur = cur.left; }else{ node.right = null; cur = cur.right; } } } return res; };
3.后序遍歷
var postOrder3 = function (root) { if (root === null) return []; let cur = root; let res = []; var addPath=(node) =>{ let arr = []; while(node != null){ arr.push(node.val); node = node.right; } return arr.reverse(); } while (cur) { let node = cur.left; if (node !== null) { while (node !== null && node.right !== null && node.right !== cur) { node = node.right; } if (node.right === null) { node.right = cur; cur = cur.left; continue; } else { node.right = null; res.push(...addPath(cur.left)); } } cur = cur.right; } res.push(...addPath(root)); return res; };