前端数据结构:二叉树遍历详解
创作时间:
作者:
@小白创作中心
前端数据结构:二叉树遍历详解
引用
1
来源
1.
https://www.xin3721.com/Articlejquery/javascript36869.html
二叉树的遍历是指从根节点出发,按照某种顺序依次访问所有节点,而且只访问一次,二叉树的遍历方式很多,如果限制了从左到右的方式,那么主要有4种:
- 先序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
- 层序遍历:按层级、从上到下,在同一层从左到右遍历
以上一篇的二叉树为例子,先序遍历 先访问根节点,在访问左节点,在访问右节点,如图:
- 先(根)序遍历(根左右):A、B、D、E、C、F、G
- 中(根)序遍历(左根右) : D、B、E、A、F、C、G
- 后(根)序遍历(左右根) : D、E、B、F、G、C、A
- 层序序遍历(上到下,左到右):A、B、C、D、E、F、G
递归实现先序、中序、后序、层序遍历
二叉树的创建用上一篇链表方法存储的二叉树,只不过增加了4个遍历的方法而已。一颗大的树都是若干颗小树(根节点、左节点、右节点)构成,根节点也有可能是其他节点的左子树(树的根节点除外),所以递归遍历就是不断的递归子树的过程。
class Node {
constructor (data = '#') {
this.data = data
this.lNode = null
this.rNode = null
}
}
class BiTree {
root = null
nodeList = []
constructor (nodeList) {
this.root = new Node()
this.nodeList = nodeList
}
createNode (node) {
const data = this.nodeList.shift()
if (data === '#') return
node.data = data
// 下一个元素是不是空节点, 如果不是创建左节点
if (this.nodeList[0] !== '#') {
node.lNode = new Node(data)
}
this.createNode(node.lNode)
// 下一个元素是不是空节点, 如果不是创建右节点
if (this.nodeList[0] !== '#') {
node.rNode = new Node(data)
}
this.createNode(node.rNode)
}
preorderTraverse (node) {
if (node === null) return
console.log(node.data)
this.preorderTraverse(node.lNode)
this.preorderTraverse(node.rNode)
}
inorderTraverse (node) {
if (node === null) return
this.inorderTraverse(node.lNode)
console.log(node.data)
this.inorderTraverse(node.rNode)
}
postorderTraverse (node) {
if (node === null) return
this.postorderTraverse(node.lNode)
this.postorderTraverse(node.rNode)
console.log(node.data)
}
sequenceTraverse (root) {
if (!root) return
let queue = []
queue.push(root)
while (queue.length) {
const node = queue.shift()
console.log(node.data)
if(node.lNode) {
queue.push(node.lNode)
}
if (node.rNode) {
queue.push(node.rNode)
}
}
}
}
let arr = ['A','B','D','#','#','E','#','#','C','F','#', '#', 'G', '#', '#']
let bi = new BiTree(arr)
bi.createNode(bi.root)
console.log(bi.root)
console.log('----先序----')
console.log(bi.preorderTraverse(bi.root))
console.log('----中序----')
console.log(bi.inorderTraverse(bi.root))
console.log('----后序----')
console.log(bi.postorderTraverse(bi.root))
console.log('----层序----')
console.log(bi.sequenceTraverse(bi.root))
层级遍历使用了队列来实现,思路也比较简单,一开始入队根节点,出队最后一个节点,出队时把相关左节点、右节点入队,如此循环,队列为空即遍历完成。
非递归实现先序、中序、后序
二叉树的递归遍历非常容易理解,但因为是递归调用需要在内存栈中分配内存用来保存参数,层数多了内存容易溢出。
非递归先序基本思路:使用数组来模拟栈的数据结构,首先根节点入栈,然后出栈,在出栈的时候把该节点结果放入数组,然后把相关需要的节点按要求把左右节点入栈,如此循环一直到这个栈为空。
步骤:
- 根节点放入栈
- 取出栈顶的节点,把该节点结果放入数组
- 如果该右节点存在,将该节点右节点放入栈
- 如果该左节点存在,将该节点左节点放入栈
- 重复 1-4
- 栈为空退出循环
preorderNonRecursion (root) {
if (!root) return ''
let stack = []
let result = []
stack.push(root)
while (stack.length) {
const node = stack.pop()
result.push(node.data)
// 如果存在右节点,先压入右节点
if (node.rNode) {
stack.push(node.rNode)
}
if (node.lNode) {
stack.push(node.lNode)
}
}
return result
}
中序、后序代码基本思路类似代码如下:
inorderNonRecursion (root) {
if (!root) return ''
let stack = []
let result = []
// stack.push(root)
while (root !== null || stack.length) {
// 找到左节点
while (root !== null) {
stack.push(root)
root = root.lNode
}
root = stack.pop()
result.push(root.data)
// 右节点
root = root.rNode
}
return result
}
postorderNonRecursion (root) {
if (!root) return ''
let stack = []
let result = []
stack.push(root)
while (stack.length) {
const node = stack.pop()
result.unshift(node.data)
if (node.lNode) {
stack.push(node.lNode)
}
if (node.rNode) {
stack.push(node.rNode)
}
}
return result
}
递归遍历、非递归遍历完整代码
class Node {
constructor (data = '#') {
this.data = data
this.lNode = null
this.rNode = null
}
}
class BiTree {
root = null
nodeList = []
constructor (nodeList) {
this.root = new Node()
this.nodeList = nodeList
}
// 创建二叉树
createNode (node) {
const data = this.nodeList.shift()
if (data === '#') return
node.data = data
// 下一个元素是不是空节点, 如果不是创建左节点
if (this.nodeList[0] !== '#') {
node.lNode = new Node(data)
}
this.createNode(node.lNode)
// 下一个元素是不是空节点, 如果不是创建右节点
if (this.nodeList[0] !== '#') {
node.rNode = new Node(data)
}
this.createNode(node.rNode)
}
// 先序
preorderTraverse (node) {
if (node === null) return
console.log(node.data)
this.preorderTraverse(node.lNode)
this.preorderTraverse(node.rNode)
}
// 中序
inorderTraverse (node) {
if (node === null) return
this.inorderTraverse(node.lNode)
console.log(node.data)
this.inorderTraverse(node.rNode)
}
// 后序
postorderTraverse (node) {
if (node === null) return
this.postorderTraverse(node.lNode)
this.postorderTraverse(node.rNode)
console.log(node.data)
}
// 层序
sequenceTraverse (root) {
if (!root) return
let queue = []
queue.push(root)
while (queue.length) {
const node = queue.shift()
console.log(node.data)
if(node.lNode) {
queue.push(node.lNode)
}
if (node.rNode) {
queue.push(node.rNode)
}
}
}
// 非递归-先序
preorderNonRecursion (root) {
if (!root) return ''
let stack = []
let result = []
stack.push(root)
while (stack.length) {
const node = stack.pop()
result.push(node.data)
// 如果存在右节点,先压入右节点
if (node.rNode) {
stack.push(node.rNode)
}
if (node.lNode) {
stack.push(node.lNode)
}
}
return result
}
// 非递归-中序
inorderNonRecursion (root) {
if (!root) return ''
let stack = []
let result = []
// stack.push(root)
while (root !== null || stack.length) {
// 找到左节点
while (root !== null) {
stack.push(root)
root = root.lNode
}
root = stack.pop()
result.push(root.data)
// 右节点
root = root.rNode
}
return result
}
// 非递归-后序
postorderNonRecursion (root) {
if (!root) return ''
let stack = []
let result = []
stack.push(root)
while (stack.length) {
const node = stack.pop()
result.unshift(node.data)
if (node.lNode) {
stack.push(node.lNode)
}
if (node.rNode) {
stack.push(node.rNode)
}
}
return result
}
}
let arr = ['A','B','D','#','#','E','#','#','C','F','#', '#', 'G', '#', '#']
let bi = new BiTree(arr)
bi.createNode(bi.root)
console.log(bi.root)
console.log('----递归先序----')
console.log(bi.preorderTraverse(bi.root))
console.log('----非递归先序----')
console.log(bi.preorderNonRecursion(bi.root))
console.log('----递归中序----')
console.log(bi.inorderTraverse(bi.root))
console.log('----非递归中序----')
console.log(bi.inorderNonRecursion(bi.root))
console.log('----递归后序----')
console.log(bi.postorderTraverse(bi.root))
console.log('----非递归后序----')
console.log(bi.postorderNonRecursion(bi.root))
console.log('----层序----')
console.log(bi.sequenceTraverse(bi.root))
热门推荐
经济性裁员全流程指南:从启动到补偿
饮食指南:如何清洗和储存蘑菇?
15 种蘑菇(菌类)及其烹饪方法
饮食指南:如何清洗和储存蘑菇?
欠款3年被起诉怎么办法律有哪些规定
“职业背债人”月入百万背后:职场借贷的灰色地带与风险警示
沪铅价格波动,汽车电池成本受冲击
新鲜银耳完全指南:选购、处理与4种创意食谱
【储蓄方法】如何储蓄最有效?精选10大无痛快速储蓄攻略
重庆医科大学附属第一医院便民门诊服务指南
重庆医科大学附属第一医院:创新糖尿病足管理模式提升患者保肢率
冬季游望仙谷,门票价格大揭秘
五一假期望仙谷全攻略:从交通到住宿,玩转这片人间仙境
望仙谷门票120元值回票价吗?答案令人惊喜
白沙古道徒步:竹林里的清凉之旅
节日是怎么来的,都在520过情人节,这天还有另一个节日被人遗忘
2024年必玩语音手机游戏推荐:7款热门语音小游戏下载排行
AI赋能非遗数字化:创新保护与体验双管齐下
产研智库|“520”大潮褪去,奢美行业实现品效合一的三个方法论
糖尿病食谱:清炒茼蒿
体育饭圈化争议:从理性追星到极端派系的七宗“症”
30分场次超乔丹,40岁詹姆斯再创NBA纪录
50位最吸金运动员揭晓:篮球占12席,足球仅4席
AI绘画:艺术还是技术?专家解读艺术性争议
五冠在手,精神不朽:科比历史地位再评估
ChatGPT评十大超科比球员:乔丹詹姆斯居首
智能穿戴设备迎发展热潮,AI眼镜、玩具等五大品类各显神通
20年前湖人王朝:季后赛仅输1场,OK组合统治力惊人
2023年二本法学专业院校排名:四川警察学院居首,录取分数全解析
中国政法大学:985工程重点建设,法学教育的“国家队”