[数据结构学习笔记19] 二叉树遍历(Binary Tree Traversal)
树是分等级的数据结构,遍历树有两种策略,一是广度优先,一是深度优先。
广度优先
a level 0
| |
b c level 1
| | | |
d e f g level 2
| |
h i level 3
广度优先,我们从level 0开始,每层节点访问完毕,接着往下一层。
a,b,c,d,e,f,g,h,i,基本是从上往下,从左往右,广度优先比较符合我们的正常思维。
深度优先
一直往左,往下,直到叶子节点,然后往回,访问另一个分支。
a,b,d,h(h是往下,往左的最后的叶子节点,开始回溯到d,访问d的右节点i),i(i访问完毕,接着回溯到b,访问b的右节点e),e(e访问完毕,接着回溯到a,访问a的右节点c),c(c有左孩子节点f),f(回溯到c,访问c右节点g),g
基本思路:
1. 访问root节点
2. 访问左边的子树
3. 访问右边的子树
我们有几个深度优先的变种,前序遍历,中序遍历,后序遍历。
遍历方法实现
两个步骤
1. 节点被发现,这个是说该节点被发现存在;
2. 节点被访问过,这个是说该节点被检查了,并且是否有子节点需要遍历。
先看广度优先:
维护两个数组:
explored;discovered。
从root开始:
explored = [a]; discovered = []
a有两个子节点b,c,从左节点开始一次放入discovered
explored = [a]; discovered = [b, c]
然后到b,b也有两个孩子节点,从左往右,放入discovered
explored = [a, b]; discovered = [c, d, e]
然后到c,一样的操作,一直到最后一个叶子节点
explored = [a, b, c]; discovered = [d, e, f, g]
explored = [a, b, c, d]; discovered = [e, f, g, h, i]
...
explored = [a, b, c, d, e, f, g, h, i]; discovered = []
广度优先,discovered其实是个queue结构,节点从头移出,从尾加入。
深度优先
discovered集合里的节点,我们不是从左往右了,而是从右往左。下一个要检查(explore)的节点,是我们发现(discovered)的最后的节点。
从root开始:
explored = [a]; discovered = [c, b]
从discovered末尾拿下一个explore的节点b
explored = [a, b]; discovered = [c, e, d]
explored = [a, b, d]; discovered = [c, e, i, h]
explored = [a, b, d, h]; discovered = [c, e, i]
explored = [a, b, d, h, i]; discovered = [c, e]
explored = [a, b, d, h, i, e]; discovered = [c]
explored = [a, b, d, h, i, e, c]; discovered = [g, f] // 这里到了右半部分
explored = [a, b, d, h, i, e, c, f]; discovered = [g]
explored = [a, b, d, h, i, e, c, f, g]; discovered = []
广度优先,discovered其实是个stack结构,节点从尾部进入,从尾部出。
代码实现(javascript)
先构造二叉树
class Node { constructor(data) { this.data = data; this.left = null; this.right = null; } } const rootNodeA = new Node("A"); const rootNodeA = new Node("B"); const rootNodeA = new Node("C"); const rootNodeA = new Node("D"); const rootNodeA = new Node("E"); const rootNodeA = new Node("F"); const rootNodeA = new Node("G"); const rootNodeA = new Node("H"); const rootNodeA = new Node("I"); rootNodeA.left = nodeB; rootNodeA.right = nodeC; nodeB.left = nodeD; nodeB.right = nodeE; nodeC.left = nodeF; nodeC.right = nodeG; nodeD.left = nodeH; nodeD.right = nodeI;
广度优先
function breadthFirstTraversal(root) { if (!root) { return; } let explored = []; // create a queue and add the root to it const discovered = new Queue(); discovered.enqueue(root); while (discovered.length > 0) { // remove the first item from queue const current = discovered.dequeue(); // process the current node explored.push(current); // store all unvisited children of the current node if (current.left) { discovered.enqueue(current.left); } if (current.right) { discovered.enqueue(current.right); } } return explored; }
Queue的实现在这里。
let fullTree = breadthFirstTraversal(rootNodeA);
console.log(fullTree);
深度优先
function depthFirstTraversal(root) { if (!root) { return; } let explored = []; // create a stack and add the root to it const discovered = new Stack(); discovered.push(root); while (discovered.length > 0) { // remove the last item from our list of discovered nodes const current = discovered.pop(); // Process the current node explored.push(current); // store all unvisited children of the current node in reverse order if (current.right) { discovered.push(current.right); } if (current.left) { discovered.push(current.left); } } return explored; }
Stack的实现在这里。
let fullTreeTwo = depthFirstTraversal(rootNodeA);
console.log(fullTreeTwo);
二叉树的遍历,深度优先、广度优先的时间复杂度和空间复杂度都是O(n)。
总结一下
1. 广度优先,按层从左往右访问节点
2. 深度优先,顺着分支访问节点,直到叶子节点,然后开始回溯
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验