二叉树笔记

常用术语

  1. 根:二叉树是由唯一的起始结点引出的结点集合,这个起始结点称为根。
  2. 父结点(双亲):任何非根结点都有且仅有一个前驱结点,称为该结点的父结点(双亲)。根结点没有父结点。
  3. 左/右子结点:二叉树的任何结点最多可能有两个后继结点,分别称为左/右子结点(或左/右海子)。具有相同父结点的结点之间称为兄弟结点。
  4. 结点的度:二叉树中一个结点的子树数目称为该结点的度。
  5. 叶结点:没有子结点的结点称为叶结点。叶结点的度为0。除叶结点外的那些非终端结点称为内部结点(或分支结点)
  6. 边:父结点k与子结点k'之间存在一条有向线段<k,k'>,称作边。
  7. 路径:两个结点间相连边的集合,该路径所经历的边的个数称为这条路径的路径长度。
  8. 结点的层数:从根结点到某个结点的路径长度称为结点的层数,根结点为第0层,非根结点的层数是其父结点的层数加1。
  9. 树的深度:层数最大的叶结点的层数称为树的深度。
  10. 树的高度:树的深度加1为树的高度。
  11. 子树:切断一个结点与其父结点的连接,则该结点与其子孙构成的树就称为以该结点为根的子树。

满二叉树和完全二叉树 

满二叉树:一棵二叉树的任何结点,或者是树叶,或者左右子树均非空。

完全二叉树:除最后一层外,每一层上的结点数均达到最大值,最后一层上只缺少右边的若干结点。



二叉树的性质 

性质1:二叉树的第i层上至多有2^{i}个结点。(根结点为第0层)

性质2:深度为k的二叉树至多有2^{k+1}-1个结点。

性质3:在任意二叉树中,若叶子结点(即度为0的结点)个数为n0,度为1的结点个数为n1,度为2的结点个数为n2,那么n0 = n2 + 1。

性质4:满二叉树定理:非空满二叉树的树叶数目等于其分支结点数加1。

性质5:满二叉树定理推论:一个非空满二叉树的空子树数目等于其结点数加1。

性质6:具有n个结点的完全二叉树深度为[log_{2}(n+1)]-1,高度为[log_{2}(n+1)]

性质7:若对于有n个结点的完全二叉树进行顺序编号\left (0\leq i\leq n-1 \right ),那么对于编号为i的结点:

父亲:

如果i = 0 ,则结点i是二叉树的根,无双亲;

如果i > 0 ,则其父结点编号是[\left ( i-1 \right )/2];

孩子:

当2i+1<=n-1时,结点i的左孩子是2i+1,否则结点i没有左孩子。

当2i+2<=n-1时,结点i的右孩子是2i+2,否则结点i没有右孩子。

二叉树的储存顺序

顺序存储和链式存储

1.顺序存储结构

对于完全二叉树,从根结点起,自上而下,从左至右存储,即按顺序编号存储。根据完全二叉树性质,对于任意一个结点,若它存储在第i个位置,则它的左右子结点存放在2i+1和2i+2的位置,其父结点的下标为[\left ( i-1 \right )/2]

顺序存储是完全二叉树最简单、最节省空间的一种存储方式。

对于一般二叉树,也必须按照完全二叉树的形式来存储树中的结点。

 2.链式存储结构

根据结点结构的不同,有两种方法:二叉链表、三叉链表。

struct node
{
    struct node *lchild;
    char c;
    struct node *rchild;
}Node,*Pnode;

 

struct node
{
    struct node *lchild;
    char c;
    struct node *parent;
    struct node *rchild;
};

 二叉树的遍历

二叉树的遍历分为:深度优先遍历和广度优先遍历

深度优先遍历

前序遍历、中序遍历、后续遍历

根左右

左根右

左右根

1)前序遍历

(1)访问根结点;

(2)按前序遍历左子树;

(3)按后序遍历右子树;

2)中序遍历

(1)按中序遍历左子树;

(2)访问根结点;

(3)按后续遍历右子树;

3)后续遍历

(1)按后序遍历左子树;

(2)按后序遍历右子树;

(3)访问根结点;

线索二叉树

在n个结点的二叉树中,必定有n+1个空链域

含有n个结点的二叉链表中,链域一共有2*n个(每个点有两个链域)。

对于除了根结点以外的每个点都是有一个父亲结点,所以一共有n-1个指针指向某个结点,

