二叉树的前序、中序、后序和层次遍历 & 二叉搜索树的插入、查找操作

树的建立

首先,先建立起二叉树的类:

public abstract class BinaryTree {
	
	public class TreeNode{
		int data;
		TreeNode left;
		TreeNode right;
		public TreeNode(int data){
			this.data = data;
		}
	}
	
	protected TreeNode root;
	
	public BinaryTree() {
		root = null;
	}
	
	public boolean empty() {
		return root == null;
	}
	
	abstract public boolean insert(int data);
	
	public int height() {
		return height(root);
	}
	
	public static int height(TreeNode root) {
		if(root == null) return 0;
		return Math.max(height(root.left), height(root.right)) + 1;
	}
}

然后是二叉搜索树的类,继承自BinaryTree ,实现了insert方法,新增了搜索的方法:

public class SearchTree extends BinaryTree {

	public SearchTree() {
		super();
	}
	
	@Override
	public boolean insert(int data) {
		if (root == null) {
			root = new TreeNode(data);
			return true;
		}
		TreeNode root = this.root;
		TreeNode temp = root;
		while(root != null) {
			temp = root;
			if(root.data < data) root = root.right;
			else if(root.data > data) root = root.left;
			else return false;
		}
		if(temp.data < data) temp.right = new TreeNode(data);
		if(temp.data > data) temp.left = new TreeNode(data);
		return true;
	}

	public TreeNode treeSearch(int target) {
		return searchForNode1(root, target);
		// return searchForNode2(root, target);
	}
	
	public TreeNode searchForNode1(TreeNode root, int target) {
		if (root == null || root.data == target) {
			return root;
		} else if (root.data < target) {
			return searchForNode1(root.right, target);
		} else {
			return searchForNode1(root.left, target);
		}
	}
	
	public TreeNode searchForNode2(TreeNode root, int target) {
		while(root != null && root.data != target) {
			if(root.data < target) root = root.right;
			else root = root.left;
		}
		return root;
	}
}

前序遍历

接下来,在BinaryTree类中增加几个遍历的方法。
首先是前序遍历。

方法一:递归

这比较容易实现,逻辑也比较清晰。

方法二:使用栈

(1) 先把根节点入栈(如果不为空的话);
(2) 访问栈顶,将栈顶弹出,并将其右、左子节点(如果有的话)依次放进栈中;
(3) 重复(2)直到栈为空。

方法三:使用栈

先访问根节点,再访问所有左孩子,直到左孩子为空,反过来访问其右孩子。这个思路比较不好理解,但是却比较通用,下面中序、后序遍历都可以使用这个思路,只需要把访问节点的代码换个位置就可以。

前序遍历代码如下:

public void preOrder() {
	// 方法一:递归
	preOrderTraverseRecursive(root);
	
	System.out.println();
	
	// 方法二:
	Stack<TreeNode> stack = new Stack<>();
	if (root != null) {
		stack.push(root);
	}
	while(!stack.empty()) {
		TreeNode top = stack.pop();
		System.out.print(top.data + ",");
		if (top.right != null) {
			stack.push(top.right);
		}
		if (top.left != null) {
			stack.push(top.left);				
		}
	}
	
	System.out.println();
	
	// 方法三:
	// Stack<TreeNode> stack = new Stack<>();
	TreeNode node = root;
	while(node != null || !stack.empty()) {
		if(node != null) {
			System.out.print(node.data + ",");
			stack.push(node);
			node = node.left;
		} else {
			node = stack.pop();
			node = node.right;
		}
	}
}
public static void preOrderTraverseRecursive(TreeNode root) {
	if (root != null) {
		System.out.print(root.data + ",");
		preOrderTraverseRecursive(root.left);
		preOrderTraverseRecursive(root.right);
	}
}

中序遍历

方法跟前序遍历的方法一、三类似,只不过在方法三中,这里改为在出栈时才访问节点。

public void inOrder() {
	// 方法一:递归
	inOrderTraverseRecursive(root);
	
	System.out.println();
	
	// 方法二:
	Stack<TreeNode> stack = new Stack<>();
	TreeNode node = root;
	while(node != null || !stack.empty()) {
		if(node != null) {
			stack.push(node);
			node = node.left;
		} else {
			node = stack.pop();
			System.out.print(node.data + ",");
			node = node.right;
		}
	}
}

public static void inOrderTraverseRecursive(TreeNode root) {
	if (root != null) {
		inOrderTraverseRecursive(root.left);
		System.out.print(root.data + ",");
		inOrderTraverseRecursive(root.right);
	}
}

后序遍历

与中序遍历的两个方法类似。不过这里比较特殊,多用一个栈来存储数据。其实看这段代码,跟前序遍历很像,不同的是这里先访问右子节点再访问左子节点,而且多了一个栈用来存储逆后序遍历的结果,即反过来输出之后,就是后序遍历的结果。

public void postOrder() {
	// 方法一:递归
	postOrderTraverseRecursive(root);
	
	System.out.println();
	
	// 方法二:
	Stack<TreeNode> stack = new Stack<>();
	Stack<TreeNode> out = new Stack<>(); // 存储逆后序遍历的结果
	TreeNode node = root;
	while(node != null || !stack.empty()) {
		if(node != null) {
			stack.push(node);
			out.push(node);
			node = node.right;
		} else {
			node = stack.pop();
			node = node.left;
		}
	}
	while(!out.empty()) {
		System.out.print(out.pop().data + ",");
	}
}

public static void postOrderTraverseRecursive(TreeNode root) {
	if (root != null) {
		postOrderTraverseRecursive(root.left);
		postOrderTraverseRecursive(root.right);
		System.out.print(root.data + ",");
	}
}

层次遍历

层次遍历就是在树的每一层(从上到下)从左到右访问。使用到了队列,步骤如下:

(1) 先把根节点入队列(如果不为的话);
(2) 访问队首,将其弹出,并将其左、右子节点(如果有的话)依次进入队列;
(3) 重复(2)直到队列为空。

public void levelTraverse() {
	LinkedList<TreeNode> queue = new LinkedList<>();
	if (root != null) {
		queue.offer(root);
	}
	while(!queue.isEmpty()) {
		TreeNode top = queue.poll();
		System.out.print(top.data + ",");
		if (top.left != null) {
			queue.offer(top.left);
		}
		if (top.right != null) {
			queue.offer(top.right);
		}
	}
}

以上的前序、中序、后序遍历其实就是树的深度优先搜索;
层次遍历就是树的宽度(广度)优先搜索。


相关阅读:
根据前序和中序(后序和中序)遍历构造树

猜你喜欢

转载自blog.csdn.net/Runner1st/article/details/88560456