树型结构和线性结构的区别
线性链表,一个节点最多只有一个前驱和一个后驱(循环链表)。树型结构的话则每一个节点可以有一个前驱节点和多个后驱节点,我们一般用到的是二叉树,也就是说有两个后继节点。
基本概念
节点的度:一个节点的子数的数量称为该节点的度
树的度:该树中节点的最大度数
叶子节点:树中度为0的节点称为叶子节点,度不为0的节点称为分支节点
节点的层数:树是一种层次结构
树的深度:一棵树中节点的最大层数称为树的深度
有序树和无序树:树中各节点的子树按一定的顺序从左向右安排称为有序树
森林:m颗互不相交的树的集合
二叉树的性质
1. 二叉树中,第i层节点总数最多为2i-1个
2. 深度为k的二叉树最多2k-1个节点,最少有k个节点,左树
3. 一颗二叉树,叶节点n0,度为2的节点总数n2,则n0=n2+1;
4. 具有n个节点的完全二叉树的深度为k, k>=log2n+1
5. 对于一个具有n个节点的完全二叉树各个节点如果采用顺序方式存储,对于任意节点i,有如下关系:
a) 如果i!=1,则其父节点的编号为i/2;
b) 如果2i<=n,则其左子树的根节点编号为2i,如果2*i>n,那么无左子树
因为对于完全二叉树,左子树的根节点编号总是2的倍数
c) 如果2*i+1<=n,那么右子树根节点编号为2*i+1;否则无右子树
完全二叉树和满二叉树
满二叉树:除了最后一层叶节点,其他节点都有2个子节点
完全二叉树: 最后一层从左向右的叶节点连续存在,只缺少右侧若干节点
二叉树的遍历
先序遍历DLR:先访问根节点,再先序遍历左子树,最后先序遍历右子树
中序遍历LDR:先中序遍历左子树,再访问根节点,最后中序访问右子树
后序遍历LRD:先后序遍历左子树,再后序遍历右子树,最后访问根节点
操作二叉树的代码
#include <stdio.h>
#include <stdlib.h>
#define QUEUE_MAXSIZE 50
typedef char DATA; //定义元素数据类型
typedef struct ChainTree{
DATA data;
struct ChainTree *left;
struct ChainTree *right;
}ChainBinTree;
//初始化二叉树
//将已有的一个节点作为二叉树的根节点
ChainBinTree *BinTreeInit(ChainBinTree *node){
if(node!=NULL){
return node;
}else{
return NULL;
}
}
//添加节点到二叉树
//添加二叉树的左子节点还是右子节点需要选择
//bt是父节点,node是需要添加的节点,n判断左右
int BinTreeAdd(ChainBinTree *bt,ChainBinTree *node,int n){
if(bt==NULL){
printf("父结点不存在,请先设置父结点!\n");
return 0;
}
switch(n){
case 1://添加到左节点
//.........需要左子节点为空才能添加 ,坑
if(bt->left){
printf("左子树结点不为空!\n");
return 0;
}else{
bt->left=node;
}
case 2:
if(bt->right) //右子树不为空
{
printf("右子树结点不为空!\n");
return 0;
}else
bt->right=node;
break;
default:
printf("参数错误!\n");
return 0;
}
return 1;
}
//获取二叉树左右子树
ChainBinTree *BinTreeLeft(ChainBinTree *bt){
if(bt){
return bt->left;
}else{
return NULL;
}
}
ChainBinTree *BinTreeRight(ChainBinTree *bt) //返回右子结点
{
if(bt)
return bt->right;
else
return NULL;
}
//二叉树状态:是否为空
int BinTreeIsEmpty(ChainBinTree *bt){
if(bt){
return 0;
}else{
return 1;
}
}
//计算二叉树深度
/*
一个错误的思路来求解深度,企图利用while循环来判断bt->left==NULL这个条件
实际上不行,因为二叉树有些节点有子节点,有些没有,这些都不是规律的
所以用到递归判断
*/
int BinTreeDepth(ChainBinTree *bt)
{
int dep1,dep2;
if(bt==NULL)
return 0; //对于空树,深度为0
else{
dep1=BinTreeDepth(bt->left);
dep2=BinTreeDepth(bt->right);
if(dep1>dep2){
return dep1+1;
}else{
return dep2+1;
}
}
}
//在二叉树中查找相关节点
//思路:递归 ***********************************************************************
//
ChainBinTree *BinTreeFind(ChainBinTree *bt,DATA data){
ChainBinTree *p;
if(bt==NULL){
return NULL;
}else{
if(bt->data==data){
return bt;
}else{
if(p=BinTreeFind(bt->left,data)){
return p;
}else if(p=BinTreeFind(bt->right,data)){
return p;
}else{
return NULL;
}
}
}
}
//**************************************************************************************
void BinTreeClear(ChainBinTree *bt) // 清空二叉树,使之变为一棵空树
{
if(bt)
{
BinTreeClear(bt->left); //清空左子树
BinTreeClear(bt->right);//清空右子树
free(bt);//释放当前结点所占内存
bt=NULL;
}
return;
}
//参数是一个二叉节点类型指针和一个函数指针,这个函数一般在main里写
//这个函数一般用来对遍历得到的节点进行操作,比如打印数据
void BinTree_DLR(ChainBinTree *bt,void (*oper)(ChainBinTree *p)){
if(bt){
oper(bt);
BinTree_DLR(bt->left,oper);
BinTree_DLR(bt->right,oper);
}
return;
}
//中序
void BinTree_LDR(ChainBinTree *bt,void (*oper)(ChainBinTree *p)){
if(bt){
BinTree_LDR(bt->left,oper);
oper(bt);
BinTree_LDR(bt->right,oper);
}
return;
}
void BinTree_LRD(ChainBinTree *bt,void (*oper)(ChainBinTree *p)) //后序遍历
{
if(bt)
{
BinTree_LRD(bt->left,oper); //后序遍历左子树
BinTree_LRD(bt->right,oper); //后序遍历右子树/
oper(bt); //处理结点数据
}
return;
}
//二叉树按层遍历不适合用递归
//从根节点出发,
void BinTree_Level(ChainBinTree *bt,void (*oper)(ChainBinTree *p)){
ChainBinTree *p;
ChainBinTree *q[QUEUE_MAXSIZE];
int head,tail=0;
if(bt){
tail=(tail+1)%QUEUE_MAXSIZE;
q[tail]=bt;
}
while(head!=tail){
head=(head+1)%QUEUE_MAXSIZE;
p=q[head];
oper(p);
if(bt->left!=NULL){
tail=(tail+1)%QUEUE_MAXSIZE;
q[tail]=p->left;
}
if(bt->right!=NULL){
tail=(tail+1)%QUEUE_MAXSIZE;
q[tail]=p->right;
}
}
return;
}