前言
数(据结构)算(法)再回顾,加深记忆抗遗忘。
前序遍历
144. 二叉树的前序遍历
基本规律:二叉树前序遍历结果一定是[root, left, right]
1 2 3 4 5 6 7 8 9
| var preorderTraversal = function (root) { if (!root) return [];
const r1 = preorderTraversal(root.left); const r2 = preorderTraversal(root.right);
return [root.val].concat(r1).concat(r2); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var preorderTraversal = function (root) { const stack = []; const result = [];
while (root || stack.length) { while (root) { stack.push(root); result.push(root.val); root = root.left; } const node = stack.pop(); root = node.right; }
return result; };
|
中序遍历
94. 二叉树的中序遍历
基本规律:二叉树中序遍历结果一定是[left, root, right]
1 2 3 4 5 6 7 8 9
| var inorderTraversal = function (root) { if (!root) return [];
const r1 = inorderTraversal(root.left); const r2 = inorderTraversal(root.right);
return r1.concat([root.val]).concat(r2); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var inorderTraversal = function (root) { const stack = []; const result = [];
while (root || stack.length) { while (root) { stack.push(root); root = root.left; } const node = stack.pop(); result.push(node.val); root = node.right; }
return result; };
|
后序遍历
145. 二叉树的后序遍历
基本规律:二叉树后序遍历结果一定是[left, right, root]
1 2 3 4 5 6 7 8 9
| var postorderTraversal = function (root) { if (!root) return [];
const r1 = postorderTraversal(root.left); const r2 = postorderTraversal(root.right);
return r1.concat(r2).concat([root.val]); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var postorderTraversal = function (root) { const stack = []; const result = []; let prevTraversedNode = null;
while (root || stack.length) { while (root) { stack.push(root); root = root.left; }
const node = stack.pop(); if (!node.right || node.right === prevTraversedNode) { result.push(node.val); prevTraversedNode = node; } else { stack.push(node); root = node.right; } }
return result; };
|
前中后序遍历总结
递归实现只需记住返回结果时的规律:
- 前:[root, left, right]
- 中:[left, root, right]
- 后:[left, right, root]
同理,该规律也可以用来解决这类经典问题:已知中序 + 前 or 后序遍历结果,重建二叉树;
迭代实现记住先左后右,然后注意何时取值;
- 前:沿左遍历时取值;
- 中:回溯到父节点时取值;
- 后:回溯到父节点时:
- 如果右子树不存在或已经遍历过,此时再取值;
- 如果右子树存在且没有遍历过,之前 pop 的节点要再 push 回去,因为此时还不能取值;
层序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| var levelOrder = function (root) { const queue = [root]; const result = []; while (root && queue.length) { const layer = []; let len = queue.length; while (len) { const node = queue.pop(); layer.push(node.val); node.left && queue.unshift(node.left); node.right && queue.unshift(node.right); len--; } result.push(layer); }
return result; };
var levelOrderBottom = function (root) { const queue = [root]; const result = [];
while (root && queue.length) { const layer = []; let len = queue.length; while (len) { const node = queue.pop(); layer.push(node.val); node.left && queue.unshift(node.left); node.right && queue.unshift(node.right); len--; }
result.push(layer); }
return result.reverse(); };
var zigzagLevelOrder = function (root) { const queue = [root]; const result = []; let shouldReverse = false;
while (root && queue.length) { let len = queue.length; const layer = []; while (len) { const node = queue.pop(); layer.push(node.val); node.left && queue.unshift(node.left); node.right && queue.unshift(node.right); len--; }
result.push(shouldReverse ? layer.reverse() : layer); shouldReverse = !shouldReverse; }
return result; };
|
根据遍历结果重建二叉树
此类问题只有两种:
原理是前中后序遍历所得序列符合固定规则,而且重点在于,任意子树对应的序列也符合该规则:
- 前:[root, left, right]
- 中:[left, root, right]
- 后:[left, right, root]
这也是数据结构考研题型中经常出现的一类问题,从上述规则可以总结出:
- 前序数组的首元素是根节点;
- 中序数组的根节点对应元素的左右两侧分别是左右子树;
- 后序数组的尾元素是根节点;
综上,通过前或后序确定根节点,通过中序确定左右子树节点个数,通过节点个数切分前或后序数组;
106 从中序与后序遍历序列构造二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function buildTree(inorder: number[], postorder: number[]): TreeNode | null { if (inorder.length === 0) { return null; } if (inorder.length === 1) { return new TreeNode(inorder[0], null, null); } const root = postorder[postorder.length - 1]; const rootIndex = inorder.findIndex((v) => v === root); const leftInorder = inorder.slice(0, rootIndex); const rightInorder = inorder.slice(rootIndex + 1); const leftPostorder = postorder.slice(0, leftInorder.length); const rightPostorder = postorder.slice( leftInorder.length, postorder.length - 1 ); const leftChild = buildTree(leftInorder, leftPostorder); const rightChild = buildTree(rightInorder, rightPostorder); return new TreeNode(root, leftChild, rightChild); }
|
105 从前序与中序遍历序列构造二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function buildTree(preorder: number[], inorder: number[]): TreeNode | null { if (inorder.length === 0) { return null; } if (inorder.length === 1) { return new TreeNode(inorder[0], null, null); } const root = preorder[0]; const rootIndexOfInorder = inorder.findIndex((v) => v === root); const leftInorder = inorder.slice(0, rootIndexOfInorder); const rightInorder = inorder.slice(rootIndexOfInorder + 1); const leftPreorder = preorder.slice(1, 1 + leftInorder.length); const rightPreorder = preorder.slice(1 + leftInorder.length); const leftChild = buildTree(leftPreorder, leftInorder); const rightChild = buildTree(rightPreorder, rightInorder); return new TreeNode(root, leftChild, rightChild); }
|
注意 Array.prototype.slice(start, end) 的用法即可,切分出的数组不包含下标为 end 的元素,且 end 默认为数组的长度。