二叉树的定义:
二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的,分别被称为根结点的左子树和右子树的二叉树组成。
二叉树的特点:
(1)每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。
(2)左子树和右子树是由顺序的,次序不能任意颠倒。
(3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
二叉树的五中基本形态:
(1)空二叉树
(2)只有一个根结点
(3)根结点只有左子树
(4)根结点只有右子树
(5)根结点既有左子树又有右子树
特殊的二叉树:
(1)斜树,所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。
(2)满二叉树,在一棵二叉树中,如果分支都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
满二叉树的特点:
一、叶子只能出现在最下一层,出现在其他层就不可能达成平衡。
二、非叶子结点的度一定是2。
三、在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
(3)完全二叉树,对一棵具有n个结点的二叉树按层序编号,如果编号i(1<=i<=n)的结点与同样深度的满二叉树中的编号为i的结点在二叉树中的位置完全相同,则这颗二叉树称为完全二叉树。
完全二叉树的特点:
一、叶子结点只能出现在最下的两层。
二、最下层的叶子一定集中在左部连续的位置。
三、倒数二层,若有叶子节点,一定都在右部连续的位置。
四、如果结点度为1,则该结点只有左孩子。
五、同样结点数的二叉树,完全二叉树的深度最小。
满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。
二叉树的性质:
(1)在二叉树的第i层上至多右2^(i-1)个结点。满二叉树的每一层结点数为2^(i-1)。
(2)深度为k的二叉树至多有2^(k)-1个结点。满二叉树的总结点数为2^(k)-1。
(3)对任何一棵二叉树T,如果其终段节点数为n0,度为2的结点数位n2,则n2+1=n0。
(4)具有n个结点的完全二叉树的深度为(表示不大于X的最大整数)。
(5)如果对一棵n个结点的完全二叉树(其深度位)的结点按层序编号(从第一层到第层,每层从左到右),对任一结点i(1<=i<=n)有:
一、如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲的编号是。
二、如果2*i>n,则结点i无左孩子(结点i位叶子结点);否则其左孩子是结点2*i。
三、2*i+1>n,则结点i无右孩子;否则其右孩子是结点2*i+1。
二叉树的存储:
(1)顺序存储:如果一个数是完全二叉树,则从根结点开始对二叉树进行从上到下、从左至右的编号。其中根结点的编号为0,其他结点依此加1。编号就是二叉树在顺序表中的下标,如果不是完全二叉树,则将二叉树补成完全二叉树(没有的位置记为NULL或树中不存在的数据),再进行存储。
(2)链式存储:二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,称这样的结构为二叉链表。
LChild | data | RChild |
二叉树的构造:
从树的概念中我们就可以看出,树是一种递归的的结构。我们可以利用递归的方法创建一棵树。
二叉树的遍历:
二叉树的遍历是指从根结点出发,按照某种次序依此访问二叉树中的所有结点,使得那个节点被访问一次且仅被访问一次。
二叉树的遍历方法:
(1)前序遍历:规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。(根左右)
(2)中序遍历:规则是若二叉树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
(3)后序遍历:规则是若二叉树为空。则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。
(4)层序遍历:规则是若二叉树为空。则空操作返回,否则从树的第一层,也就是根结点开始访问,从上到下逐层遍历,在同一层,按照从左到右的顺序对结点逐个访问。
#include <stdio.h>
#include <malloc.h>
typedef char ElemType;
typedef struct _BinaryTreeNode
{
ElemType data;
_BinaryTreeNode *rightChild, *leftChild;
}BinaryTreeNode;
typedef BinaryTreeNode* BinaryTree;
//创建二叉树
void createBitTree(BinaryTree* root)
{
ElemType value;
scanf("%c", &value);
if ('0'== value)
{
(*root) = NULL;
return;
}
*root = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
(*root)->rightChild = NULL;
(*root)->rightChild = NULL;
(*root)->data = value;
createBitTree(&(*root)->leftChild);
createBitTree(&(*root)->rightChild);
}
//先序遍历
void preorderTraversal(BinaryTree root)
{
if (NULL == root)
{
printf(" ,");
return;
}
printf("%c,", root->data);
preorderTraversal(root->leftChild);
preorderTraversal(root->rightChild);
}
//中序遍历
void sequentialTraversal(BinaryTree root)
{
if (NULL == root)
{
printf(" ,");
return;
}
sequentialTraversal(root->leftChild);
printf("%c,", root->data);
sequentialTraversal(root->rightChild);
}
//后序遍历
void postOrderTraversal(BinaryTree root)
{
if (NULL == root)
{
printf(" ,");
return;
}
postOrderTraversal(root->leftChild);
postOrderTraversal(root->rightChild);
printf("%c,", root->data);
}
int main()
{
BinaryTree root=NULL;
createBitTree(&root);
puts("先序遍历:");
preorderTraversal(root);
puts("\n中序遍历:");
sequentialTraversal(root);
puts("\n后序遍历:");
postOrderTraversal(root);
return 0;
}
线索二叉树:在创建二叉树时一共创建了2n个指针域,n为结点数,其中使用了n-1个,还有n+1个没有使用,这就非常浪费。那如何利用这些空的指针域那。这就是我们的线索二叉树。
线索二叉树就是将中序遍历结构中的后继结构存储在该结点的右孩子中。这样在一次中序遍历后,我们在进行中序遍历便可以直接通过该结点的右孩子取到中序遍历中该结点的后继结点了,就会快很多。
对树的操作还有很多:
森林、树和二叉树的转化。
哈夫曼编码等等。