数据结构:二叉树遍历集合,树高(Java)

1.基本介绍

下面介绍的基于二叉树的基本查询的总结.
二叉树的插入方法是基于二叉搜索树的规则.left<root<right.
结点的高度:结点到其叶子结点的最长路径上的结点个数.
结点的深度:树的根结点到当前结点路径上结点个数.
树的高度:所有结点高度的最大值.
树的深度:所有结点深度的最大值.
树的高度是等于树的深度的.
但是对于所有的结点的高度不一定等于结点的深度.

2.二叉树示例

二叉树图

在这里插入图片描述

结果打印

-----------------------
树的高度:
递  归:4
非递归:4
-----------------------
前序遍历:
递  归:56,32,21,25,43,76,88,80,
非递归:56,32,21,25,43,76,88,80,
-----------------------
中序遍历:
递  归:21,25,32,43,56,76,80,88,
非递归:21,25,32,43,56,76,80,88,
-----------------------
中序遍历:
递  归:25,21,43,32,80,88,76,56,
非递归:25,21,43,32,80,88,76,56,
-----------------------
层序遍历:56,32,76,21,43,88,25,80,

3.数据结构

3.1结点

树的结点

/**
	 * 树的结点
	 * @param
	 */
	protected static class Node {
		public int value;
		public Node left;// 左节点
		public Node right;// 右节点

		public Node(int value) {
			this.value = value;
			this.left = null;
			this.right = null;
		}
	}

3.2二叉树

包含一个根和结点个数

public class ExBinaryTree{
	private Node root;// 根结点
	private int size;
	public ExBinaryTree() {
	}
}

4.添加结点


	/**
	 * 添加结点:有序添加,left<root<right 重复的元素:后添加的覆盖之前添加的
	 * 
	 * @param value 结点的值
	 */
	public void add(int value) {
		Node node = null;
		// 1.如果是空树,那么将置为根结点
		if (root == null) {
			node = new Node(value);
			root = node;
			return;
		}
		// 2.非空树,先找到插入的位置.
		Node parentNode = root;
		Node tempNode = root;
		int comparator = 0;
		while (tempNode != null) {
			comparator = value - tempNode.value;
			parentNode = tempNode;
			if (comparator == 0) {// 插入的结点和当前结点一样大
				// 这里处理的是不可以插入重复的结点,覆盖旧结点
				tempNode.value = value;
				return;
			} else if (comparator < 0) {// 插入的结点小
				tempNode = tempNode.left;
			} else {// 插入的节点大
				tempNode = tempNode.right;
			}
		}
		// 3.将新结点通过父结点添加到左节点/右节点
		node = new Node(value);
		if (comparator < 0) {
			parentNode.left = node;
		} else {
			parentNode.right = node;
		}
	}

5.遍历

打印结点的值

/**
	 * 打印结点的值
	 * @param num
	 */
	private void printNodeValue(int num) {
		System.out.print(num + ",");
	}

5.1前序遍历

前序遍历-递归


	/**
	 * 前序遍历:递归方法:root,left,right
	 * 
	 */
	public void preOrderTraversal() {
		preOrderTraversal(root);
	}

	/**
	 * 前序遍历:root,left,right
	 * 
	 * @param node 遍历的结点
	 */
	private void preOrderTraversal(Node node) {
		if (node == null)
			return;
		// 访问root
		printNodeValue(node.value);
		// 访问left
		preOrderTraversal(node.left);
		// 访问right
		preOrderTraversal(node.right);
	}

前序遍历-非递归


	/**
	 * 前序遍历:非递归:root,left,right
	 * 
	 */
	public void preOrderTraversal1() {
		Node temp = root;
		if (temp == null) {
			return;
		}
		// 使用栈存储访问的结点
		Stack<Node> stack = new Stack<>();
		while (temp != null || !stack.isEmpty()) {
			if (temp != null) {
				stack.push(temp);
				// 访问root
				printNodeValue(temp.value);
				// 访问left
				temp = temp.left;
			} else {
				temp = stack.pop();
				// 访问right
				temp = temp.right;
			}
		}
	}

5.2中序遍历

中序遍历-递归

	/**
	 * 中序遍历:递归方法:left,root,right
	 * 
	 */
	public void inOrderTraversal() {
		inOrderTraversal(root);
	}

	/**
	 * 中序遍历:递归方法
	 * 
	 * @param node 遍历到的结点
	 */
	private void inOrderTraversal(Node node) {
		if (node == null)
			return;
		// 访问left
		inOrderTraversal(node.left);
		// 访问root
		printNodeValue(node.value);
		// 访问right
		inOrderTraversal(node.right);
	}

中序遍历-非递归


	/**
	 * 中序遍历:非递归:left,root,right
	 */
	public void inOrderTraversal1() {
		Node temp = root;
		if (temp == null) {
			return;
		}
		// 2.使用栈存储访问的结点
		Stack<Node> stack = new Stack<>();
		while (temp != null || !stack.isEmpty()) {
			// 1.访问left
			if (temp != null) {
				stack.push(temp);
				temp = temp.left;
			} else {
				temp = stack.pop();
				// 2.访问root
				printNodeValue(temp.value);
				// 3.更改指针,去访问right
				temp = temp.right;
			}
		}
	}

5.3后序遍历

后序遍历-递归

/**
	 * 后序遍历:递归方法:left,right,root
	 * 
	 */
	public void postOrderTraversal() {
		postOrderTraversal(root);
	}

	/**
	 * 后序遍历:递归方法:left,right,root
	 * 
	 * @param node 遍历到的方法
	 */
	private void postOrderTraversal(Node node) {
		if (node == null)
			return;
		postOrderTraversal(node.left);
		postOrderTraversal(node.right);
		printNodeValue(node.value);
	}