于是形成n-1个有内容的链域(减1即是父亲结点)所以一共有2*n-(n-1)=n+1个链域没有指向任何东西。

  • 若结点的左子树为空,则该结点的左孩子指针指向其前驱结点
  • 若结点的右子树为空,则该结点的右孩子指针指向其后继结点

这种指向前驱和后继的指针称为线索,将一棵普通的二叉树以某种次序遍历,并添加线索的过程称为线索化。

 

  • ltag==0,指向左孩子;ltag==1,指向前驱结点
  • rtag==0,指向右孩子;rtag==1,指向后继结点

#include<stdio.h>
#include<stdlib.h>
//二叉树结构的定义
typedef struct BinaryTreeNode{
	int m_nValue;
	struct BinaryTreeNode *m_pLeft;
	struct BinaryTreeNode *m_pRight;
}BinaryTreeNode;
 
//构建二叉树的核心函数,前序的子串起止位置和中序的子串起止位置
BinaryTreeNode * ConstructCore(int *startPreorder, int *endPreorder, int *startInorder, int *endInorder);
 
//构建二叉树的函数,前序遍历和中序遍历的起始位置与长度
BinaryTreeNode *Construct(int *preorder, int *inorder, int length)
{
	if(preorder == NULL || inorder == NULL || length <= 0) {
		printf("invalid input!\n");
		return 0;
	}
 
	return ConstructCore(preorder, preorder + length -1, inorder, inorder + length - 1);
}
 
BinaryTreeNode *ConstructCore(int *startPreorder,int *endPreorder,int *startInorder,int *endInorder)
{
    //前序遍历的第一个元素,即为根节点
	int rootValue = startPreorder[0];
    //构建根节点
	BinaryTreeNode * root = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
	root->m_nValue = rootValue;
	root->m_pLeft = root->m_pRight = NULL;
 
    //顺序遍历到最后一个元素
	if (startPreorder == endPreorder)
	{
        //该节点满足是根节点的条件如括号中所示
		if (startInorder == endInorder && *startPreorder == *startInorder) {
            return root;
        } else {
            printf("invalid input!\n");
        }	
	}
 
    //根据前序遍历中的根节点的值,在中序遍历中找到根节点
	int* rootInorder = startInorder;
	while(rootInorder <= endInorder && *rootInorder != rootValue) {
        ++rootInorder;
    }	
 
	if(rootInorder == endInorder && *rootInorder != rootValue) {
        printf("invalid input!\n");
    }
 
    //在中序数组中找到它的左子树的长度
	int leftLength = rootInorder - startInorder;
    //根据中序遍历中左子树的长度在前序遍历中找到左子树的位置
	int* leftPreorderEnd = startPreorder + leftLength;
 
	if(leftLength > 0){
        //构建左子树,输入为前序遍历子串的起始地址,中序遍历的起始地址
		root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1);
	}
 
	if(leftLength < (endPreorder - startPreorder))
    {
        //构建右子树,输入为前序遍历子串的起始地址,中序遍历的起始地址
		root->m_pRight = ConstructCore(leftPreorderEnd + 1,endPreorder, rootInorder + 1, endInorder);
    }
 
	return root;
}
 
void lastOrderTraverse(BinaryTreeNode *root)
{
	if(root)
	{
		lastOrderTraverse(root->m_pLeft);
		lastOrderTraverse(root->m_pRight);
		printf("%d ",root->m_nValue);
	}
}
 
int main()
{
	int preorder[8] = {1, 2, 4, 7, 3, 5, 6, 8};
	int inorder[8] = {4, 7, 2, 1, 5, 3, 8, 6};
	int length = sizeof(preorder) / sizeof(int);
	BinaryTreeNode *tree = Construct(preorder, inorder, length);
	lastOrderTraverse(tree);
 
	return 0;
}

堆是一种特殊类型的二叉树,具有以下两个性质:

(1)每个节点的值大于(或小于)等于其每个子节点的值;

(2)该树完全平衡,其最后一层的叶子都处于最左侧的位置。 满足上面两个性质定义的是大顶堆(max heap)(或小顶堆min heap)。这意味着大顶堆的根节点包含了最大的元素,小顶堆的根节点包含了最小的元素

由于堆是一个完全树,一般采用数组实现,对于一个下标为i的结点:

其父结点下标为:(i-1)/2

其子结点下标为:2i+1,2i+2

  • heap[i] ≧heap[2*i+1]
  • heap[i] ≧heap[2*i+2] 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值