数据结构 二叉树及遍历二叉树

概述

二叉树,是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。

这里写图片描述

二叉树的特性

  • 在非空二叉树中,第i层的结点总数不超过2^(i-1) , i>=1;
  • 深度为h的二叉树最多有2^h个结点(h>=1),最少有h个结点;
  • 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
  • 具有n个结点的完全二叉树的深度为
  • 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
    若I为结点编号则 如果I>1,则其父结点的编号为I/2;
    如果2I<=N,则其左儿子(即左子树的根结点)的编号为2I;若2I>N,则无左儿子;
    如果2
    I+1<=N,则其右儿子的结点编号为2I+1;若2I+1>N,则无右儿子。
  • 给定N个节点,能构成h(N)种不同的二叉树。h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。
  • 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i[4]

二叉树的分类

斜树

所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。所以,线性表的数据结构可以理解成树的一种表达方式。

这里写图片描述

满二叉树

在一棵树中,如果所有的分支节点都存在左子树和右子树
,并且所有的叶子节点都在同一层上,那么这样的二叉树称之为满二叉树。

这里写图片描述

完全二叉树

对一颗具有n个节点的二叉树按层序编号,如果编号为i(1<= i <=n)的节点与同样深度的满二叉树中编号为i的节点的位置完全相同,那么这样的树,称之为完全二叉树。

这里写图片描述

二叉树的存储结构

这里写图片描述

顺序存储结构:
这里写图片描述

二叉链表:
这里写图片描述

二叉树的遍历

二叉树的数据结构中,常用的遍历方式有三种:

  • 先序遍历
  • 中序遍历
  • 后序遍历

当然,二叉树还有一种不常见的遍历方式-层序遍历,这里暂不做介绍。

在树的遍历中,我们通常使用的是通过递归方式来进行遍历。

先序遍历

规则是若二叉树为空,则空操作返回,否则先访问跟结点,然后先序遍历左子树,再先序遍历右子树。

这里写图片描述

代码实现如下:

	/**
	 * 先序遍历
	 * 
	 * parent->left->right
	 */
	public void preOrder(TreeNode node) {
		if (node == null) {
			return;
		}

		System.out.print(node.data + "\t");
		preOrder(node.leftChild);
		preOrder(node.rightChild);
	}

中序遍历

规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。

这里写图片描述

代码实现如下:

	/**
	 * 中序遍历
	 * 
	 * left->parent->right
	 */
	public void midOrder(TreeNode node) {
		if (node == null) {
			return;
		}

		midOrder(node.leftChild);
		System.out.print(node.data + "\t");
		midOrder(node.rightChild);
	}

后序遍历

规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。

这里写图片描述

代码实现如下:

	/**
	 * 后序遍历
	 * 
	 * left->right->parent
	 */
	public void postOrder(TreeNode node) {
		if (node == null) {
			return;
		}

		postOrder(node.leftChild);
		postOrder(node.rightChild);
		System.out.print(node.data + "\t");
	}

Java实现二叉树

package structdemo;

/**
 * 
 * 二叉树
 * 
 * @author zhangke
 */
public class BinaryTree {

	// 创建二叉树并调用遍历方法
	public static void main(String[] args) {

		BinaryTree binaryTree = new BinaryTree();
		binaryTree.createBinaryTree();
		System.out.println("height:" + binaryTree.getHeight());
		System.out.println("size:" + binaryTree.getSize());

		binaryTree.preOrder(binaryTree.root);
		binaryTree.midOrder(binaryTree.root);
		binaryTree.postOrder(binaryTree.root);

	}

	/**
	 * 根节点
	 */
	private TreeNode root = null;

	public BinaryTree() {
		root = new TreeNode(1, "A");
	}

	/**
	 * 构建二叉树 
	 * 
	 *       A 
	 *       
	 *   B      C 
	 * 
	 * D    E       F
	 */
	public void createBinaryTree() {
		TreeNode nodeB = new TreeNode(2, "B");
		TreeNode nodeC = new TreeNode(3, "C");
		TreeNode nodeD = new TreeNode(4, "D");
		TreeNode nodeE = new TreeNode(5, "E");
		TreeNode nodeF = new TreeNode(6, "F");
		root.leftChild = nodeB;
		root.rightChild = nodeC;
		nodeB.leftChild = nodeD;
		nodeB.rightChild = nodeE;
		nodeC.rightChild = nodeF;
	}

	/**
	 * 获取树的深度
	 * 
	 * @return
	 */
	public int getHeight() {
		return getHeight(root);
	}

	/**
	 * 获取指定结点的深度
	 */
	private int getHeight(TreeNode node) {
		if (node == null) {
			return 0;
		}

		// 每当一个结点存在子结点时,Height就加一个
		int i = getHeight(node.leftChild);
		int j = getHeight(node.rightChild);
		return (i > j) ? (i + 1) : (j + 1);
	}

	/**
	 * 获取结点个数
	 * 
	 * @return
	 */
	public int getSize() {
		return getSize(root);
	}

	private int getSize(TreeNode node) {
		if (node == null) {
			return 0;
		}
		return 1 + getSize(node.leftChild) + getSize(node.rightChild);
	}

	/**
	 * 先序遍历
	 * 
	 * parent->left->right
	 */
	public void preOrder(TreeNode node) {
		if (node == null) {
			return;
		}

		System.out.print(node.data + "\t");
		preOrder(node.leftChild);
		preOrder(node.rightChild);
	}

	/**
	 * 中序遍历
	 * 
	 * left->parent->right
	 */
	public void midOrder(TreeNode node) {
		if (node == null) {
			return;
		}

		midOrder(node.leftChild);
		System.out.print(node.data + "\t");
		midOrder(node.rightChild);
	}

	/**
	 * 后序遍历
	 * 
	 * left->right->parent
	 */
	public void postOrder(TreeNode node) {
		if (node == null) {
			return;
		}

		postOrder(node.leftChild);
		postOrder(node.rightChild);
		System.out.print(node.data + "\t");
	}

	/**
	 * 定义树的结点
	 *
	 * @author zhangke
	 */
	public class TreeNode {

		/**
		 * 下标
		 */
		private int index;
		/**
		 * 数据
		 */
		private String data;
		/**
		 * 左孩子
		 */
		private TreeNode leftChild;
		/**
		 * 右孩子
		 */
		private TreeNode rightChild;

		public TreeNode(int index, String data) {
			this.index = index;
			this.data = data;
		}
	}

}

常见面试题

已知,某树的先序遍历为:{ 4, 2, 1 ,0, 3, 5, 9, 7, 6, 8 },中序遍历为: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },请画出该树。

在解题之前我们可以明确一个概念:二叉树中序遍历加任意一种其他遍历方式都可以确定一棵树。
解释:因为不论是先序遍历、后序遍历或者是层序遍历,都只能确定一颗树的根节点,在确定根节点后通过中序遍历就能区分树的左右子树,这样通过递归的思想就能确定一棵树了。

解题步骤如下:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值