二叉树的存储结构主要了解二叉链表结构,也就是一个数据域,两个指针域,(分别为指向左右孩子的指针)
typedef char DataType;
typedef struct BinTreeNode
{
//左孩子
struct BinTreeNode* pleft;
//右孩子
struct BinTreeNode* pright;
//数据区
DataType data;
}BinTreeNode;
在本文中,我们要构建一棵如下图中的不完全二叉树,他的遍历结果如下图中所示
创建二叉树
首先需要创建节点,编写创建节点函数如下
建立二叉树时,这里是以前序遍历的方式,输入的是扩展二叉树,也就是要告诉计算机什么是叶结点,否则将一直递归,当输入“#”时,指针指向NULL,说明是叶结点。
- 然后定义一个指针index,指向数组首元素,便于操作数组元素(访问,判断)
- 根据index指针的内容,创建一个节点
- 然后将指针的位置后移,即++(*index),然后递归调用构建左子树
- 继续将指针的位置后移,即++(*index),然后递归调用构建右子树
//创建节点
BinTreeNode * BinTreeNodeCreate(DataType value)
{
BinTreeNode* newnode=( *BinTreeNode)malloc(BinTreeNode);
assert( newnode);
newnode->data=value;
newnode->pleft=NULL;
newnode->right=NULL;
return newnode;
}
//创建二叉树
BinTreeNode* BinTreeCreate(DataType *arr,size_t size,size_t *index,DataType null_node)
{
//数组为空或者index指向空
if(arr==NULL||index==NULL)
{
return NULL;
}
//如果index不在有效范围内
if(*index>=size)
{
return NULL;
}
//如果该节点为空节点
if(arr[*index]==null_node)
{
return NULL;
}
//创建根节点
BinTreeNode* newnode=BinTreeNodeCreate(arr[*index]);
//创建左孩子
++(*index);
newnode->pleft=BinTreeCreate(arr,size,index,null_node);
//创建右孩子
++(*index);
newnode->pright=BinTreeCreate(arr,size,index,null_node);
return newnode;
}
先序遍历:首先访问根节点,然后遍历访问左子树,最后遍历访问右子树
//二叉树的初始化
void BinTreeInit(BinTreeNode** root)
{
//非法输入
if(root==NULL)
{
return ;
}
*root=NULL;
return ;
}
先序遍历:首先访问根节点,然后访问根节点的左子树,最后访问根节点的右子树
(这里的访问是指打印,当然还有其他访问方式,如比较,销毁等等)
在途中,蓝色线条圈住的属于同一颗子树,数字标号表示走的顺序
//先序遍历
void BinTreePreOrder(BinTreeNode* root)
{
if( root==NULL)
{
return ;
}
//先访问根节点
printf( "%c ",root->data);
//再递归访问左左子树
BinTreePreOrder(root->pleft);
//最后递归遍历右子树
BinTreePreOrder(root->pright);
return;
}
中序遍历:首先访问根节点的左子树,然后访问根节点,最后访问右子树
如果左右子树还有子数,则重复上述步骤
//中序遍历
void BinTreeInOrder(BinTreeNode* root)
{
if( root==NULL)
{
return ;
}
//先递归遍历左子树
BinTreeInOrder( root->pleft);
//在访问根节点
printf("%c "root->data);
//最后遍历访问右子树
BinTreeInOrder( root->pright);
return ;
}
后续遍历:先访问根节点的左子树,然后访问右子树,最后访问根节点
特点:后序遍历。最后一个元素必定是根节点
//后续遍历
void BinTreePostOrder(BinTreeNode* root)
{
if( root==NULL)
{
return ;
}
//先递归遍历左子树
BinTreepostOrder( root->pleft);
//然后遍历访问右子树
BinTreepostOrder( root->pright);
//最后访问根节点
printf("%c ",root->data);
return ;
}
层序遍历:从根节点点出发,从上到下,从左到右,依次访问。
二叉树的层序遍历的实现还是比较简单的,由于其层级的关系,很明显要用到队列来辅助实现,主要是从左向右,自上而下,依次将二叉树的各节点入队,这样便可以保证输出的顺序是层序排列的。下面是算法的实现思想:
先将树的根节点入队,
如果队列不空,则进入循环
{
将队首元素出队,并输出它;
如果该队首元素有左孩子,则将其左孩子入队;
如果该队首元素有右孩子,则将其右孩子入队
}
//层序遍历
void BinTreeLeveOrder(BinTreeNode* root)
{
SeqQueueNode queue;
if( root==NULL)
{
return ;
}
SeqQueueInit(&queue);
//将根节点入队列
SeqQueuePush(&queue,root);
//循环访问二叉树的左右子树
while( 1)
{
SeqQueueType front;
//取队首元素,如果返回值是0,则说明对空,结束遍历
int ret=SeqQueueFront(&queue,&front);
if(ret==0)
{
break;
}
//访问队首元素
printf("%c ",front->data);
//将队首出队列
SeqQueuePop(&queue);
//将左子树根节点插入队列
if(front->pleft)
{
SeqQueuePush(&queue,front->pleft);
}
//将右子树根节点插入队列
if(front->pright)
{
SeqQueuePush(&queue,front->pright);
}
}
}
- 基于后序遍历实现二叉树的销毁
由于二叉树结构根节点便可以找到其他任何节点,所以销毁时,保存根节点,然后递归遍历销毁左右子树,最后销毁根节点
二叉树的每个节点都是通过malloc申请所得,所谓销毁就是free掉malloc申请的节点,并置为NULL,防止野指针出现
//销毁二叉树
void BinTreeDestory(BinTreeNode* root)
{
//空树,直接返回
if(root==NULL)
{
return ;
}
//销毁左子树
if(root->pleft)
{
BinTreeDestory(root->pleft);
free(root->pleft);
root->pleft=NULL;
}
//销毁右子树
if(root->pright)
{
BinTreeDestory(root->pright);
free(root->pright);
root->pright=NULL;
}
//销毁根节点
if(root)
{
BinTreeDestory(root);
free(root);
root=NULL;
}
}
当然,也可以按照别的遍历顺序实现销毁,只要在销毁根节点之前先保存左右子树即可。