二叉树,顾名思义一个节点最多只有两个节点,且有左右之分,是树形结构中重要的一种结构
数据定义:
typedef char BTDataType;//节点的数据类型
typedef struct BinaryTreeNode
{
BTDataType _data;//数据
struct BinaryTreeNode* _left;//左节点
struct BinaryTreeNode* _right;//右节点
}BTNode;
二叉树的遍历总共有四种,以下列二叉树为例,分别实现前/中/后/层序遍历:
A
/ \
B C
/ \ / \
D E F G
\
H
前序遍历:根,左节点,右节点
void BinaryTreePrevOrder(BTNode* root) { if(root==NULL) { printf("NULL "); return; } printf("%c ",root->_data); //打印根 BinaryTreePrevOrder(root->_left); //递归到最底层后,打印最底层的左右节点 BinaryTreePrevOrder(root->_right); }
打印值:A B D NULL NULL E NULL H NULL NULL C F NULL NULL G NULL NULL
中序遍历:左节点,根,右节点
void BinaryTreeInOrder(BTNode* root) { if(root==NULL) { printf("NULL "); return; } BinaryTreeInOrder(root->_left); printf("%c ",root->_data); //和前序遍历差不多 只不过是把根放到了中间 BinaryTreeInOrder(root->_right); }
打印值:NULL D NULL B NULL E NULL H NULL A NULL F NULL C NULL G NULL
后序遍历:左节点,右节点,根
void BinaryTreePostOrder(BTNode* root) { if(root==NULL) { printf("NULL "); return; } BinaryTreePostOrder(root->_left); BinaryTreePostOrder(root->_right); printf("%c ",root->_data); //后序遍历也是一样 只需移动根节点的输出 }
打印值:NULL NULL D NULL NULL NULL H E B NULL NULL F NULL NULL G C A
前/中/后序遍历的原理是一样的,都是利用递归到最后一个有效节点的左右孩子的位置,判断是否为NULL,如果为NULL则开始打印那个节点的值,不同的是打印的顺序。
但是层序遍历不一样,它无法使用递归实现,它是从左往右依次遍历所有节点,它的实现需要用到队列来帮助。
思路:先push根节点,然后判断根节点的左右孩子是否为空,如果为空则不入列,如果不为空就入列,然后队列入一个值打印一个值,再立刻出列pop掉,为什么要立刻出列呢,是因为可以用判断队列是否为空来控制循环,当队列为空时,则证明已经遍历了树的所有节点。
具体实现如下:
定义队列以及实现队列:
typedef BTNode* QDataType;//定义队列中数据的类型 //二叉树队列 typedef struct QListNode { struct QListNode* _next; QDataType _data; }QNode; //队列的实现 Queue.c // 队列的结构 typedef struct Queue { QNode* front; //队头 QNode* rear; //队尾 int size;//计数 }Queue; // 初始化队列 void QueueInit(Queue* q) { q->front=NULL; q->rear=NULL; q->size=0; } // 队尾入队列 void QueuePush(Queue* q, QDataType data) { assert(q); QNode* newNode=(QNode*)malloc(sizeof(QNode)); if(newNode==NULL) { perror("malloc fail:"); exit(-1); } newNode->_data=data; newNode->_next=NULL; if(q->rear==NULL)//front和rear未指向时 赋值newNode给它们当第一个值 { q->front=q->rear=newNode; } else { q->rear->_next=newNode;//尾插 q->rear=q->rear->_next; } q->size++;//计数 } // 队头出队列 void QueuePop(Queue* q) { assert(q); if(q->front->_next==NULL)//只有一个元素时 出列(释放) 置NULL { free(q->front); q->front=q->rear=NULL; } else//多元素时出列后 front往后移 { QNode* del =q->front; q->front=del->_next; free(del); } q->size--; } // 获取队列头部元素 QDataType QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->front->_data; } // 检测队列是否为空,如果为空返回非零结果,如果非空返回0 int QueueEmpty(Queue* q) { assert(q); return q->front==NULL &&q->rear==NULL; } // 销毁队列 void QueueDestroy(Queue* q) { assert(q); while(q->size) { QueuePop(q);//销毁就是一直出列 } }
层序遍历:一层一层依次从左到右遍历所有节点
void BinaryTreeLevelOrder(BTNode* root) { Queue q; QueueInit(&q); if(root!=NULL) QueuePush(&q,root);//树不为空 插入根节点 while(!QueueEmpty(&q))//为空 { QDataType front = QueueFront(&q);//BTNode*=QDataType printf("%c ",front->_data); QueuePop(&q);//出列 if(front->_left!=NULL) { QueuePush(&q,front->_left);//左子节点不为空则入列 } if(front->_right!=NULL) { QueuePush(&q,front->_right);//右子节点不为空则入列 } } printf("\n"); QueueDestroy(&q); }
打印值:A B C D E F G H
实现过遍历后,也能通过遍历来实现二叉树的创建,这里拿前序遍历来构建二叉树
前序遍历数组"ABD##E#H##CF##G##"构建二叉树
以及二叉树的销毁
BTNode* BinaryTreeCreate(BTDataType* a,int* p) { BTNode* Tree = (BTNode*)malloc(sizeof(BTNode)); if(a[*p]=='#') { (*p)++; return NULL; } Tree->_data=a[(*p)++]; //和遍历原理一样 Tree->_left=BinaryTreeCreate(a,p); Tree->_right=BinaryTreeCreate(a,p); return Tree; } // 二叉树销毁 void BinaryTreeDestory(BTNode** root) { if(*root==NULL) return; BinaryTreeDestory(&(*root)->_left); BinaryTreeDestory(&(*root)->_right); free(*root); //递归到叶子节点再销毁,否则先销毁前面的节点会导致后面节点无法被找到 }
还可以利用递归,来判断二叉树的总节点数、叶子节点数以及每层的节点数
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
//静态变量无法释放 第二次调用函数时会接着往上加 用的时候会出现叠加问题,所以不用
// static int size=0;
// if(root==NULL) return 0;
// ++size;
// BinaryTreeSize(root->_left);
// BinaryTreeSize(root->_right);
// return size;
if(root==NULL) return 0;
return BinaryTreeSize(root->_left)+
BinaryTreeSize(root->_right)+1;
//简写
//return root==NULL? 0:BinaryTreeSize(root->_left)+BinaryTreeSize(root->_right)+1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{ //为NULL时返回0 结束递归
if(root==NULL) return 0;
//节点左右都为NULL时是叶子节点
if(root->_left==NULL&&root->_right==NULL) return 1;
return BinaryTreeLeafSize(root->_left) //左右子树的叶子节点相加即可
+BinaryTreeLeafSize(root->_right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if(root==NULL)
{
return 0;
}
if(k==1) //当k=1时 是要计算的层数 如果不为NULL则+1
{
return 1;
}
return BinaryTreeLevelKSize(root->_left,k-1)+
BinaryTreeLevelKSize(root->_right,k-1);
/* A 相对于A层 DEFG层为第三层 k=3
/ \
B C 相对于BC层 DEFG层为第二层 k-1层 也就是k=2
/ \ / \
D E F G 相当于DEFG层 DEFG层为第一层 k-1-1层 也就是k=1
\
H
计算第三层有几个节点时 k-1层B的子节点数+C的子节点数
当BC的子节点不为NULL时 就能返回k==1
k==1 B的左节点不为NULL 返回1 加上 右节点不为NULL 返回1
那么子树B(k-1层)有2个节点 同理子树C(k-1层)有2个
递归回去后 k!=1 root!=NULL 则返回 2 + 2 = 3
*/
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if(root==NULL) return NULL;
if(root->_data==x) return root;//递归结束条件
if(BinaryTreeFind(root->_left,x)) //先遍历左子树
return BinaryTreeFind(root->_left,x);
else //左子树找不到再遍历右子树
return BinaryTreeFind(root->_right,x);
//简写
// BTNode* left=BinaryTreeFind(root->_left,x);
// BTNode* right=BinaryTreeFind(root->_right,x);
// return left ? left : right;
}