递归与迭代的稍讲解
递归递归,先递进再归来。要做递归现在清楚递归每一步要做什么,以及递归的终止条件和入口参数。迭代的前提要清楚你要遍历的实体的结构,然后在遍历每一个点,在二叉树的迭代遍历中,迭代模仿了递归的工作方式,用栈存储了每个结点,直到将叶子结点进栈之后,才不断出栈,然后访问结点。
二叉树前序遍历
递归实现
存在子结点,就调用自身往下一层,不存在返回空。
var preorderTraversal = function(root) {
let result = [];
function visit(root){ //递归函数
if(!root){
return null;
}
result.push(root.val);
if(root.left) visit(root.left);
if(root.right) visit(root.right);
}
visit(root);
return result;
};
迭代实现
前序遍历的迭代很好实现,按照先根后左右结点的方式,我们可以从上往下遍历,用栈存储结点,根据栈先进后出的原则,我们要先访问左结点,进栈的时候就得右结点先进栈, 然后就是将根结点出栈,再将他的子节点进栈。
var preorderTraversal2 = function(root){
const result = [];
if(!root){
return result;
}
let stack = [];
stack.push(root);
while(stack.length){
const root = stack.pop();
result.push(root.val);
if(root.right) stack.push(root.right);
if(root.left) stack.push(root.left);
}
return result;
}
二叉树中序遍历
递归
递归和上边前序遍历的一模一样,区别在于访问结点的时候
var inorderTraversal = function(root) {
let result = [];
function visit(root){
if(!root){
return null;
}
if(root.left) visit(root.left);
result.push(root.val);
if(root.right) visit(root.right);
}
visit(root);
return result;
};
迭代
迭代思想就是一直往左深度遍历,用栈存储结点,当到达左子树最后一个左结点时,退栈访问该结点的右孩子,然后不断重复。
var inorderTraversal = function(root) {
let result = [];
let stack = [];
stack.push(root);
let myroot = root;
while(stack.length){
while(myroot){
myroot = myroot.left;
stack.push(myroot);
}
stack.pop();
if(stack.length){
myroot = stack.pop();
result.push(myroot.val);
myroot = myroot.right;
stack.push(myroot);
}
}
return result;
};
二叉树后序遍历
递归
如上文。
迭代
后序遍历的迭代比较麻烦,主要在于如何记录这个结点左右孩子访问完毕,退栈方式和上面有所不同,当这个结点时叶子结点或是该结点左右孩子都访问过了就退栈。
var postorderTraversal = function(root) {
if (!root) {
return [];
}
const stack = []; // 记录结点和结点是否访问完全的标识。
const result = [];
stack.push({root, tag: false});
let myroot = root;
while (stack.length) {
//一直往左,直至最后一个左结点
while(myroot) {
myroot = myroot.left;
stack.push({ root: myroot, tag: false });
}
stack.pop(); //空值或返回上一层结点退栈。
if (stack.length) {
//取最后一个,当他是叶子结点,或者是遍历完子结点的根结点,就退栈
const obj = stack[stack.length - 1];
myroot = obj.root.right;
if (!myroot || obj.tag) {
result.push(obj.root.val);
myroot = null; //指针置空,该结点左右孩子访问完毕
} else {
obj.tag = true;
stack.push({ root: myroot, tag: false });
}
}
}
return result;
};
总结
递归就调用自身函数,入口传参就是左右孩子结点,当结点不存在的时候return空,递归结束。迭代就用栈存储孩子结点,遍历栈来访问结点。
声明:作者处于菜鸟阶段,还在不断学习,代码与思想仅供参考,欢迎指正。
复杂事往简单了去想,是拆解,是切割,就像一剑破万法,而将简单事往复杂了去想,是缝补,是搭建,是打造小天地。