02_二叉搜索树_前、中、后、层序四种遍历及增强遍历接口

1.递归方法

  • 前序遍历:根->左->右
	public void preorderTraversal() {
		preorderTraversal(root);
	}

	private void preorderTraversal(Node<E> node) {
		if (node == null) return;

		System.out.println(node.element);
		preorderTraversal(node.left);
		preorderTraversal(node.right);
	}
  • 中序遍历:左子树->根->右子树
	public void inorderTraversal() {
		inorderTraversal(root);
	}

	private void inorderTraversal(Node<E> node) {
		if (node == null) return;

		inorderTraversal(node.left);
		System.out.println(node.element);
		inorderTraversal(node.right);
	}
  • 后序遍历:左子树->右子树->根
	public void postorderTraversal() {
		postorderTraversal(root);
	}

	private void postorderTraversal(Node<E> node) {
		if (node == null) return;

		postorderTraversal(node.left);
		postorderTraversal(node.right);
		System.out.println(node.element);
	}
  • 层序遍历:不可以用递归,用队列
    • 1.将根节点入队
      2.循环执行以下操作,直到队列为空
      • a.将队头节点A 出队,进行访问
        b.将A的左子节点入队
        c.将A的右子节点入队
        在这里插入图片描述
        如上图,7入队,7出队,将4、9入队,然后4出队,2、5入队,依此可以遍历每一层节点
	public void levelOrderTraversal() {
		if (root == null) return;

		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);//1

		while (!queue.isEmpty()) {//2
			Node<E> node = queue.poll();//2.a
			System.out.println(node.element);

			if (node.left != null) {//2.b
				queue.offer(node.left);
			}

			if (node.right != null) {//2c.
				queue.offer(node.right);
			}
		}
	}

2.增强遍历接口

  • 由于拿到元素并非只有打印的需求,而是对其进行操作
  • 操作:定义visitor访问器接口,在上述那些前中后层次遍历的方法中将输出语句更改为由监听器内部定义的方法visitor.visit(node.element)
  • 作用:可以外部自定义对元素的操作方法,并且可以输出停止位置,即输出哪个元素就停止
  • 注:前中后判断停止位置的代码不同


  • 定义visitor访问器接口,而接口不允许有成员变量,所以定义为抽象类:stop成员用于存储每次停止位置
	public static abstract class Visitor<E> {
		boolean stop;
		/**
		 * @return 如果返回true,就代表停止遍历
		 */
		public abstract boolean visit(E element);
	}
  • 前序遍历:增强遍历接口
	public void preorder(Visitor<E> visitor) {
		if (visitor == null) return;
		preorder(root, visitor);
	}
	
	private void preorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop) return;
		
		visitor.stop = visitor.visit(node.element);
		preorder(node.left, visitor);
		preorder(node.right, visitor);
	}
  • 中序遍历:增强遍历接口
	public void inorder(Visitor<E> visitor) {
		if (visitor == null) return;
		inorder(root, visitor);
	}
	
	private void inorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop) return;
		
		inorder(node.left, visitor);
		if (visitor.stop) return;
		visitor.stop = visitor.visit(node.element);
		inorder(node.right, visitor);
	}	
  • 后序遍历:增强遍历接口
	public void postorder(Visitor<E> visitor) {
		if (visitor == null) return;
		postorder(root, visitor);
	}
	
	private void postorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop) return;
		
		postorder(node.left, visitor);
		postorder(node.right, visitor);
		if (visitor.stop) return;
		visitor.stop = visitor.visit(node.element);
	}
  • 层次遍历:增强遍历接口
	public void levelOrder(Visitor<E> visitor) {
		if (root == null || visitor == null) return;
		
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);
		
		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			if (visitor.visit(node.element)) return;
			
			if (node.left != null) {
				queue.offer(node.left);
			}
			
			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}
  • 测试代码:创建Visitor内部类,实现内部visit方法,默认返回false即不停止输出,所以返回true停止输出
	public static void main(String[] args){
		Integer data[] = new Integer[] {
				7, 4, 9, 2, 1
		};
		BinarySearchTree<Integer> bst = new BinarySearchTree<>();
		for (int i = 0; i < data.length; i++) {
			bst.add(data[i]);
		}
		BinaryTrees.println(bst);
		
		bst.preorder(new BinarySearchTree.Visitor<Integer>() {
			public boolean visit(Integer element) {
				System.out.print(element + " ");
				return element == 2 ? true : false;
			}
		});
	}
总结:
1. if (node == null || visitor.stop) return;  的语句中的  visitor.stop 若不加,则只是输出了前几个元素,
    实则还是进行了所有的递归操作,所以加上可以终止后续的递归操作,进而提高性能
2.后面的  if (visitor.stop) return;  也需要加,如在后序遍历的代码中如果不加则可能存在其右子树为true,
   那么紧接着不加判断的话自己的值也会进行输出,实则不需要。因为左->右->根,右子树如果为true了,
   后序不用进行输出,即根不需要输出。也就是说从打印层面看,
3.即这两句的 visitor.stop 功能是不一样的
对于这个问题,我们可以使用队列和分治法来求解二叉搜索树层序遍历。具体的实现方法如下: 1. 首先,根据后序遍历的定义,可以知道二叉搜索树的根节点是序列的最后一个元素。 2. 然后,使用分治法将序列划分为两个部分,即左子树和右子树。可以找到第一个比根节点小的元素,将其之的部分定义为左子树的后序遍历,之后的部分定义为右子树的后序遍历。 3. 对于左右子树,分别重复上述过程,递归求解左右子树的层序遍历。 4. 最后,将左右子树的层序遍历按照层次合并起来,即可得到整棵二叉搜索树层序遍历。 下面是具体的实现代码: ```python def postorder_to_levelorder(postorder): if not postorder: return [] root = postorder[-1] mid = bisect.bisect_left(postorder, root) left_postorder = postorder[:mid] right_postorder = postorder[mid:-1] left_levelorder = postorder_to_levelorder(left_postorder) right_levelorder = postorder_to_levelorder(right_postorder) levelorder = [root] queue = [root] while queue: node = queue.pop(0) if node in left_levelorder: levelorder.extend(left_levelorder[left_levelorder.index(node)+1:]) queue.extend(left_levelorder[left_levelorder.index(node)+1:]) if node in right_levelorder: levelorder.extend(right_levelorder[right_levelorder.index(node)+1:]) queue.extend(right_levelorder[right_levelorder.index(node)+1:]) return levelorder ``` 该实现使用了递归和队列,时间复杂度为 O(nlogn)。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值