死磕算法-二叉树的遍历

二叉树实现

public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

二叉树中的访问,访问其实是根据实际的需要来确定要做什么,比如对每个结点进行相关计算,输出打印等

二叉树的遍历分为两种:深度优先遍历和广度优先遍历;深度优先遍历又分为两种,先序,中序,后序等,深度优先遍历的辅助结构是栈。

无论是哪种遍历方法,考察节点的顺序都是一样的。
在这里插入图片描述

考察节点顺序:
二叉树的考察节点顺序是先进入根节点的左节点,之后若该节点的左节点存在则继续延申考察,知直到左节点不存在,则返回原节点,考察右节点。
上图的考察顺序为:1->2->4->4->6->7->7->7->6->8->8->8->6->4->->2->2->1->3->3->5->5->5->3->1

简单讲下,首先进入根节点元素1,考察其左节点2,考察左节点2的左节点4,左节点4的的左节点不存在,则返回至左节点4(这样节点4就出现了两次),考察其右节点6,继续考察右节点6的左节点7,左节点7的左节点不存在,返回至左点7,考察左节点7的右节点,右节点不存在,返回至左节点7,这样节点7就出现了三次,其他同理

可以看到每个节点都出现了三次,先序遍历就是选择每个节点第一次出现的位置,中序遍历选择每个节点第二次出现的位置,后序遍历选择每个节点第三次出现的位置

三种遍历方法(人工)得到的结果分别是:

先序:1 2 4 6 7 8 3 5
中序:4 7 6 8 2 1 3 5
后序:7 8 6 4 2 5 3 1

三种遍历方法的考查顺序一致,得到的结果却不一样,原因在于

  1. 先序:考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)
  2. 中序:考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)
  3. 后序:考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)

可以简单理解为所谓前序、中序、后序是指"根"在其中的位置。

三种遍历方式之间的特点

  1. 前序遍历的第一个节点为根节点
  2. 中序遍历中,root在左右子树中间
  3. 后序遍历的最后一个节点为根节点

二叉树的先序遍历递归

若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树
在这里插入图片描述

public static void preOrderRecur(Node head) {
		//根->左->右
		if (head == null) {
			return;
		}
		System.out.print(head.value + " ");
		preOrderRecur(head.left);
		preOrderRecur(head.right);
	}

二叉树的中序遍历递归

若二叉树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点左子树,然后访问根结点,最后中序遍历右子树
在这里插入图片描述

public static void inOrderRecur(Node head) {
		//左->根->右
		if (head == null) {
			return;
		}
		inOrderRecur(head.left);
		System.out.print(head.value + " ");
		inOrderRecur(head.right);
	}

二叉树的后序遍历递归

若二叉树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。后序遍历的最后一个必然是根
在这里插入图片描述

public static void posOrderRecur(Node head) {
		//左->右->根
		if (head == null) {
			return;
		}
		posOrderRecur(head.left);
		posOrderRecur(head.right);
		System.out.print(head.value + " ");
	}

二叉树的先序遍历非递归

根据前序遍历访问的顺序,优先访问根节点,然后再分别访问左孩子和右孩子,即对于任一结点,其可看做是根节点,可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问完它的左子树后,再访问它的右子树。

在这里插入图片描述

public static void preOrderUnRecur(Node head) {
		System.out.print("pre-order: ");
		if (head != null) {
			Stack<Node> stack = new Stack<Node>();
			//压入根节点
			stack.add(head);
			while (!stack.isEmpty()) {
				head = stack.pop();
				System.out.print(head.value + " ");
				if (head.right != null) {
					stack.push(head.right);
				}
				if (head.left != null) {
					stack.push(head.left);
				}
			}
		}
		System.out.println();
	}

二叉树的中序遍历非递归

二叉树的中序遍历是:左-根-右。非递归实现是一路将根节点的左子树压入栈直到不存在左节点为止,之后弹出栈,压入右子树。

在这里插入图片描述

中序遍历就是将左子树作为单元,一层左子树遍历完后,跳到右节点,再遍历该右节点的左子树

	public static void inOrderUnRecur(Node head) {
		System.out.print("in-order: ");
		if (head != null) {
			Stack<Node> stack = new Stack<Node>();
			while (!stack.isEmpty() || head != null) {
				if (head != null) {
					stack.push(head);
					head = head.left;
				} else {
					head = stack.pop();
					System.out.print(head.value + " ");
					head = head.right;
				}
			}
		}
		System.out.println();
	}

二叉树的后序遍历非递归

二叉树的后序遍历为:左->右->根。后序遍历的非递归实现较为复杂,需要两个栈进行实现。

在这里插入图片描述

栈二入栈的顺序是右子树先入,弹栈的顺序便是左->右->根

public static void posOrderUnRecur1(Node head) {
		System.out.print("pos-order: ");
		if (head != null) {
			Stack<Node> s1 = new Stack<Node>();
			Stack<Node> s2 = new Stack<Node>();
			s1.push(head);
			while (!s1.isEmpty()) {
				head = s1.pop();
				s2.push(head);
				if (head.left != null) {
					s1.push(head.left);
				}
				if (head.right != null) {
					s1.push(head.right);
				}
			}
			while (!s2.isEmpty()) {
				System.out.print(s2.pop().value + " ");
			}
		}
		System.out.println();
	}

打印一颗二叉树

public static void printTree(Node head) {
		System.out.println("Binary Tree:");
		printInOrder(head, 0, "H", 17);
		System.out.println();
	}

	public static void printInOrder(Node head, int height, String to, int len) {
		if (head == null) {
			return;
		}
		printInOrder(head.right, height + 1, "v", len);
		String val = to + head.value + to;
		int lenM = val.length();
		int lenL = (len - lenM) / 2;
		int lenR = len - lenM - lenL;
		val = getSpace(lenL) + val + getSpace(lenR);
		System.out.println(getSpace(height * len) + val);
		printInOrder(head.left, height + 1, "^", len);
	}

打印出来的内容需要逆时针90度去看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值