这段时间因为在学习其它内容和做项目,隔了20天没刷算法了~~ ,心里感到膈应的慌,一日不刷一日进不了大厂,额的天啊。。。
今天继续开始每天刷算法 (ง •̀_•́)ง ;
这是一篇基于刷leetcode的文章的扩充内容,这里有我刷 《leetcode初级算法》 的文章合集;
1、概述
这篇文章是包含二叉树的前序遍历、中序遍历、后序遍历的递归和非递归,和层次遍历的总结和代码实现;
1.1 、递归
对于前序遍历、中序遍历、后序遍历的递归实现来说,只要弄清了递归的基本套路,立马就可以写出程序,这里重新复习一下递归:
递归算法的基本流程:
- 将问题分解成一个一个的子问题;
- 求解子问题;
- 归并子问题的解;
最重要的就是要确定子问题(递归体)和找到开始归并的边界条件;
1.2、非递归
对于前序遍历、中序遍历、后序遍历的非递归实现,需要借助栈来完成,栈的特点是先入后出,满足递归的需要;
实际上前序遍历、中序遍历、后序遍历的递归实现也使用了栈,只不过这个栈是系统维护的函数调用递归栈;
对于前序遍历和后序遍历,它们的非递归实现相对来说不是很难理解;需要理解的难点还是在后序遍历的非递归遍历时,需要借助一个临时变量记录上一次操作过的节点,这样才可以在从子节点归并时确认是从左子节点还是从右子节点归并的;
1.3、层次遍历
层次遍历需要借助队列来实现,队列的特点是先入先出;
层次遍历也相对简单,重要的是要理解队列的概念(这也体现学好数据结构的重要性),这里不过多叙述;
下面是具体的代码实现,也可以访问本文的代码仓库:gitee.com/hotpotliuyu…
2、前序遍历
const { createBiTree,createBitreeByLevelOrder } = require("./createBitree");
const biTree = createBiTree(10);
console.log(biTree);
/**
*
* @param {biTree} root
* @description 二叉树前序遍历 递归实现
* @returns
*/
const preOrderByRecursion = function (root) {
if(root === null) {
return;
}
console.log(root.value);
preOrderByRecursion(root.left);
preOrderByRecursion(root.right);
}
console.log('preOrderByRecursion------------------');
preOrderByRecursion(biTree);
/**
*
* @description 借助栈实现非递归前序遍历二叉树
*/
const preOrderByNoRecursion = function(root) {
const stack = [];
let temp = root;
while(stack.length !== 0 || temp !== null) {
if(temp !== null) {
stack.push(temp);
console.log(temp.value);
temp = temp.left;
}else {
temp = stack.pop();
temp = temp.right;
}
}
}
console.log('preOrderByNoRecursion------------------');
preOrderByNoRecursion(biTree);
3、中序遍历
const { createBiTree,createBitreeByLevelOrder } = require("./createBitree");
const biTree = createBiTree(10);
/**
*
* @param {biTree} root
* @description 中序遍历递归实现
* @returns
*/
function inOrderByRecursion(root) {
if(root === null) {
return;
}
inOrderByRecursion(root.left);
console.log(root.value);
inOrderByRecursion(root.right);
}
/**
*
* @param {biTree} root
*/
function inOrderByNoRecursion(root) {
const stack = [];
let temp = root;
while(stack.length !== 0 || temp !== null) {
if(temp !== null) {
stack.push(temp);
temp = temp.left;
}else {
temp = stack.pop();
console.log(temp.value);
temp = temp.right;
}
}
}
inOrderByRecursion(biTree);
console.log('=======');
inOrderByNoRecursion(biTree);
3、后序遍历
const { createBiTree,createBitreeByLevelOrder } = require("./createBitree");
const biTree = createBiTree(10);
/**
*
* @param {biTree} root
* @description
*/
function postOrderByRecursion(root) {
if(root === null) {
return ;
}
postOrderByRecursion(root.left);
postOrderByRecursion(root.right);
console.log(root.value);
}
/**
*
* @param {biTree} root
* @description 后续非递归遍历的重点是要判断递归时是从左孩子还是从右孩子回溯的(借助一个临时指针指向上一次访问的节点,然后和当前节点的右孩子节点作比较)
*/
function postOrderByNoRecursion(root) {
const stack = [];
let temp = root;
let pre = null; // pre 指向前一次读取过内容的节点
while(temp !== null || stack.length !== 0) {
if(temp !== null) { // 递归深入阶段
stack.push(temp);
temp = temp.left;
}else {
temp = stack.slice(-1)[0];
if(temp.right !== null && pre !== temp.right) { // 向右分支深入
temp = temp.right;
} else { // 递归归并阶段
temp = stack.pop();
console.log(temp.value);
pre = temp; // 重点
temp = null; // 重点
}
}
}
}
postOrderByRecursion(biTree);
console.log('====================');
postOrderByNoRecursion(biTree);
4、层次遍历
const {createBiTree} = require('./createBitree');
const biTree = createBiTree(10);
/**
*
* @param {biTree} root
* @description 层次遍历
* @returns
*/
function levelOrder(root) {
if(root === null) {
return;
}
const queue = [];
queue.push(root);
let temp = null;
while(queue.length !== 0) {
temp = queue.shift();
console.log(temp.value);
if(temp.left !== null) {
queue.push(temp.left);
}
if(temp.right !== null) {
queue.push(temp.right)
}
}
}
levelOrder(biTree);