后序遍历-非递归

这里要解决两个问题
1.怎么判断左子树遍历完.
计算一个标记,只要已经打印的阶段,那么在访问上一个结点的话,那么其左子树就是已经访问完了,所以使用了isPeekNode变量,每次打印结点时,给其置为true.当访问未访问的结点时,再置为false.
2.怎么判断右子树遍历完.
如果上一个打印的结点是当前访问结点的右结点,那么说明右子树已经遍历完成,那么就不必再访问右节点,所以此时打印此节点即可.
注意点:
当此节点的左子树访问完毕后,该访问此结点的右结点,那么需要通过此结点访问其右结点,所以要再次把当前结点添加到栈中.再次访问其右节点.

/**
	 * 后序遍历:非递归:left,right,root
	 */
	public void postOrderTraversal1() {
		Node temp = root;
		if (temp == null) {
			return;
		}
		Stack<Node> stack = new Stack<>();
		Node lastNode = null;// 为了去判断右子树是否已经访问完毕
		boolean isPeekNode = false;// 为了去判断左子树是否已经访问完毕
		while (temp != null || !stack.isEmpty()) {
			// 1.访问left:
			// isPeekNode表示temp结点的左子树已经遍历完成,不必再遍历左侧
			if (temp != null && !isPeekNode) {
				stack.push(temp);// 入栈
				temp = temp.left;
			} else {
				temp = stack.pop();// 出栈
				// 2.访问right:
				// temp.right != null:有右节点,
				// temp.right == lastNode:表示右节点已经访问
				if (temp.right != null && temp.right != lastNode) {
					stack.push(temp);// 右子树未访问的,所以要把当前结点再入栈
					temp = temp.right;
					isPeekNode = false;
				} else {
					// 3.访问根结点:表示当前temp为根结点的树,已经访问完毕
					printNodeValue(temp.value);//
					lastNode = temp;
					if (stack.isEmpty()) {// 这里是遍历到了最后一个结点
						return;
					}
					temp = stack.peek();// 访问栈顶元素:继续往上遍历
					isPeekNode = true;
				}

			}
		}
	}

5.4层序遍历


6.树高

树高-递归

每次都要计算出左右子树的最大值,然后+1,直到遍历到叶子结点

/**
	 * 获取树的高度:递归
	 * 
	 * @return
	 */
	public int height() {
		// 树的高度,其实就是根结点的高度
		return height(root);
	}

	/**
	 * 获取某个结点的高度:递归的方法 就是当前结点到叶子结点的路径最长的值
	 * 
	 * @param node
	 * @return
	 */
	private int height(Node node) {
		if (node == null)
			return 0;
		return 1 + Math.max(height(node.left), height(node.right));
	}

树高-非递归

按照层级遍历的思想,每遍历完一层,那么高度+1.
使用一个队列存储遍历过的结点.
levelSize:记录当前层级结点个数


	/**
	 * 非递归
	 * 
	 * @return
	 */
	public int height1() {
		return height1(root);
	}

	/**
	 * 获取某个结点的高度:就是当前结点到叶子结点的路径最长的值 使用层次遍历
	 * 
	 * @param node
	 * @return
	 */
	private int height1(Node node) {
		if (node == null)
			return 0;

		// 树的高度
		int height = 0;
		// 存储着每一层的元素数量
		int levelSize = 1;
		Queue<Node> queue = new LinkedList<>();
		queue.offer(root);
		Node tempNode;
		while (!queue.isEmpty()) {
			tempNode = queue.poll();
			levelSize--;
			if (tempNode.left != null) {
				queue.offer(tempNode.left);
			}
			if (tempNode.right != null) {
				queue.offer(tempNode.right);
			}
			if (levelSize == 0) { // 意味着即将要访问下一层
				levelSize = queue.size();
				height++;
			}
		}
		return height;
	}

7.调用示例

class TestEx {

	public static void main(String[] args) {
		method();
	}

	public static void method() {
		int nums[] = new int[] { 56, 76, 32, 43, 21, 25, 88, 80 };
		ExBinaryTree tree = new ExBinaryTree();
		for (int i = 0; i < nums.length; i++) {
			tree.add(nums[i]);
		}
		System.out.println("\n-----------------------");
		System.out.println("树的高度:");
		System.out.println("递  归:" + tree.height());
		System.out.print("非递归:" + tree.height1());
		System.out.println("\n-----------------------");
		System.out.println("前序遍历:");
		System.out.print("递  归:");
		tree.preOrderTraversal();
		System.out.print("\n非递归:");
		tree.preOrderTraversal1();
		System.out.println("\n-----------------------");
		System.out.println("中序遍历:");
		System.out.print("递  归:");
		tree.inOrderTraversal();
		System.out.print("\n非递归:");
		tree.inOrderTraversal1();
		System.out.println("\n-----------------------");
		System.out.println("中序遍历:");
		System.out.print("递  归:");
		tree.postOrderTraversal();
		System.out.print("\n非递归:");
		tree.postOrderTraversal1();
		System.out.println("\n-----------------------");
		System.out.print("层序遍历:");
		tree.levelOrderTraversal();
	}

}

8.总结
对于求遍历和树高的方法中,最好用非递归方式,因为递归方式不断的方法嵌套调用,是一个压栈和出栈过程,比较耗费内存,如果结点个数比较多,树高比较大,那么递归方法会开辟更大的内存,有栈溢出的风险.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值