一、概述
1.二叉树定义:
每个节点最多2个子树,且有左右之分
2.遍历方式:
深度优先遍历:前序遍历,中序遍历,后序遍历 (深度优先遍历,利用Stack的先进后出的特性,注意栈的出栈顺序和入栈顺序相反) 广度优先遍历:层序遍历(广度优先遍历,利用Queue的先进先出的特性)
3.遍历实现方式:
递归遍历,颜色标记迭代遍历,常规迭代遍历,Morris遍历,
二、原理及代码实现
*深度优先遍历:
1.标记迭代遍历:
1.1 颜色标记
Leetcode上的高赞题解。值得一提,能统一3种深度优先遍历的迭代的实现代码,只用按需调用入栈顺序即可。参考链接 简单说下思路: 用颜色标记节点状态,新节点为白色,已访问的节点为灰色。 如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点(这里是以中序遍历为例,前序和后序类比分别按需入栈)依次入栈。 如果遇到的节点为灰色,则将节点的值输出。
enum Color{
White, // 新节点(第一次入栈)
Grey // 已被访问过的节点(已完成一次入栈和出栈)
}
type MarkedNode={
color:Color,
node:TreeNode
}
function inorderTraversal(root:TreeNode):number[]{
if(!root) return [];
const res:number[]=[];
const stack:[[Color,TreeNode]]=[[Color.White,root]];
while(stack.length>0){
const [color,node]=stack.pop();
if(!node) continue;
if(color===Color.White){
stack.push([Color.White,node.right]);
stack.push([Color.Grey,node]);
stack.push([Color.White,node.left]);
}else{
res.push(node.val);
}
}
return res;
}
1.2 数据类型标记
然后,就这个解法,有一个拓展,不用颜色标记,而是直接巧妙的用另一种标识方式,也即入栈的数据类型。
思路如下: 维护一个栈,新节点将节点入栈,已访问的节点将节点的val入栈。 如果遇到的item为number类型,则将item直接输出。 若遇到的item不是number类型,则右子节点、自身、左子节点(这里是以中序遍历为例,前序和后续分别按需入栈)依次入栈。
function inorderTraversal4(root:TreeNode):number[]{
if(!root) return [];
const res:number[]=[];
const stack:(number|TreeNode|null)[]=[];
stack.push(root);
while(stack.length>0){
const item=stack.pop();
if(item===null||item===undefined) continue;
if(typeof item !== 'number'){
stack.push(item.right);
stack.push(item.val);
stack.push(item.left)
}else{
res.push(item);
}
}
return res;
}
2.递归遍历
递归最简单,直接上代码
2.1. 前序遍历:
function preorderTraversal(root: TreeNode|null):number[]{
const res:number[]=[];
recur(root,res);
return res;
function recur(root:TreeNode|null,res:number[]){
if(!root) return null;
res.push(root.val);
root.left&&recur(root.left,res);
root.right&&recur(root.right,res)
}
}
2.2 中序遍历:
function midorderTraversal(root: TreeNode|null):number[]{
const res:number[]=[];
recur(root,res);
return res;
function recur(root:TreeNode|null,res:number[]){
if(!root) return null;
root.left&&recur(root.left,res);
res.push(root.val);
root.right&&recur(root.right,res)
}
}
2.3 后序方式:
function postorderTraversal(root: TreeNode|null):number[]{
const res:number[]=[];
recur(root,res);
return res;
function recur(root:TreeNode|null,res:number[]){
if(!root) return null;
root.left&&recur(root.left,res);
root.right&&recur(root.right,res);
res.push(root.val);
}
}
3. 迭代方式
利用栈的特性。
3.1 前序遍历:
function preorderTraversal(root: TreeNode|null):number[]{
if(!root) return [];
const res:number[]=[];
const stack:TreeNode[]=[];
stack.push(root);
while(stack.length>0){
const node=stack.pop();
node&&res.push(node.val);
node?.right&&stack.push(node?.right); // 注意这里后left,这样下层循环left先出
node?.left&&stack.push(node?.left);
}
return res;
}
3.2 中序遍历:
function inorderTraversal(root: TreeNode|null):number[]{
if(!root) return [];
const res:number[]=[];
const stack:(TreeNode|null)[]=[];
let curr=root;
while(stack.length>0||!!curr){
while(!!curr){
stack.push(curr);
curr=curr.left;
}
const node:TreeNode=stack.pop();
if(!node) continue;
res.push(node.val);
if(node.right){
curr=node.right;
}
}
return res;
}
3.3 后序遍历:
迭代方式:后进先出,双栈处理, stack1左右中进栈 => stack2中右左进栈
function postOrderTraversal1(root: TreeNode|null):number[]{
if(!root) return [];
const res:number[]=[];
const stack1:TreeNode[]=[];
const stack2:TreeNode[]=[]; // 中右左进栈
stack1.push(root);
while(stack1.length>0){
const node=stack1.pop();
node&&stack2.push(node);
node?.left&&stack1.push(node?.left);
node?.right&&stack1.push(node?.right);
}
while(stack2.length>0){
const node=stack2.pop();
node&&res.push(node.val);
}
return res;
}
4. Morris遍历(待续)
*广度优先遍历
4. 层序遍历
迭代方式:利用queue的先进先出的的特性来实现。
// *****4.1 输出一维数组[]
function levelOrderTraversal1(root: TreeNode|null):number[]{
if(!root) return [];
const res:number[]=[];
const queue:TreeNode[]=[];
queue.push(root);
while(queue.length>0){
const node=queue.shift();
node&&res.push(node.val);
node?.left&&queue.push(node?.left);
node?.right&&queue.push(node?.right);
}
return res;
}
// *****4.2 按层输出二维数组[[]]
function levelOrderTraversal2(root: TreeNode|null):number[][]{
if(!root) return [];
const res:number[][]=[];
const queue:TreeNode[]=[];
queue.push(root);
while(queue.length>0){
let item:number[]=[];
for(let i=queue.length;i>0;i--){
const node=queue.shift();
node&&item.push(node.val);
node?.left&&queue.push(node?.left);
node?.right&&queue.push(node?.right);
}
res.push(item);
}
return res;
}