数据结构之二叉树

二叉树

二叉树的定义

  在树的基础上加上两个限制条件即可得到二叉树。
(至于什么是度,什么是树等,可以预先了解下再开始下面关于二叉树的内容)

  • 每个子结点最多有两棵子树,二叉树中结点的度只能为0、1、2。
  • 子树有左右区别,不能颠倒。

  关于二叉树的形态主要关注两个,一个是满二叉树,另一个是完全二叉树;

  • 满二叉树:叶子结点(终端结点)都在二叉树的最下面一层。
  • 完全二叉树:各个结点编号与深度相同的满二叉树中的结点编号均相同。
  • 联系: 完全二叉树就是由满二叉树由右至左、由下至上依次删除结点所得,这里不能跳着删除结点,跳着删除就不是完全二叉树。

左边为满二叉树右边不是完全二叉树
  上图中的左边是满二叉树,而右边就不是所谓的完全二叉树。

二叉树的主要性质

  • 性质1:非空二叉树上的叶子节点等于双分支结点数+1
    n0 = n2 + 1( 叶子结点数为 n0 单叶子结点数为 n1 双叶子节点数 n2)
  • 性质2:二叉树的第 i 层上最多由2 i-1(i ⩾ \geqslant 1) 个结点
  • 性质3:高度(深度)为 k 的二叉树最多由2 k-1(k ⩾ \geqslant 1)个结点
  • 性质4:函数Catalan():给定 n 个结点,能构成H(n)种不同的二叉树,代码: H ( x ) = C 2 n n n + 1 H(x)=\frac{C^n_2n }{n+1} H(x)=n+1C2nn
  • 性质5:具有 n (n)个结点的完全二叉树的高度(深度)为 ⌊ l o g 2 n ) ⌋ \lfloor log_2n) \rfloor log2n)+1

二叉树的存储结构

  如果前面有关于链表或者队列那两篇博客的同学,这边应该大概明白二叉树的存储结构也分为两种,分别是顺序存储和链式存储。
  1.顺序存储结构即利用数组来存储二叉树,这里一般常用于存储完全二叉树,存储一般二叉树相对比较浪费空间,因为顺序存储所分配的内存是连续的。不过这样存储仅仅只能存储结点编号信息,局限性比较大!
  2.链式存储结构即利用链接的方式将一个个结点连接起来。具体的结点类型定义如下:

typedef struct BTNode
{
	int data; // // data store the node's data field (default type int)
	struct BTNode *lchild;
	struct BTNode *rchild;
}BTNode;

二叉树的遍历算法

前序遍历

  前序遍历的操作过程如下:
    如果二叉树为空树则不需要进行,
  否则:
    1) 访问根节点
    2) 前序遍历左子树
    3) 前序遍历右子树

/*****************************************************************************
 Function    : Operate_BTNode
 Description : operate the node ,like print the data
 Input       : BTNode p
 Output      : void
 Return      : void
 *****************************************************************************/
 
void Operate_BTNode(BTNode *p)
{
	cout << "the p->data is :" << p->data << endl;
	cout << "the p->lchild is :" << p->lchild << endl;
	cout << "the p->rchild is :" << p->rchild << endl;
}

/*****************************************************************************
 Function    : preorder
 Description : the preorder travesal
 Input       : BTNode p
 Output      : void
 Return      : void
 *****************************************************************************/
void preorder(BTNode *p)
{
	if(p != NULL) // condition judgement (whether the p is NULL)
	{
		Operate_BTNode(p);  // show the info in p
		preorder(p->lchild);
		preorder(p->rchild);
	}
}

中序遍历

  中序遍历的操作过程如下:
    如果二叉树为空树则不需要进行,
  否则:
    1) 中序遍历左子树
    2) 访问根节点
    3) 中序遍历右子树

/*****************************************************************************
 Function    : inorder
 Description : the inorder travesal
 Input       : BTNode p
 Output      : void
 Return      : void
 *****************************************************************************/
void inorder(BTNode *p)
{
	if(p != NULL) // condition judgement (whether the p is NULL)
	{
		preorder(p->lchild);
		Operate_BTNode(p);  // show the info in p
		preorder(p->rchild);
	}
}

后序遍历

  后序遍历的操作过程如下:
    如果二叉树为空树则不需要进行,
  否则:
    1) 后序遍历右子树
    2) 后序遍历左子树
    3) 访问根节点

/*****************************************************************************
 Function    : postorder
 Description : the postorder travesal
 Input       : BTNode p
 Output      : void
 Return      : void
 *****************************************************************************/
void postorder(BTNode *p)
{
	if(p != NULL) // condition judgement (whether the p is NULL)
	{
		preorder(p->lchild);
		preorder(p->rchild);
		Operate_BTNode(p);  // show the info in p
	}
}

层序遍历

  层序遍历比较容易理解,按照树一层层来进行遍历。
层序遍历

/*****************************************************************************
 Function    : level
 Description : the level travesal
 Input       : BTNode p
 Output      : void
 Return      : void
 *****************************************************************************/
void postorder(BTNode *p)
{
	int front = 0,rear = 0;
	BTNode *queue[MaxSize];  // define a queue
	BTNode *q;
	if(p != NULL)
	{
		rear = (rear+1) % MaxSize;
		queue[rear] = p;    // enqueue the root node
		while(front != rear)  // the queue isn't empty
		{
			front = (front+1) % MaxSize;
			q = queue[front];
			Operate_BTNode(p);
			if(q->lchild != NULL)
			{
				rear = (rear+1) % MaxSize;
				queue[rear] = q->rchild;
			}
			if(q->rchild != NULL)
			{
				rear = (rear+1) % MaxSize;
				queue[rear] = q->rchild;
			}
		}
	}
}

思考

  上面三种遍历方式是利用递归来实现的,相对于非递归的遍历效率肯定低不少,后面会再进一步引入优化算法。主要通过上面的递归算法让我们了解下二叉树的前中后序三种遍历方式的具体过程。
  接下来通过实例来回顾下遍历方式:
  下图的前中后序遍历的结果分别是什么呢?
前中后序遍历
  按照前面的步骤来是说,答案是显而易见的:

  • 前序遍历输出序列为:A,B,C,D,E,F,G,H
  • 中序遍历输出序列为:C,B,E,D,F,A,H,G
  • 后续遍历输出序列为:C,E,F,D,B,H,G,A
  • 层序遍历输出序列为:A,B,G,C,D,H,E,F
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我蘑菇先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值