首先二叉树的基本结构:
public class BinaryTree<E> {
private TreeNode<E> root; //根节点
public BinaryTree(){
}
public BinaryTree(TreeNode<E> root){
this.root = root;
}
}
class TreeNode<E>{
private E value;
private TreeNode left; //左孩子
private TreeNode right; //右孩子
private boolean visited = false; //是否访问过,遍历器用
public TreeNode(){}
public TreeNode(E value){
this.value = value;
}
...省略get,set方法
}
先序遍历(先根遍历)
递归算法:
//前序遍历(递归)
public List<TreeNode> preOrder(TreeNode treeNode){
ArrayList<TreeNode> list = new ArrayList<>();
if(treeNode==null){
return list;
}
list.add(treeNode);
list.addAll(preOrder(treeNode.getLeft()));
list.addAll(preOrder(treeNode.getRight()));
return list;
}
栈实现算法:
栈实现前序遍历较简单,由于每次先输出根节点,再输出左节点随后是右节点。因此处理逻辑是:1、若栈非空则输出根节点,并出栈
2、如果存在右节点,右节点入栈
3、如果存在左节点,左节点入栈
4、重复第1步,直至栈空
代码:
//栈实现前序遍历
public List<TreeNode> preOrderByStack(TreeNode treeNode){
ArrayList<TreeNode> list = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList();
if(root == null){
return list;
}
stack.push(treeNode);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
list.add(node);
if(node.getRight()!=null){
stack.push(node.getRight());
}
if(node.getLeft()!=null){
stack.push(node.getLeft());
}
}
return list;
}
中序遍历
递归算法:
//中序遍历(递归)
public List<treenode>> inOrder(TreeNode treeNode){
ArrayList<TreeNode>> list = new ArrayList<>();
if(treeNode==null){
return list;
}
list.addAll(inOrder(treeNode.getLeft()));
list.add(treeNode);
list.addAll(inOrder(treeNode.getRight()));
return list;
}
栈实现算法:
栈的中序遍历需要套两层循环,由于需要先输出左节点,因此必须向下查找直到左节点为空才能输出。处理逻辑如下:1、如果栈顶元素非空且左节点存在,将其入栈,重复该过程。若不存在则进入第2步
2、若栈非空,输出栈顶元素并出栈。判断刚出栈的元素的右节点是否存在,不存在重复第2步,存在则将右节点入栈,跳至第1步
代码:
//栈实现中序遍历
public List<TreeNode> inOrderByStack(TreeNode treeNode){
ArrayList<TreeNode> list = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
if(treeNode == null){
return list;
}
TreeNode curr = treeNode;
while(curr!=null || !stack.isEmpty()){
while(curr!=null){
stack.push(curr);
curr = curr.getLeft();
}
curr = stack.pop();
list.add(curr);
curr = curr.getRight();
}
return list;
}
//栈实现中序遍历2
public List<TreeNode> inOrderByStack2(TreeNode treeNode) {
ArrayList<TreeNode> list = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
if (treeNode == null) {
return list;
}
stack.push(treeNode);
while(!stack.isEmpty()){
while(treeNode.getLeft()!=null){
treeNode = treeNode.getLeft();
stack.push(treeNode);
}
TreeNode pop = stack.pop();
list.add(pop);
if(pop.getRight()!=null){
treeNode = pop.getRight();
stack.push(treeNode);
continue;
}
}
return list;
}
后序遍历
递归算法:
//后序遍历(递归)
public List<TreeNode> postOrder(TreeNode treeNode){
ArrayList<TreeNode>> list = new ArrayList<>();
if(treeNode==null){
return list;
}
list.addAll(postOrder(treeNode.getLeft()));
list.addAll(postOrder(treeNode.getRight()));
list.add(treeNode);
return list;
}
栈实现算法:
后序遍历在中序的双层循环的基础上需要加入一个记录,专门记录上一次出栈的节点。步骤如下:1、如果栈顶元素非空且左节点存在,将其入栈,重复该过程。若不存在则进入第2步(该过程和中序遍历一致)
2、判断上一次出栈节点是否当前节点的右节点,或者当前节点是否存在右节点,满足任一条件,将当前节点输出,并出栈。否则将右节点压栈。跳至第1步
代码
//栈实现后序遍历
public List<TreeNode> postOrderByStack(TreeNode treeNode) {
ArrayList<TreeNode> list = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
if (treeNode == null) {
return list;
}
stack.push(treeNode);
TreeNode lastPop = null;
while(!stack.isEmpty()){
while(treeNode.getLeft()!=null){
treeNode = treeNode.getLeft();
stack.push(treeNode);
}
TreeNode curr = stack.peek();
if(curr!=null && lastPop!=null && lastPop == curr.getRight()) {
TreeNode pop = stack.pop();
pop.setVisited(true);
list.add(pop);
}
curr = stack.peek();
//如果没有isVisited属性,那么可以将访问过的节点放到一个统一的hashset(或hashmap)中,用!set.contains(curr.getRight())来代替curr.getRight().isVisited()==false
if(curr!=null && curr.getRight() != null && curr.getRight().isVisited()==false){
stack.push(curr.getRight());
treeNode = curr.getRight();
continue;
}
if(!stack.isEmpty()) {
lastPop = stack.pop();
lastPop.setVisited(true);
list.add(lastPop);
}
}
return list;
}