二叉树是一种特殊的树形结构,这篇博文主要介绍二叉树的特点和二叉树的操作。
介绍二叉树
基础概念
节点
节点是一个结构体,在二叉树中节点专指树节点,树节点在C中的一般定义形式为
struct Node{
struct Node *left;
int value;
struct Node *right;
};
根据代码,我们可以看见一个节点主要由三部分组成:左分支,序号,右分支。除这些外,可以自由加入储存信息的内容,但这三个部分是必要的。形象地说,节点就像是一个伸出两个分支的树枝,多个这样的节点一起构成一个树。
父节点、子节点和兄弟节点
父节点与子节点对应,直白地说,相互连接的两个节点,在上面的节点称为在下面的节点的父节点,在下面的节点称为在上面的节点的子节点。
兄弟节点,直白地说就是有同一个父节点的多个节点。
以上图为例,C的子节点有E和F,D的子节点有G、H和I,B和C为兄弟节点。
- 一个父节点可以有多个子节点
- 一个子节点只能有一个父节点
- 一个节点可以既为父节点,又为子节点
树的深度和节点的度
树是一种由上至下、分层,每层的节点数呈指数增加的结构,一个树的层数就是树的深度。
以下图为例,我们可以说树的深度为4。
一个节点有几个子节点成为这个节点的度。以上图为例,A的度为2,E的度为1,D的度为3。对于二叉树而言,节点的度只能为0、1、2。
认识二叉树
二叉树的特点
- 对于一个二叉树父节点的子节点,左侧的子节点称为左子节点,右侧的子节点称为右子节点。
- 左子节点<父节点<右子节点。
- 即使一个父节点只有一个子节点或没有子节点,子节点依然有左右之分。
- 以左子节点为根节点的子树为左子树,以右子节点为根节点的子树为右子树。
二叉树的形状
这是最普通的二叉树,符合二叉树的所有定义,但没有特殊的性质。
上面的两个树称为斜树,对于二叉树搜索,斜树是最费时的结构,因此需要对斜树进行平衡,这在后面的博文中会有介绍。
这是满二叉树,是最理想的二叉树结构。在满二叉树中,叶节点(无子节点的节点)只出现在最下面一层并且最下面一层全是叶节点。每个父节点的度都为2。
这是完全二叉树,如果对一棵树的节点按层、自左至右进行编号i(1<=i<=n),每一个节点都与同样深度的满二叉树对应,则称这个树为完全二叉树。完全二叉树有许多性质。
- 叶节点只出现在最下层和次下层
- 最下层叶节点集中在左侧
- 次下层叶节点集中在右侧
- 左子树与右子树的深度差<=1
- 如果节点度为1,则节点只有左子节点,没有右子节点
二叉树的操作
建立二叉树
对于节点的序号,我们规定在二叉树中的所有节点满足左子节点<父节点<右子节点,因此,若我们想要插入一个新的节点,就需要根据这个特点找到新的节点应该在的位置,并连接新插入的节点和它的父节点。
struct Node *planttree()
{
int v;
struct Node *pre,*current,*p,*root;
printf("we will define the root,please give me a number\n");
scanf("%d",&v);
root=(struct Node *)malloc(sizeof(struct Node)); /*建立根节点*/
(*root).left=NULL;
(*root).value=v;
(*root).right=NULL;
printf("please add a number\n");
scanf("%d",&v);
while(v) /*只要v不为负数,就继续插入节点,结束插入的条件可以更改*/
{
p=(struct Node *)malloc(sizeof(struct Node)); /*建立新插入的节点*/
(*p).left=NULL;
(*p).value=v;
(*p).right=NULL;
pre=root; /*开始寻找新插入的节点应该在的位置*/
current=pre;
while(current) /*只要current还未指向NULL*/
{
pre=current; /*pre移动到current的位置*/
if(v>(*pre).value) /*若v比当前节点的序号大,current则移动到当前节点的右子节点*/
{
current=(*pre).right;
}
if(v<(*pre).value) /*若v比当前节点的序号小,current则移动到当前节点的左子节点*/
{
current=(*pre).left;
}
if(v==(*pre).value) /*若v与当前节点的序号一致,说明序号已存在,终止循环,停止寻找*/
{
break;
}
}
if((*pre).value<v) /*连接新插入的节点和它的父节点*/
{
(*pre).right=p;
}
if((*pre).value>v)
{
(*pre).left=p;
}
if(v==(*pre).value)
{
printf("this value has existed\n");
}
printf("please add a number\n");
scanf("%d",&v);
}
return root;
}
二叉树的插入操作并不难,与链表的插入区别不大,只是链表寻找插入的位置是逐个寻找,二叉树寻找插入的位置会有左右的选择。
这样插入新的节点,新的节点一定是叶节点。当然在保证二叉树的节点的序号正确的前提下,可以把节点插入二叉树两个节点之间,只是这样会比较麻烦。
遍历二叉树
遍历二叉树两种方法,其中一种为层序遍历,即自上而下对二叉树逐层遍历;另一种方法有三种表现形式,分别为前序、中序和后序。
前序遍历
A-B-D-H-I-E-J-C-F-G
void print(struct Node *root)
{
if(root==NULL)
{
return;
}
printf("%d\n",