树:
只有一个前驱,但是可以有多个后继
根节点:最顶层节点(没有前驱)
分支节点:有前驱也有后继
叶子节点:没有后继的节点
层:根节点所在为第一层,每过一个分支节点,层数+1
深度: 从根节点出发到达节点的分支节点个数称为该节点的深度
高度:从叶子节点出发到该节点最大的节点个数称为该节点的高度
树的高度:整个树形结构中高度最高的节点的高度称为树的高度
树的深度:整个树形结构中深度最深的节点的深度称为树的深度
树的层数 == 树的高度 == 树的深度
节点的度: 叶子节点度数为0
节点的后继的个数
二叉树:
所有节点中最大度数为2的树形结构
满二叉树:满二叉树是一种特殊的二叉树,其中每个层级的节点数都是最大值,即每个层级都是完全填充的
完全二叉树:所有节点展开后,节点编号排列连续
二叉树特点:叶子节点、只有左孩子、只有右孩子、左右孩子都有
满二叉树:二叉树第k层最多有2^(k-1)个节点
满二叉树有k层,则所有节点数为 2^k -1二叉树的三种遍历方法:
1.前序遍历:根左右A B D E H C F I G
2.中序遍历:左根右D B E H A F I C G
3.后续遍历:左右根D H E B I F G C A
二叉树结构体
typedef struct node
{
int No; //节点序号
char data;//也可以是char类型的
struct node *pLeftChild;//左孩子
struct node *pRightChild;//右孩子
}BinTree;
创建完全二叉树
BinTree *CreateBinTree(int Start,int End)
{
BinTree *pRoot=NULL;
pRoot=malloc(sizeof(BinTree));
if(pRoot==NULL)
{
return NULL;
}
pRoot->No=Start;
pRoot->pLeftChild=NULL;
pRoot->pRightChild=NULL;
if(2*Start<=End)
{
pRoot->pLeftChild = CreateBinTree(2*Start,End);
}
if(2*Start+1<=End)
{
pRoot->pRightChild = CreateBinTree(2*Start+1,End);
}
return pRoot;
}
前序遍历(递归)
int PreOrderBinTree(BinTree *pRoot)
{
//printf("%d ",pRoot->No);
printf("%c ",pRoot->data);
if(pRoot->pLeftChild!=NULL)
{
PreOrderBinTree(pRoot->pLeftChild);
}
if(pRoot->pRightChild!=NULL)
{
PreOrderBinTree(pRoot->pRightChild);
}
return 0;
}
中序遍历(递归)
int InOrderBinTree(BinTree *pRoot)
{
if(pRoot->pLeftChild!=NULL)
{
InOrderBinTree(pRoot->pLeftChild);
}
//printf("%d ",pRoot->No);
printf("%c ",pRoot->data);
if(pRoot->pRightChild!=NULL)
{
InOrderBinTree(pRoot->pRightChild);
}
return 0;
}
后序遍历(递归)
int PostOrderBinTree(BinTree *pRoot)
{
if(pRoot->pLeftChild!=NULL)
{
PostOrderBinTree(pRoot->pLeftChild);
}
if(pRoot->pRightChild!=NULL)
{
PostOrderBinTree(pRoot->pRightChild);
}
//printf("%d ",pRoot->No);
printf("%c ",pRoot->data);
return 0;
}
销毁二叉树(后序遍历思想)
int DestroyBinTree(BinTree **pRoot)
{
if((*pRoot)->pLeftChild!=NULL)
{
DestroyBinTree(&(*pRoot)->pLeftChild);
}
if((*pRoot)->pRightChild!=NULL)
{
DestroyBinTree(&(*pRoot)->pRightChild);
}
free((*pRoot));
*pRoot=NULL;
return 0;
}
创建不完全二叉树
//输入#代表此节点不存在
//节点存在则再次递归调用本函数,创建其左右子树
BinTree *CreateUncompleteTree(void)
{
BinTree *pRoot=NULL;
char Tmpdata=0;
scanf(" %c",&Tmpdata);
if(Tmpdata=='#')
{
return NULL;
}
else
{
pRoot=malloc(sizeof(BinTree));
if(pRoot==NULL)
{
return NULL;
}
pRoot->data=Tmpdata;
pRoot->pLeftChild=CreateUncompleteTree();
pRoot->pRightChild=CreateUncompleteTree();
}
return pRoot;
}
获得树的高度
//每个节点的高度等于 左子树或右子树中较高的子树高度 +1
int GetBinTreeHeight(BinTree *pRoot)
{
int LeftHeight=0;
int RightHeight=0;
if(pRoot==NULL)
{
return 0;
}
LeftHeight = GetBinTreeHeight(pRoot->pLeftChild);
RightHeight=GetBinTreeHeight(pRoot->pRightChild);
return (LeftHeight>RightHeight?LeftHeight:RightHeight)+1;
}
定义链表结构体(使用内核链表)
typedef struct Data
{
struct list_head node;
BinTree *pData;
}Data_t;
层次遍历
使用队列实现层次遍历
算法思想:
- 初始化:创建一个队列,并将根节点加入队列中。
- 循环处理:当队列不为空时,进行循环。
- 队列中第一个节点出队。
- 访问该节点(进行你需要的操作,如打印节点的值)。
- 如果该节点有左子节点,则将左子节点加入队列。
- 如果该节点有右子节点,则将右子节点加入队列。
- 结束:当队列为空时,表示所有节点都已经遍历完,算法结束。
int LayerOrderBinTree(BinTree *pRoot)
{
struct list_head head;
Data_t *pTmpNode=NULL;
Data_t *pFreeNode=NULL;
if(pRoot==NULL)
{
return -1;
}
//创建链式队列
INIT_LIST_HEAD(&head);
//为根节点申请一块链式节点的空间
pTmpNode=malloc(sizeof(Data_t));
if(pTmpNode==NULL)
{
return -1;
}
//链式节点的pData挂载根节点
pTmpNode->pData=pRoot;
//根节点入队
list_add_tail(&pTmpNode->node,&head);
while(!list_empty(&head))
{
//找到队头(要出栈的节点)
pFreeNode=list_entry(head.next,Data_t,node);
//打印队头
printf(" %c",pFreeNode->pData->data);
//判断要出栈的节点是否有左孩子,左孩子存在则入队
if(pFreeNode->pData->pLeftChild!=NULL)
{
pTmpNode=malloc(sizeof(Data_t));
if(pTmpNode==NULL)
{
return -1;
}
pTmpNode->pData=pFreeNode->pData->pLeftChild;
list_add_tail(&pTmpNode->node,&head);
}
//判断要出栈的节点是否有右孩子,右孩子存在则入队
if(pFreeNode->pData->pRightChild!=NULL)
{
pTmpNode=malloc(sizeof(Data_t));
if(pTmpNode==NULL)
{
return -1;
}
pTmpNode->pData=pFreeNode->pData->pRightChild;
list_add_tail(&pTmpNode->node,&head);
}
//队头出栈
list_del(&pFreeNode->node);
//释放为此节点申请的空间
free(pFreeNode);
}
return 0;
}
前序遍历(非递归)
算法思想
初始化:创建一个空栈用于存储节点,同时设定一个指针(或称为当前访问节点)指向根节点。
访问当前节点:当栈不为空或当前节点不为空时,进行循环。
- 如果当前节点不为空,则首先访问(处理)当前节点(例如,打印其值),然后将当前节点推入栈中,并转向其左子节点(即,将当前节点设为左子节点)。
- 如果当前节点为空,则从栈中弹出一个节点作为新的当前节点,并转向其右子节点(即,将当前节点设为右子节点)。
结束条件:当栈为空且当前节点也为空时,表示所有节点都已遍历完毕,算法结束。
int PreOrderBinTreeByStack(BinTree *pRoot)
{
struct list_head stackhead;
Data_t *pTmpNode=NULL;
Data_t *pFreeNode=NULL;
BinTree *pTreeNode=NULL;
if(pRoot==NULL)
{
return -1;
}
//初始化链式栈
INIT_LIST_HEAD(&stackhead);
pTreeNode=pRoot;
while(1)
{
//每个左孩子节点入栈
while(NULL!=pTreeNode)
{
pTmpNode=malloc(sizeof(Data_t));
if(pTmpNode==NULL)
{
return -1;
}
pTmpNode->pData=pTreeNode;
//入栈前打印此节点
printf(" %c",pTreeNode->data);
list_add(&pTmpNode->node,&stackhead);
pTreeNode=pTreeNode->pLeftChild;
}
if(list_empty(&stackhead))
{
break;
}
//此时的栈顶节点出栈
pFreeNode=list_entry(stackhead.next,Data_t,node);
list_del(&pFreeNode->node);
//将此节点的右子树的所有左孩子循环入栈
pTreeNode=pFreeNode->pData->pRightChild;
free(pFreeNode);
}
return 0;
}
中序遍历(非递归)
与前序遍历的区别为出栈时打印
int InOrderBinTreeByStack(BinTree *pRoot)
{
struct list_head stackhead;
Data_t *pTmpNode=NULL;
Data_t *pFreeNode=NULL;
BinTree *pTreeNode=NULL;
if(pRoot==NULL)
{
return -1;
}
//初始化链式栈
INIT_LIST_HEAD(&stackhead);
pTreeNode=pRoot;
while(1)
{
//每个左孩子节点入栈
while(NULL!=pTreeNode)
{
pTmpNode=malloc(sizeof(Data_t));
if(pTmpNode==NULL)
{
return -1;
}
pTmpNode->pData=pTreeNode;
list_add(&pTmpNode->node,&stackhead);
pTreeNode=pTreeNode->pLeftChild;
}
if(list_empty(&stackhead))
{
break;
}
//出栈此时的栈顶节点
pFreeNode=list_entry(stackhead.next,Data_t,node);
//出栈打印此节点
printf(" %c",pFreeNode->pData->data);
list_del(&pFreeNode->node);
//将此节点的右子树的所有左孩子循环入栈
pTreeNode=pFreeNode->pData->pRightChild;
free(pFreeNode);
}
return 0;
}
后序遍历(非递归)
算法思想
初始化:创建一个空栈用于存储节点,同时设定一个指针(或称为当前访问节点)指向根节点。
访问当前节点:当栈不为空或当前节点不为空时,进行循环。
- 如果当前节点不为空,则首先访问(处理)当前节点(例如,打印其值),然后将当前节点推入栈中,并转向其左子节点(即,将当前节点设为左子节点)。
- 如果当前节点为空,则从栈中弹出栈顶节点作为新的当前节点,
- 第一次出栈将标志位flag置为2(标志位初始化为1),重新入栈,并转向其右子节点(即,将当前节点设为右子节点)。
- 若是第二次出栈则打印此节点,并继续下一次循环
结束条件:当栈为空且当前节点也为空时,表示所有节点都已遍历完毕,算法结束。
A B D 依次入栈,当pTreeNode指向D的左孩子时为NULL,退出内层循环,弹出栈顶元素D(栈内元素A,B,D),D第一次出栈,将D的标志位flag置为2,D再次入栈(栈内元素A,B,D),pTreeNode指向D的右孩子也是NULL,再次循环时不进入内层while循环,D第二次出栈(栈内元素A,B),打印D,continue继续下一次外层循环,pTreeNode此时还为NULL,弹出栈顶元素B,B是第一次出栈,B的标志位置为2,B重新入栈(栈内元素A,B),pTreeNode指向B的右子树,此时pTreeNode指向E,进入内层循环,E,H入栈(栈内元素A,B,E,H),pTreeNode指向H的左孩子为NULL时突出内存循环,弹出栈顶节点H,H第一次出栈,flag=2,重新入栈(栈内元素A,B,E,H),pTreeNode指向H的左孩子为NULL,弹出栈顶元素H,H第二次出栈(栈内元素A,B,E),打印H,pTreeNode指向H的右孩子为NULL,再次弹出栈顶元素E,E第一次出栈,重新入栈,pTreeNode指向E的右孩子为NULL,再次弹出栈顶元素E(栈内元素A,B),打印E,继续外层循环,pTreeNode此时还是为NULL,弹出栈顶元素B,B第二次出栈(栈内元素A),打印B。。。后面依次遍历
int PostOrderBinTreeByStack(BinTree *pRoot)
{
struct list_head stackhead;
Data_t *pTmpNode=NULL;
Data_t *pFreeNode=NULL;
BinTree *pTreeNode=NULL;
if(pRoot==NULL)
{
return -1;
}
//初始化链式栈
INIT_LIST_HEAD(&stackhead);
pTreeNode=pRoot;
while(1)
{
//每个左孩子节点入栈
while(NULL!=pTreeNode)
{
pTmpNode=malloc(sizeof(Data_t));
if(pTmpNode==NULL)
{
return -1;
}
pTmpNode->pData=pTreeNode;
pTmpNode->flag=1;
list_add(&pTmpNode->node,&stackhead);
pTreeNode=pTreeNode->pLeftChild;
}
if(list_empty(&stackhead))
{
break;
}
pFreeNode=list_entry(stackhead.next,Data_t,node);
list_del(&pFreeNode->node);
if(pFreeNode->flag==2)
{
//出栈打印此节点
printf(" %c",pFreeNode->pData->data);
continue;
}
else if( pFreeNode->flag==1)//第一次出栈
{
pFreeNode->flag=2;
list_add(&pFreeNode->node,&stackhead);
}
pTreeNode=pFreeNode->pData->pRightChild;
}
}