包含树的深度优先遍历(递归、非递归),广度优先遍历,先序遍历(递归、非递归),中序遍历(递归、非递归),后序遍历(递归、非递归)。
二叉树的四种遍历方式分别是:先序、中序、后序和层次。
它们的时间复杂度都是O(n),因为它们只访问每个节点一次,不存在多余的访问。
三种深度优先遍历方法(先序、中序和后序)的时间复杂度是O(h),其中h是二叉树的深度,额外空间是函数递归的调用栈产生的,而不是显示的额外变量。层次遍历的时间复杂度是O(w),其中w是二叉树的宽度(拥有最多节点的层的节点数),因为层次遍历通常是用一个queue来实现的。
package com.interview.Tree;
import java.util.*;
public class Tree {
public static void main(String[] args) {
BinaryTree bt = new BinaryTree();
TreeNode root = bt.getBinaryTree(new int[]{1, 2, 3, 4, 5, 6, 7, 8}, 0);
//深度优先
ArrayList<Integer> list = bt.DFS(root);
System.out.println(list.toString());
//深度优先,使用递归
bt.DFSWithRecursive(root);
System.out.println();
//广度优先
ArrayList<Integer> list2 = bt.BFS(root);
System.out.println(list2.toString());
//先序遍历
bt.preOrderTraverse1(root);
System.out.println();
bt.preOrder1(root);
System.out.println();
//中序遍历
bt.inOrderTraverse1(root);
System.out.println();
bt.inOrder1(root);
System.out.println();
//后序遍历
bt.postOrderTraverse1(root);
System.out.println();
bt.posOrder1(root);
System.out.println();
}
}
class TreeNode {
int value;
TreeNode left;
TreeNode right;
TreeNode(int value) {
this.value = value;
}
}
class BinaryTree {
/**
* 构建二叉树,但是这个好像只能构建完全二叉树。
* @param arr
* @param index
* @return
*/
public TreeNode getBinaryTree(int[] arr, int index) {
// TODO Auto-generated method stub
TreeNode node = null;
if (index < arr.length) {
int value = arr[index];
node = new TreeNode(value);
node.left = getBinaryTree(arr, index * 2 + 1);
node.right = getBinaryTree(arr, index * 2 + 2);
return node;
}
return node;
}
/**
* 深度优先
* @param root
* @return
*/
public ArrayList<Integer> DFS(TreeNode root) {
//非递归,注意先插右再插左。实际上就是前序遍历
ArrayList<Integer> list = new ArrayList<>();
if (root == null) {
return list;
}
Stack<TreeNode> stack = new Stack();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
list.add(node.value);
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return list;
}
/**
* 广度优先
* @param root
* @return
*/
public ArrayList<Integer> BFS(TreeNode root) {
//非递归,注意先插左再插右。
ArrayList<Integer> list = new ArrayList<>();
if (root == null) {
return list;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
list.add(node.value);//因为不是递归,所以先后add均可以
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return list;
}
/**
* 深度优先,递归
* @param root
*/
public void DFSWithRecursive(TreeNode root) {//递归
if (root != null) {//递归版本的深度优先,其实就是先序遍历
System.out.print(root.value + " ");
DFSWithRecursive(root.left);
DFSWithRecursive(root.right);
}
}
/**
* 先序遍历
* 递归
* @param root
*/
public void preOrderTraverse1(TreeNode root) {
if (root != null) {
System.out.print(root.value + " ");
preOrderTraverse1(root.left);
preOrderTraverse1(root.right);
}
}
/**
* 先序遍历
* 非递归
* @param root
*/
public void preOrder1(TreeNode root) {//先序遍历非递归
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {//这里要保证root为空,stack也能进入
while (root != null) {
System.out.print(root.value + " ");
stack.push(root);
root = root.left;
}
if (!stack.isEmpty()) {//转化为右节点
root = stack.pop();
root = root.right;
}
}
}
/**
* 中序遍历
* 递归
* @param root
*/
public void inOrderTraverse1(TreeNode root) {
if (root != null) {
inOrderTraverse1(root.left);
System.out.print(root.value + " ");
inOrderTraverse1(root.right);
}
}
/**
* 中序遍历
* 非递归
* @param root
*/
public void inOrder1(TreeNode root) {//先序遍历非递归,与前序类似
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {//这里要保证root为空,stack也能进入
while (root != null) {
stack.push(root);
root = root.left;
}
if (!stack.isEmpty()) {//转化为右节点
root = stack.pop();
System.out.print(root.value + " ");
root = root.right;
}
}
}
/**
* 后序遍历
* 递归
* @param root
*/
public void postOrderTraverse1(TreeNode root) {
if (root != null) {
postOrderTraverse1(root.left);
postOrderTraverse1(root.right);
System.out.print(root.value + " ");
}
}
/**
* 后序遍历
* 非递归
* @param root
*/
//按访问顺序来
//要访问一个节点的条件是上一个访问的节点是右儿子。我们可以增加一个变量Prev来判断当前节点Curr的上一个节点与它的关系来执行相应的操作。
//若Prev为空(Curr节点是根节点)或者Prev是Curr的父节点,将Curr节点的左孩子压入栈,否则将右孩子压入栈。
//若Prev是Curr的左儿子,则将Curr的右儿子压入栈;
//若Prev是Curr的右儿子,访问Curr;
//若Prev等于Curr,说明为叶子节点,或没有右儿子,访问Curr;
public void posOrder1(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null, cur = null;
stack.push(root);
while (!stack.isEmpty()) {
cur = stack.peek();
if (pre == null || pre.left == cur || pre.right == cur) {
if(cur.left!=null){
stack.push(cur.left);
}
else if(cur.right!=null){//注意这里是else
stack.push(cur.right);
}
}
else if(pre == cur.left){
if(cur.right!=null){
stack.push(cur.right);
}
}
else if(pre == cur.right){
System.out.print(cur.value+" ");
stack.pop();
}
else if(pre == cur){
System.out.print(cur.value+" ");
stack.pop();
}
pre = cur;
}
}
}
博客介绍了树的深度优先、广度优先、先序、中序、后序等遍历方式,包括递归与非递归实现。指出二叉树有先序、中序、后序和层次四种遍历方式,其时间复杂度均为O(n),还分别说明了深度优先和层次遍历的时间复杂度。
2573

被折叠的 条评论
为什么被折叠?



