提起树,大家肯定想到了平时生活中见到的树,它由根、树枝和树叶组成。而我们这里说的树,其实就是把生活中的树的模型给颠倒过来了。如下图所示:
二叉树是通过上述5种形式的组合或嵌套而形成。
二叉树的概念:
一棵二叉树其实是结点的一个有限集合,该集合要么为空,要么由一个根结点再加上两棵分别称为左子树和右子树的二叉树组成。二叉树的特点:
每个结点最多有两棵子树,及二叉树不存在度大于2的结点
二叉树的子树有左右之分,其子树顺序不能颠倒
接下来,我们对二叉树做以下基本操作:
头文件声明:
#pragma once
#include<stddef.h>
typedef char TreeNodeType;
typedef struct TreeNode
{
TreeNodeType data;
struct TreeNode* lchild;
struct TreeNode* rchild;
}TreeNode;
void TreeInit(TreeNode** pRoot);//初始化
void TreePreOrder(TreeNode* root);//先序遍历
void TreeInOrder(TreeNode* root);//中序遍历
void TreePostOrder(TreeNode* root);//后序遍历
void TreeLevelOrder(TreeNode* root);// 层序遍历
TreeNode* CloneTree(TreeNode* root);//克隆树
void TreeDestroy(TreeNode* root);//销毁树
size_t TreeSize(TreeNode* root);//求树的节点个数
size_t TreeLeafSize(TreeNode* root);//求叶子节点个数
size_t TreeKLevelSize(TreeNode* root,size_t k);//求第k层节点个数
size_t TreeHeight(TreeNode* root);//求树的高度
TreeNode* TreeFind(TreeNode* root,TreeNodeType to_find);//在于一个树中找某个值的位置
TreeNode* ParentTree(TreeNode* root,TreeNode* child);//找一个节点的父节点
TreeNode* LChildNode(TreeNode* root,TreeNode* lchild);//找一个节点的左孩子
TreeNode* RChildNode(TreeNode* root,TreeNode* rchild);//找一个节点的右节点
假如现在有一个这样的二叉树,我们该如何对它进行遍历呢?
具体函数实现代码如下:
#include"bin_tree.h"
#include"seqqueue.h"
#include<stdio.h>
#include<stdlib.h>
void TreeInit(TreeNode** pRoot)
{
if(pRoot == NULL)
{
return;//非法输入
}
(*pRoot) = NULL;
}
TreeNode* CreateTreeNode(TreeNodeType value)//创建数的结点
{
TreeNode* new_node = (TreeNode*)malloc(sizeof(TreeNode));
new_node->data = value;
new_node->lchild = NULL;
new_node->rchild = NULL;
return new_node;
}
void DestroyTreeNode(TreeNode* node)//销毁结点
{
free(node);
}
//前序遍历树
void TreePreOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空树
}
//先访问根节点
printf("%c ",root->data);
//然后递归的遍历左子树
TreePreOrder(root->lchild);
//最后递归的遍历右子树
TreePreOrder(root->rchild);
return;
}
//中序遍历树
void TreeInOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空树
}
//先递归的访问左子树
TreeInOrder(root->lchild);
//然后访问根节点
printf("%c ",root->data);
//最后递归的访问右子树
TreeInOrder(root->rchild);
return;
}
//后序遍历树
void TreePostOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空树
}
//先递归访问左子树
TreePostOrder(root->lchild);
//再递归的访问右子树
TreePostOrder(root->rchild);
//最后访问根节点
printf("%c ",root->data);
return;
}
//层序遍历,借助队列来实现
//循环的取队首元素,访问并出栈,然后将其左右子树入队列
//循环上述过程,直到队列为空,说明访问完了
void TreeLevelOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空树
}
SeqQueue queue;
SeqQueueInit(&queue);
SeqQueuePush(&queue,root);
while(1)
{
SeqQueueType front;
size_t ret = SeqQueueFront(&queue,&front);
if(ret == 0)
{
break;//队列为空
}
printf("%c ",front->data);
SeqQueuePop(&queue);
if(front->lchild != NULL)
{
SeqQueuePush(&queue,front->lchild);
}
if(front->rchild != NULL)
{
SeqQueuePush(&queue,front->rchild);
}
}
printf("\n");
return;
}
TreeNode* _CreateTree(TreeNodeType data[],size_t size,size_t* index,TreeNodeType null_node)//创建树
{
if(index == NULL)
{
return NULL;//非法输入
}
if(*index >= size)
{
return NULL;//指针越界
}
if(data[*index] == null_node)
{
return NULL;//子树为空
}
//根据index指向的内容,创建一个节点
TreeNode* root = CreateTreeNode(data[*index]);
//先++index,然后递归构建新节点的左子树
++(*index);
root->lchild = _CreateTree(data,size,index,null_node);
//再++index,然后递归的构建新节点的右子树·
++(*index);
root->rchild = _CreateTree(data,size,index,null_node);
return root;
}
//借用先序遍历的规则和用#表示一个空子树
TreeNode* CreateTree(TreeNodeType data[],size_t size,TreeNodeType null_node)
{
//每次指向的元素
size_t index = 0;
//借助下面这个函数完成每次的递归调用
return _CreateTree(data,size,&index,null_node);
}
TreeNode* CloneTree(TreeNode* root)//克隆一棵树
{
if(root == NULL)
{
return;//空树
}
//按照先序方法来遍历
TreeNode* new_node = CreateTreeNode(root->data);
new_node->lchild = CloneTree(root->lchild);
new_node->rchild = CloneTree(root->rchild);
return new_node;
}
void TreeDestroy(TreeNode* root)//销毁树
{
if(root == NULL)
{
return;//空树
}
//按照后序的顺序销毁树
//如果按照前、中、层序的方法,先销毁根节点,就会出现找不到其左右孩子节点的情况,造成访问出错
TreeDestroy(root->lchild);
TreeDestroy(root->rchild);
DestroyTreeNode(root);
}
size_t TreeSize(TreeNode* root)//树的大小
{
if(root == NULL)
{
return 0;//空树
}
return 1+TreeSize(root->lchild)+TreeSize(root->rchild);
}
size_t TreeLeafSize(TreeNode* root)//叶子结点(左右子树为空)的大小
{
if(root == NULL)
{
return 0;//空树
}
if(root->lchild == NULL && root->rchild == NULL)
{
return 1;
}
return TreeLeafSize(root->lchild)+TreeLeafSize(root->rchild);
}
size_t TreeKLevelSize(TreeNode* root,size_t k)//第k层的大小
{
if(root == NULL || k < 1)
{
return 0;//空树
}
if(k == 1)
{
return 1;
}
return TreeKLevelSize(root->lchild,k-1)+TreeKLevelSize(root->rchild,k-1);
}
size_t TreeHeight(TreeNode* root)//树的高度
{
if(root == NULL)
{
return 0;//空树
}
if(root->lchild == NULL && root->rchild == NULL)
{
return 1;
}
//以左右子树中比较高的子树的高度作为树的高度
size_t lheight = TreeHeight(root->lchild);
size_t rheight = TreeHeight(root->rchild);
//先比较左右子树哪个高,最后用较高的加上根结点
return 1+(lheight > rheight ? lheight : rheight);
}
//在树中查找某个值
TreeNode* TreeFind(TreeNode* root,TreeNodeType to_find)
{
if(root == NULL)
{
return NULL;//空树
}
if(root->data == to_find)
{
return root;//找到了,返回此结点
}
TreeNode* lresult = TreeFind(root->lchild,to_find);
TreeNode* rresult = TreeFind(root->rchild,to_find);
return lresult != NULL ? lresult:rresult;
}
//找一个结点的父结点
TreeNode* ParentTree(TreeNode* root,TreeNode* child)
{
if(root == NULL || child == NULL)
{
return NULL;//root为空是空树,child为空,非法输入
}
if(root->lchild == child ||root->rchild == child)
{
return root;
}
TreeNode* lresult = ParentTree(root->lchild,child);
TreeNode* rresult = ParentTree(root->rchild,child);
return lresult != NULL ? lresult:rresult;
}
//找一个结点的左孩子节点
TreeNode* LChildNode(TreeNode* root,TreeNode* lchild)
{
if(root == NULL)
{
return NULL;//空树
}
if(root == lchild)
{
return root->lchild;
}
return LChildNode(root->lchild,lchild);
}
//找一个结点的右孩子结点
TreeNode* RChildNode(TreeNode* root,TreeNode* rchild)
{
if(root == NULL)
{
return NULL;//空树
}
if(root == rchild)
{
return root->rchild;
}
return RChildNode(root->rchild,rchild);
}
通过上述操作,我们发现树是递归实现的,因此对其操作时一般也采用递归的方法来实现。