我们都知道二叉树的遍历方式有先序遍历,中序遍历,后序遍历。
先序遍历
他们最基本的实现方式是递归实现,那如果我们想要用非递归方式来实现呢?
首先,先序遍历的遍历方式是 根-左-右
递归方式代码实现:
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> list=new LinkedList<>();
preSort(root,list);
return list;
}
public void preSort(TreeNode root,List<Integer> list){
if(root==null) return;
list.add(root.val);//访问根节点
preSort(root.left, list);//访问左节点
preSort(root.right, list);//访问右节点
}
非递归方式实现:
思路:
1.将根节点进栈
2.弹出栈顶元素,并保存之
3.右孩子进栈,然后左孩子进栈
为什么先是右孩子进栈,然后左孩子进栈?
原因在于为了保证栈顶元素始终是先序序列顺序!这里充分利用了栈的后进先出的特性!
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> list=new LinkedList<>();
Stack<TreeNode> stack=new Stack<>();
if(root==null) return list;
stack.push(root);
TreeNode cur;
while(!stack.isEmpty()) {
cur=stack.pop();//获取栈顶元素并将该元素出栈
list.add(cur.val);//根节点
if(cur.right!=null) stack.push(cur.right);//右孩子进栈
if(cur.left!=null) stack.push(cur.left);//左孩子进栈
}
return list;
}
中序遍历
递归实现,和先序遍历一样,十分的简单:
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
inorderHelper(root, result);
return result;
}
private void inorderHelper(TreeNode root, List<Integer> result) {
if(root == null) return;
inorderHelper(root.left, result); // 递归遍历左子树
result.add(root.val); // 访问根节点
inorderHelper(root.right, result); // 递归遍历右子树
}
非递归实现
思路:
1.查找最左边的结点,在这个过程中,每个根节点都要入栈
2.获取栈顶元素,并将值保存起来
3.访问右子树,重复123
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> list=new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
if(root==null) return list;
TreeNode cur=root;
while(cur!=null || !stack.isEmpty()) {
//访问左子树,直到到达最左结点
while(cur!=null) {
stack.push(cur);
cur=cur.left;
}
//获取最左结点,并从栈中弹出来
cur=stack.pop();
//将值加入到集合中
list.add(cur.val);
//访问右子树
cur=cur.right;
}
return list;
}
后序遍历
递归实现,同上:
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
inorderHelper(root, result);
return result;
}
private void inorderHelper(TreeNode root, List<Integer> result) {
if(root == null) return;
inorderHelper(root.left, result); // 递归遍历左子树
inorderHelper(root.right, result); // 递归遍历右子树
result.add(root.val); // 访问根节点
}
非递归实现:
思路1:
1.访问最左边的结点,并将经过的每个根节点入栈
2.获取栈顶元素(最左边的元素),然后判断是否有右子树或右子树是否已经被遍历
原因:因为不同的结点对右子树的处理不一样,如结点A有右子树,A此时需要访问它。结点A的父亲结点B有右子树,但是当我们访问到结点B时,其右子树已经访问过了,这时候就不能再访问它了。所以我们每访问到一个结点,该结点不存在右子树或右子树已经被访问,就将标记结点更新
3.如果该结点存在右子树且尚未被访问,则访问右子树,重复123
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> list=new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
if(root==null) return list;
TreeNode cur=root;
//标记结点,用来记录该结点是否已经被访问
TreeNode pre=null;
while(cur!=null || stack.isEmpty()) {
//找到最左边的结点
while(cur!=null) {
stack.push(cur);
cur=cur.left;
}
//这里不能用pop(),因为后面我们需要判断这个结点是否存在右子树
cur=stack.peek();
//判断是否存在右子树或者右子树是否已经遍历过
if(cur.right==null || cur.right==pre) {
stack.pop();
list.add(cur.val);
pre=cur;
cur=null;
//如果存在右子树且右子树没有被遍历,则访问右子树
}else cur=cur.right;
}
return list;
}
思路2:借鉴先序遍历的思想
主要改变就是:先左孩子入栈,然后右孩子入栈
原因:使栈顶元素始终是按照根右左的顺序排序,最后只需将集合反转即可!
public ArrayList<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> list=new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
if(root==null) return list;
TreeNode cur;
stack.push(root);
while(!stack.isEmpty()){
cur=stack.pop();
list.add(cur.val);
if(cur.left!=null) stack.push(cur.left);
if(cur.right!=null) stack.push(cur.right);
}
Collections.reverse(list);
return list;
}
参考博客:https://segmentfault.com/a/1190000016674584?utm_source=tag-newest&tdsourcetag=s_pctim_aiomsg