失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 二叉树的深度优先遍历和广度优先遍历

二叉树的深度优先遍历和广度优先遍历

时间:2021-04-20 13:35:47

相关推荐

二叉树的深度优先遍历和广度优先遍历

二叉树是一种很重要的数据结构,对于二叉树的遍历,有深度优先遍历和广度优先遍历,深度优先遍历又有先序、中序、后续遍历,广度优先遍历就是按层遍历。

1. 深度优先遍历

深度优先遍历,也就是先序、中序、后续遍历,我之前有一篇随笔已经说的很清楚了,在这里我只贴下代码就好了。

传送门:详细教你实现BST(二叉排序树)

在这里我依然用之前建立好的Node、Stack、BST结构来实现代码。

class Node {constructor(data, leftNode, rightNode) {this.data = datathis.leftNode = leftNodethis.rightNode = rightNode}print () {return this.data}}class Stack {constructor() {this.arr = []}pop () {return this.arr.shift()}push (data) {this.arr.unshift(data)}isEmpty () {return this.arr.length == 0}}class BST {constructor() {this.root = null}insert (data) {...}preOrder () {...}inOrder () {...}postOrder () {...}...}

先是先序、中序、后序遍历的递归实现,很简单。

// 递归先序function preOrderFn (node) {if (node) {console.log(node.print())preOrderFn(node.leftNode)preOrderFn(node.rightNode)}}// 递归中序function inOrderFn (node) {if (node) {inOrderFn(node.leftNode)console.log(node.print())inOrderFn(node.rightNode)}}// 递归后续function postOrderFn (node) {if (node) {postOrderFn (node.leftNode)postOrderFn (node.rightNode)console.log(node.print())}}

然后就是先序、中序、后续遍历的非递归实现了。详细的解释和说明,点击上面的传送门就有了,这里不过多赘述。

// 非递归先序function PreOrderWithoutRecursion (root) {if (!root)returnvar parentNode = rootvar stack = new Stack()while (parentNode || !stack.isEmpty()) {// 一直遍历到左子树的最下面,一边打印data,将一路遍历过的节点push进栈中if (parentNode) {console.log(parentNode.data)stack.push(parentNode)parentNode = parentNode.leftNode}// 当parentNode为空时,说明已经达到了左子树的最下面,可以出栈操作了else {parentNode = stack.pop()// 进入右子树,开始新一轮循环parentNode = parentNode.rightNode}}}// 非递归中序function inOrderWithoutRecursion (root) {if (!root)returnvar parentNode = rootvar stack = new Stack()while (parentNode || !stack.isEmpty()) {// 一直遍历到左子树的最下面,将一路遍历过的节点push进栈中if (parentNode) {stack.push(parentNode)parentNode = parentNode.leftNode}// 当parentNode为空时,说明已经达到了左子树的最下面,可以出栈操作了else {parentNode = stack.pop()console.log(parentNode.data)// 进入右子树,开始新一轮循环parentNode = parentNode.rightNode}}}// 非递归后续function PostOrderWithoutRecursion (root) {if (!root)returnvar parentNode = rootvar stack = new Stack()var lastVisitNode = nullwhile (parentNode || !stack.isEmpty()) {if (parentNode) {stack.push(parentNode)parentNode = parentNode.leftNode}else {parentNode = stack.pop()// 如果当前节点没有右节点或者是右节点被访问过,则访问当前节点if (!parentNode.rightNode || parentNode.rightNode.data == lastVisitNode.data) {console.log(parentNode.data)lastVisitNode = parentNode}// 访问右节点else {stack.push(parentNode)parentNode = parentNode.rightNodewhile (parentNode) {parentNode = parentNode.leftNode}}}}}

2. 广度优先遍历

其实这片随笔有点打酱油了,只说了两个遍历,还有一个是在以前实现过的。

广度优先遍历,顾名思义,就是横向先遍历,也就是按层次遍历,从根节点往下,对每一层依此访问,在每一层中从左到右(也可以从右到左)遍历,遍历完一层就进入下一层,直到没有节点。

之前讲深度优先非递归遍历的时候,我们用到了一个栈的数据结构,到了广度优先遍历的时候,我们就要用到队列这个数据结构。

为什么上一次用栈,这一次就要用到队列了呢?

拿非递归中序遍历举例,我们每遍历到一个节点就要进行入栈操作,遍历完左节点之后,还需要找到根节点,再通过根节点找到右节点,所以我们需要最后遍历到的节点在这个数据结构的最顶端,这不就是栈吗?

先把我们的队列的数据结构先建立起来再说。依然用数组模拟队列的操作。

class Queue {constructor () {this.arr = []}enqueue (data) {return this.arr.push(data)}dequeue () {return this.arr.shift()}isEmpty () {return this.arr.length == 0}}

为什么要用队列呢,我们按层次遍历,首先遍历根节点,然后左子树,右子树,然后左子树的左子树,左子树的右子树,右子树的左子树,依此类推。每遍历到一个节点,就将它存在一个数据结构里,先把它的前面的节点遍历完,才能遍历它,也就是一个先进先出(FIFO)的遍历方式,这不就是队列吗?

说下思路:首先现将根节点做入队操作,队列里的节点表示我们要遍历的节点,所以队列为空的时候也就是没有节点可以遍历了,即队列不为空的时候循环遍历整个队列。首先我们取出队列的第一个节点,也就是对这个队列做出队操作,访问这个节点的值,如果这个节点存在左子树,那么将它的左子树放在队列的末尾,也就是对左子树做入队操作,右子树同理。

思路很简单,实现起来不难:

class BST {constructor() {this.root = null}// 广度优先遍历levelOrderTraversal () {levelOrderTraversalFn(this.root)}insert (data) {...}preOrder () {...}inOrder () {...}postOrder () {...}find (data) {..}getMax () {...}getMin () {...}deleteNode (data) {...}depth () {...}nodeCount () {...}}// 广度优先遍历function levelOrderTraversalFn (node) {if(!node) {return}var que = new Queue()que.enqueue(node)while(!que.isEmpty()) {node = que.dequeue()console.log(node.data)if(node.leftNode) que.enqueue(node.leftNode)if(node.rightNode) que.enqueue(node.rightNode)}}

我们试一下:

没错,那我们的广度优先遍历也就写完了。

如果觉得《二叉树的深度优先遍历和广度优先遍历》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。