二叉树操作详解
二叉树块
我们先预想建立一个二叉树(如图),首先对其进行前中后三种遍历
前序遍历
按根左右的顺序一层一层遍历
代码如下
void BinaryTreePrevOrder(BTNode* root)
{
if (root==NULL)
{
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
前序遍历是每到一个节点,先输出该节点数据,再往左递归,待左边指针为空,再往回递归
中序遍历
和前序遍历操作差不多,逻辑上按左根右遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
中序遍历是每到一个节点,优先往左递归,待递归到空指针,再回来输出节点数据
后序遍历
逻辑上按左右根遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
构建二叉树
从数组中读取数据构建二叉树,选定终止条件,即读取的数据下标大于等于最大下标n,这边选用pi而不是pi,是因为不传pi的话,往下递归会重新产生名为pi的形参,而原来的pi并没有因为形参的改变而改变
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
//a为数组,n为数组最大下标,*pi表示当前读取位置下标
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if ((*pi) >= n)
{
return NULL;
}
if (a[(*pi)] == '#')
{
(*pi)++;
return NULL;
}
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("malloc fail");
return;
}
node->_data = a[(*pi)++];
node->_left = BinaryTreeCreate(a, n, pi);
node->_right = BinaryTreeCreate(a, n, pi);
return node;
}
思路:每读取一个字符就申请一个结构体储存,并且按照前序遍历顺序储存
二叉树销毁
这里因为root是指针,要把它置空,就要用二级指针
void BinaryTreeDestory(BTNode** root)
{
if (*root == NULL)
{
return;
}
BinaryTreeDestory(&((*root)->_left));
BinaryTreeDestory(&((*root)->_right));
free(*root);
*root = NULL;
}
二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root== NULL)
return 0;
return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
返回的是左边节点加右边节点加该节点自身,故而+1。
推荐大家多画递归展开图理解
二叉树叶子节点个数
//是叶子节点就返回1,不是就接着往左往右递归
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->_left == NULL && root->_right == NULL)//叶子节点判断条件
return 1;
return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
二叉树第k层节点个数
首先要判断有没有k层,且k是不是大于0,然后往下递归。每递归一层,k-1,一直到k==1,如果还不为空,就表示有一个这样的节点,把这样的节点数量加起来,就得到个数了。这里不能传int* k,操作要求是每下一层减1,不是每递归一次减1。
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(root->_left, k-1)+BinaryTreeLevelKSize(root->_right, k-1);
}
二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->_data == x)
return root;
BTNode* ret1=BinaryTreeFind(root->_left, x);
if (ret1)
return ret1;
BTNode* ret2 = BinaryTreeFind(root->_right, x);
if (ret2)
return ret2;
return NULL;//找不到返回空指针
}
这边注意找到了是一层一层返回,难理解建议画展开图
层序遍历
这里以及判断二叉树是否是完全二叉树需要用到队列的知识,队列代码放下面了。
思路为先把根节点入队列,然后取队头用front接收,输出数据并出队列,再把左右节点(非空)入队,然后出左节点,再把左节点的左右节点入队,接着又是右节点,依此循环,直到队列为空。
void BinaryTreeLevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
printf("%c ", front->_data);
QueuePop(&q);
if (front->_left)
QueuePush(&q, front->_left);
if (front->_right)
QueuePush(&q, front->_right);
}
printf("\n");
QueueDestroy(&q);
}
判断二叉树是否是完全二叉树
这里和层序遍历差不多,区别是空指针也入队列,以及在遍历到空指针出队列就结束循环。如果是完全二叉树,自第一个空指针出队列,后面就全是空指针了。建议自己画队列理解
int BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueuePush(&q, front->_left);
QueuePush(&q, front->_right);
}
else
break;
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);
return 0;
}
}
QueueDestroy(&q);
return 1;
}
队列块
我设置的队列是带头节点的单链表,队头指针指向头节点。然后我队列的操作是放在二叉树头文件里的,我觉得方便一点,不用考虑头文件包含问题,也不会出现复制路径操作不当出现头文件打不开的问题
不多赘述了,代码如下
初始化队列
void QueueInit(Queue* q)
{
assert(q);
QNode* p = (QNode*)malloc(sizeof(QNode));
if (p == NULL)
{
perror("malloc fail");
return;
}
p->_next = NULL;
q->_front = p;
q->_rear = p;
}
队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);
QNode* p = (QNode*)malloc(sizeof(QNode));
if (p == NULL)
{
perror("malloc fail");
return;
}
p->_next = NULL;
p->_data = data;
q->_rear->_next = p;
q->_rear = p;
}
队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
if (q->_front->_next == q->_rear)
{
free(q->_rear);
q->_rear = q->_front;
}
else
{
QNode* cur = q->_front->_next;
q->_front->_next = cur->_next;
free(cur);
cur = NULL;
}
}
获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->_front != q->_rear);
return q->_front->_next->_data;
}
检测队列是否为空
如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
assert(q);
return q->_front == q->_rear;
}
销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
while(!QueueEmpty(q))
QueuePop(q);
free(q->_front);
q->_front = NULL;
}