二叉树的遍历
常见的二叉树的四种遍历方式为先序遍历,中序遍历,后序遍历及层次遍历。
所谓的先序、中序及后序遍历,都是先递归的访问左子树,再递归访问右子树,而根结点要么是在访问左子树之前访问,要么是在左子树之后右子树之前访问,要么是在右子树之后访问,这就是对应了先序、中序及后序遍历。也就是如下:
先序是:根左右
中序是:左根右
后序是:左右根
而层次遍历是从上到下按照每一层的顺序遍历,每一层中按照从左到右的顺序遍历。
一、先序遍历
1、递归方法
按照递归的定义,利用递归很容易实现二叉树的先序遍历。
//先序遍历
//递归实现
public List<Value> preOrder() {
List<Value> list = new ArrayList<Value>();
list = preOrder(root,list);
return list;
}
private List<Value> preOrder(Node node, List<Value> list) {
if(node == null)
return list;
list.add(node.val );
list = preOrder(node.left, list);
list = preOrder(node.right,list);
return list;
}
2、迭代的方法(1)
根据先序遍历的定义,先访问根结点,再访问左子树,最后访问右子树,我们可以利用一个栈来递归的实现。
//先序遍历的迭代实现1
private List<Value> preOrder1(Node node, List<Value> list) {
if(node == null)
return list;
Stack<Node> stack = new Stack<Node>();
stack.push(node);
while(!stack.isEmpty()) {
Node n = stack.pop();
list.add(n.val);
if(n.right != null)
stack.push(n.right);
if(n.left != null)
stack.push(n.left);
}
return list;
}
下面是一个采用此迭代方法的一个实例。
但是这种方法不能有效的推广到中序、后序遍历中,因此,换一种思路,我们可以得到新的迭代的方法。
3、迭代的方法(2)
观察一下以下这个先序遍历二叉树的一个实例。
通过观察,我们可以将二叉树的先序遍历总结为这样一个过程,如下图所示:
我们总是以根结点开始,首先从上到下沿着它的最左边的那条路径访问,直到最左边的那个结点不再有左子结点;然后,我们再从下到上,依次访问前面最左边路径上的每个结点的右结点所对应的子树。
这种方法的实现依旧需要使用到一个栈,具体的代码实现如下:
//先序遍历的迭代实现2
private List<Value> preOrder2(Node node, List<Value> list) {
if(node == null)
return list;
Stack<Node> stack = new Stack<Node>();
while(true) {
visitLeftLine(node,list,stack);
if(stack.isEmpty())
break;
node = stack.pop();
}
return list;
}
//从上到下依次遍历完以node为根结点的二叉树的最左边路径上的所有结点
private List<Value> visitLeftLine(Node node, List<Value> list, Stack<Node> stack) {
while(node != null) {
list.add(node.val);
if(node.right != null)
stack.push(node.right);
node = node.left;
}
return list;
}
利用此方法的进行遍历的过程的一个实例如下图所示
二、中序遍历
1、递归方法
首先,根据中序遍历的定义,通过递归仍然可以很容易的进行中序遍历:
//中序遍历,递归
public List<Value> inOrder() {
List<Value> list = new ArrayList<Value>();
list = inOrder(root,list);
return list;
}
private List<Value> inOrder(Node node, List<Value> list) {
if(node == null)
return list;
list = inOrder(node.left, list);
list.add(node.val);
list = inOrder(node.right,list);
return list;
}
2、迭代方法:
首先观察中序遍历的规律:
通过观察,我们可以总结出如下规律:
对于一颗二叉树,中序遍历首先总是沿着最左侧路径向下,直到某个结点的左结点为空,然后访问该节点,再访问该结点的右子树,访问完成之后,再沿着左侧路径向上,依次向上遍历。
为了能够迭代实现,依然需要使用一个栈,具体的代码如下:
//迭代方法
private List<Value> inOrder1(Node node, List<Value> list) {
if(node == null)
return list;
Stack<Node> stack = new Stack<Node>();
while(true) {
goAlongLeftLine(node, stack);
if(stack.isEmpty())
break;
node = stack.pop();
list.add(node.val);
node = node.right;
}
return list;
}
private void goAlongLeftLine(Node node, Stack<Node> stack) {
while(node != null) {
stack.push(node);
node = node.left;
}
}
下面是一个迭代方法对二叉树进行中序遍历的实例:
三、后序遍历
1、递归实现:
//后序遍历
public List<Value> postOrder() {
List<Value> list = new ArrayList<Value>();
list = postOrder1(root,list);
return list;
}
//递归方法
private List<Value> postOrder(Node node, List<Value> list) {
if(node == null)
return list;
list = postOrder(node.left, list);
list = postOrder(node.right,list);
list.add(node.val);
return list;
}
四、层次遍历
利用队列实现:
//层次遍历
public List<Value> order() {
List<Value> list = new ArrayList<Value>();
Queue<Node> queue = new LinkedBlockingQueue<Node>();
if(root == null)
return list;
queue.add(root);
while(!queue.isEmpty()) {
Node node = queue.remove();
list.add(node.val);
if(node.left != null)
queue.add(node.left);
if(node.right != null)
queue.add(node.right);
}
return list;
}