前几天参加了阿里暑期实习的内推面试,发现本身的数据结构算法基础特别薄弱,好比其中一个问题是中序遍历的递归与非递归算法,我平时看数据结构只知道递归算法,非递归的算法直接被问懵逼了,在思考了几十秒以后想出了用数组存放每次遍历节点的父节点,而后用for循环遍历,虽然能够实现,可是我以为面试官听到以后估计在吐血,还有HashMap底层本身明明知道用的是散列表,在紧张之下我居然说是数组,面完以后被舍友吐槽,因此下定决心好好把算法基础打扎实了,虽然面试的不怎么样,可是可以知道本身的数据结构基础太薄弱了仍是有收获的。java
这篇博文主要是研究前序遍历的递归与非递归算法,后面还会慢慢有中序和后序的递归与非递归算法。面试
以前看的关于数据结构的书籍,在二叉树那一章的前序遍历主要讲的是递归算法,在这里也复习一下算法
先初始化二叉树数组
public TreeNode initTree(){ TreeNode G = new TreeNode("G", null, null); TreeNode H = new TreeNode("H", null, null); TreeNode D = new TreeNode("D", G, H); TreeNode B = new TreeNode("B", D, null); TreeNode I = new TreeNode("I", null, null); TreeNode E = new TreeNode("E", null, I); TreeNode F = new TreeNode("F", null, null); TreeNode C = new TreeNode("C", E, F); TreeNode A = new TreeNode("A", B, C); return A; }
再进行二叉树的前序递归遍历数据结构
public void preOrderTraverse(TreeNode T){ //前序遍历递归算法 if(T==null){ return; } System.out.println(T.data); //对节点进行处理,这里用输出代替处理 preOrderTraverse(T.lchild); preOrderTraverse(T.rchild); }
以上是前序递归遍历,代码简洁,若是对递归算法理解透彻的话,前序递归遍历很好理解,这里就不作过多阐述。虽然递归遍历简单和好理解,可是面对海量数据的时候,因为递归算法须要建立不少对象,须要占用大量内存,使得空间复杂度极大,也容易形成堆栈的溢出,所以递归算法面对海量数据时仍是有很是致命的缺陷,下面探究一下前序遍历的非递归算法。code
当用非递归遍历时,咱们不可以像递归遍历那样调用本身的方法来访问子节点,那怎么办呢?第一个出如今脑海中的想法即是用循环是遍历每个节点,可是当遍历到叶子节点的时候咱们须要回到前面遍历以前没有遍历到的右节点,这可怎么办?当初在面试的时候由于就给了几十秒的时间有点紧张,因此想出了用数组存储每次遍历后的节点的父节点,但其实二叉树的前序遍历有个特色就是遍历到左叶子节点的时候须要回头再去遍历以前未遍历到的右节点,这个特色恰好和栈的先进后出的特色很相符,因此应该改用栈来存储未遍历到的右节点
对象
代码以下blog
public void preOrderWithoutRecursion(TreeNode T){ //前序遍历非递归算法 TreeNode p; Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(T); //先将根节点压进栈中 while(T!=null&&!stack.empty()){ p = stack.pop(); //弹出栈顶的节点赋给p System.out.println(p.data); //用输出来代替对节点的处理 if(p.rchild!=null){ stack.push(p.rchild); //若是弹出节点的右孩子不为空则压入栈 } //注意,这里的重点是必定要先将右孩子压入栈,再将左孩子压入 if(p.lchild!=null){ stack.push(p.lchild); //若是弹出节点的左孩子不为空则压入栈 } } }
与递归的算法结果同样递归
虽然非递归算法的代码量比递归稍多了一点,可是对于海量数据来讲非递归算法的空间复杂度远低于递归算法,也不容易形成堆栈溢出。
内存