目录
由于之前讲过二叉树的性质,概念等。这里就不赘述了
一. 二叉树的各种基本操作
用以下树来演示
1.求树的节点个数
利用递归,到节点D时,D的左右都是NULL,所以D返回给B的值为0+0+1,E节点亦是如此,此时B的返回值就是D的返回值加E的返回值+1
int TreeSize(Tree* root)
{
if (root == NULL)
return 0;
return TreeSize(root->left) + TreeSize(root->right) + 1;
}
2. 求叶子节点的个数
与求树的节点类似,但不同的地方是只有到叶子才会计数,也就是说只有当前节点的左右孩子都为NULL才会返回1
int TreeLeafSize(Tree* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
3.求树的高度
我们将此问题分解开来就是比较左子树和右子树的高,即比较左右的高,高的+1返回即可
但要注意
return TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left)+1 : TreeHeight(root->right)+1;
直接递归会造成大量的重复计算,效率非常低,我们可以将左右子树的高记录下来,来实现记忆化搜索,省下大量时间
int TreeHeight(Tree* root)
{
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
//效率太低了
/*return TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left)+1 : TreeHeight(root->right)+1;*/
}
4.求第k层的节点数量
我个人认为与求叶子节点个数类似,递归到第k层返回1即可,如果当前节点到达不了根节点了就返回0
int TreeLevelKSize(Tree* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
5. 求值x所在的节点
如果走到空就返回NULL,如果找到了就返回此节点,使用find记录往左找的值,如果没有找到(find为空)就往右找,找到了就一直返回就可以
Tree* TreeFind(Tree* root, int x)
{
if (root == NULL)
return NULL;
if (root->a == x)
return root;
Tree* find = TreeFind(root->left, x);
if (find == NULL)
find = TreeFind(root->right, x);
return find;
//if (find==NULL||find->a != x)
// return NULL;
//else
// return find;
}
6. 销毁二叉树
类似于后序遍历,先销毁左右子树最后再销毁根,因为通过根才能找到左右子树
void TreeDestory(Tree* root)
{
if (root == NULL)
return;
TreeDestory(root->left);
TreeDestory(root->right);
free(root);
}
二. 层历遍历与判断是否是完全二叉树
以下两种功能都需要队列辅助实现,使用到的函数如下
typedef int TreeType;
typedef struct Tree
{
TreeType a;
struct Tree* left;
struct Tree* right;
}Tree;
typedef Tree* QueueType;
typedef struct QueueNode
{
struct QueueNode* next;
QueueType val;
}QNode,*pQNode;
typedef struct Queue
{
pQNode head;
pQNode tail;
int size;
}Queue;
void QueueInit(Queue* pq)
{
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QueueType x)
{
pQNode tmp = (pQNode)malloc(sizeof(QNode));
if (tmp == NULL)
{
perror("malloc:fail\n");
return;
}
tmp->next = NULL;
tmp->val = x;
if (pq->tail == NULL)
{
pq->head = tmp;
pq->tail = tmp;
}
else
{
pq->tail->next = tmp;
pq->tail = tmp;
}
pq->size++;
}
QueueType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->val;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head);//要是没有节点就报错
pQNode tmp = pq->head;
if (pq->head == pq->tail)//如果只有一个节点
pq->tail = NULL;
pq->head = pq->head->next;
free(tmp);
tmp = NULL;
pq->size--;
}
bool QueueEmpty(Queue* pq)
{
return pq->head == NULL && pq->tail == NULL;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
while (pq->head)
{
pQNode tmp = pq->head;
pq->head = pq->head->next;
free(tmp);
tmp = NULL;
}
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
1. 层序遍历
在外面先将根入队,进入循环后用newq记录队头的值将队头的左右孩子入队再将根出队,左右孩子为空时不入队。即可完成我们的想法
void TreeLevelOrder(Tree* root)
{
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
Tree* newq = QueueFront(&q);
QueuePop(&q);
printf("%d ",newq ->a);
if (newq->left != NULL)
{
QueuePush(&q, newq->left);
}
if (newq->right != NULL)
{
QueuePush(&q, newq->right);
}
}
QueueDestroy(&q);
}
2. 判断二叉树是否是完全二叉树
1.层序遍历走,但是NULL也进队列。
2.队列走到一个空节点时,开始判断,如果后面全是空就是完全二叉树后面有节点就不是完全二叉树。
遇到空时后面还有非空没进队列。假设空在树的第三层的第三个,没进去的一定是第五层并不会影响对二叉树的判断,因为第四层已经进队列的,NULL后面有节点说明是非完全二叉树。如下图所示
假设队列此时的队头为红圆圈出的NULL,G虽然没进队列但队列后有F节点证明其不是完全二叉树
后面的非空一定有前面非空的孩子 ,
当层序遍历出到空时,前面的非空都出队列了,那他们的孩子一定进队列了,如果他们的孩子不是空(即遍历到空,队列后还有非空时)那这个二叉树就不是完全二叉树
即上图中的节点F是节点E的孩子
不要忘记销毁队列
bool TreeCompelete(Tree* root)
{
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
Tree* newq = QueueFront(&q);
QueuePop(&q);
if (newq == NULL)
{
while (!QueueEmpty(&q))
{
if (QueueFront(&q) != NULL)//如果有非空就不是完全二叉树
{
QueueDestroy(&q);
return false;
}
QueuePop(&q);
}
break;
}
QueuePush(&q, newq->left);
QueuePush(&q, newq->right);
}
QueueDestroy(&q);
return true;
}
三. OJ题,根据输入的先序字符串还原树并中序输出
题目描述
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
根据先序遍历还原二叉树,我们给函数传两个参数,字符数组和下标(下标要用指针,因为要改变值)
例如输入:abc##de#g##f###
构造树的函数运行过程大致如下
malloc节点后将其赋值为a,并将数组下标++,进入递归此时ch[si]指向b,开辟空间赋值,再往左递归开辟空间赋值为c,c给左子树遇见'#' (数组下标++)c的左子树接受返回值NULL,c给右子树赋值遇见'#'(数组下标++),c的右子树接受返回值为NULL,b的左子树接受返回值为c节点,b给右子树赋值开辟空间赋值为d,d给左子树赋值开辟空间赋值为e,e左子树赋值遇见'#',e左子树赋值为NULL,e右子树开辟空间赋值为g,g左右子树赋值都遇见'#',左右子树都接受返回值NLL,e右子树接受返回值为g的节点,d左子树接收返回值为e的节点,以此类推............
Tree* CreateOrder(char* ch,int* si)
{
if(ch[*si]=='#')
{
(*si)++;
return NULL;
}
Tree* root=(Tree*)malloc(sizeof(Tree));
root->a=ch[(*si)++];
root->left=CreateOrder(ch, si);
root->right=CreateOrder(ch,si);
return root;
}
完整AC代码如下
#include <stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
typedef char TreeType;
typedef struct Tree
{
TreeType a;
struct Tree* left;
struct Tree* right;
}Tree;
Tree* CreateOrder(char* ch,int* si)
{
if(ch[*si]=='#')
{
(*si)++;
return NULL;
}
Tree* root=(Tree*)malloc(sizeof(Tree));
root->a=ch[(*si)++];
root->left=CreateOrder(ch, si);
root->right=CreateOrder(ch,si);
return root;
}
void InOrder(Tree* root)//中序遍历
{
if (root == NULL)
{
return;
}
InOrder(root->left);//左子树
printf("%c ", root->a);//根
InOrder(root->right);//柚子树
}
int main() {
char* ch[100];
scanf("%s",ch);
int i=0;
Tree* root=CreateOrder(ch,&i);
InOrder(root);
return 0;
}
这篇文章就到这里啦!希望能对你有所帮助
(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