二叉树的深度优先和广度优先遍历方式的实现
二叉树是什么
二叉树是一种树存储结构,只要符合以下两个条件的树就是二叉树。
- 本身是有序树。
- 每个节点的度不能超过2
树的度指的就是节点子树,也就是说二叉树每个节点的子树不能够多于2。
Java中实现二叉树的结构
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
每个二叉树的节点在保存自身数据的基础上还保存了子节点,其实就是一个递归的结构,因此使用递归的方法来获取二叉树的数据非常简单。二叉树的遍历主要分为两种,深度优先遍历(Depth First Search)和广度优先遍历(Breadth First Search)。
二叉树深度优先遍历
二叉树的深度优先遍历就是依据某种次序将二叉树由某一层开始访问,当访问完成当前层的所有节点后再进入下一层,根据根节点在遍历过程中的顺序,将深度优先遍历又分为了先序遍历,中序遍历和后序遍历。
- 先序遍历
先序遍历就是先访问根节点,再分别访问左右节点的方式来遍历二叉树。
对于一个上图中的二叉树来说,它的先序遍历顺序就是A,B,D,E,F,G,C,H,首先访问A节点,然后访问它的左节点B,又因为B又是它的子树的根节点,所以再访问它的左节点,这样下来就是上面的顺序了。
用Java来实现先序遍历主要有栈和递归两种方式,后面的中序和后序也会用这两种方式去实现。
Java递归方式实现先序遍历:
public void firstSearch(TreeNode root){
if(root!=null){
System.out.println(root.val);
firstSearch(root.left);
firstSearch(root.right);
}
}
非常简单的代码,就不用解释了
Java栈的方式实现先序遍历:
public void firstSearch2(TreeNode root){
Stack<TreeNode> stack=new Stack<>();
while(root!=null||stack.size()>0){
if(root!=null){
System.out.println(root.val);
stack.push(root);
root=root.left;
}else{
TreeNode pop = stack.pop();
root=pop.right;
}
}
}
首先创建一个栈,然后访问根节点的数据,最后把它压入栈并取出它的左子节点作为下一次循环的值,如果发现当前的根节点为null,就表示上一个节点的左节点为null,那就从栈里弹出它并将它的右子节点作为下一次循环的值,直到栈内没有元素。
- 中序遍历
二叉树的中序遍历就是先左再根最后右节点的方式来进行遍历。
对于上图的二叉树来说,它的中序遍历方式就是E,D,F,B,G,A,C,H。
Java递归的方式实现中序遍历:
public void firstSearch3(TreeNode root){
if(root!=null){
firstSearch3(root.left);
System.out.println(root.val);
firstSearch3(root.right);
}
}
还是非常简单,只不过调换了一下代码的执行顺序,变成先遍历左节点,再输出根节点,最后输出右节点。
Java栈的方式实现中序遍历:
public void firstSearch4(TreeNode root){
Stack<TreeNode> stack =new Stack<>();
while(root!=null||stack.size()>0){
if(root!=null){
stack.push(root);
root=root.left;
}else{
TreeNode pop = stack.pop();
System.out.println(pop.val);
root=pop.right;
}
}
}
和先序遍历的方法没什么区别,和递归一样只是调换的代码顺序,当所有左子节点压入栈后,再弹出遍历,并
3. 后序遍历
二叉树的中序遍历就是先左再右后根节点的方式来进行遍历。
同样是这个二叉树,根据后序遍历的方式得到的结果是,E,F,D,G,B,H,C,A。
Java递归的方式实现后续遍历
public void firstSearch5(TreeNode root){
if(root!=null){
firstSearch5(root.left);
firstSearch5(root.right);
System.out.println(root.val);
}
}
Java栈的方式实现后续遍历
public void firstSearch7(TreeNode root){
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode pre = null;
stack.push(root);
while(!stack.isEmpty()){
TreeNode curr = stack.peek();
if((curr.left == null && curr.right == null) ||
(pre != null && (pre == curr.left || pre == curr.right))){
//如果当前结点左右子节点为空或上一个访问的结点为当前结点的子节点时,当前结点出栈
System.out.println(curr.val);
pre = curr;
stack.pop();
}else{
if(curr.right != null) stack.push(curr.right); //先将右结点压栈
if(curr.left != null) stack.push(curr.left); //再将左结点入栈
}
}
}
后序遍历栈的实现方式相比前两种就复杂一些,因为它要求我们将根节点弹出栈后在它有右节点情况下不访问它,最后再访问它。在代码的前半部分还是和中序遍历一样的思路,先找到最左的子节点,不同的是,弹栈的时候我们每次都进行判断,它的右节点是否为空以及上一次是否访问了它的右节点,如果不符合这个条件我们就把它再存入栈,将它的右节点作为下一个节点,当访问完成它的左右节点后再对它进行访问。
二叉树广度优先遍历
二叉树的广度优先遍历就是自上而下,将二叉树逐层遍历,
对于上图中的二叉树来说,它的广度优先遍历结果就是A,B,C,D,G,H,E,F。
Java队列的方式实现二叉树的广度优先遍历:
public void firstSearch8(TreeNode root){
Queue<TreeNode> queue=new LinkedList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
TreeNode remove = queue.remove();
System.out.println(remove.val);
if(remove.left!=null){
queue.add(remove.left);
}
if(remove.right!=null){
queue.add(remove.right);
}
}
}
非常简单,利用队列先进先出的思想,访问根节点后再将它再下一层的子节点放在队列末尾,这样遍历完成这一层后,就可以再访问下一层节点。