1.遍历操作
- 前序遍历:按 根 左子树 右子树的顺序处理
此为实现路径图,运用递归思想实现,先从下往上满足前序结构,再逐步满足根结点的前序结构
2.中序遍历:按 左子树 根 右子树的顺序处理
3.后序遍历:按 左子树 右子树 根的顺序处理
4.层序遍历:从二叉树的根结点出发,从左到右依次访问每一层的结点。
2.链式二叉树的实现
定义
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
创建结构体类型的左右指针来链接,data来存储数据
创建结点
BTNode* BuyNode(BTDataType x)
{
BTNode* node =(BTNode*) malloc(sizeof(BTNode));
if (node == NULL)
{
perror("malloc fail");
return NULL;
}
node->data = x;
node->left = node->right = NULL;
return node;
}
创建树
BTNode* CreatTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
BTNode* node7 = BuyNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
node3->right = node7;
return node1;
}
创建指针并初始化它们,node1作为根结点,将其左子结点设为node2,右子结点设为node3,往下以此类推,最终形成一棵二叉树。此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
前序遍历
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PreOrder(root->left);
printf("%d ", root->data);
PreOrder(root->right);
}
后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PreOrder(root->left);
PreOrder(root->right);
printf("%d ", root->data);
}
二叉树结点个数
int TreeSize(BTNode* root)
{
return root == NULL ? 0 :
TreeSize(root->left)
+ TreeSize(root->right) + 1;
}
结点个数为左子树+右子树+1,递归调用Treesize函数,分别计算左子树和右子树的结点数,+1将当前结点本身计入结点总数。
二叉树的高度
int TreeHeight(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int Lheight = TreeSize(root->left) + 1;
int Rheight = TreeSize(root->right) + 1;
return Lheight > Rheight ? Lheight + 1 : Rheight + 1;
}
当前树的高度等于左右子树高度大的+1。创建Lheight和Rheight为了提前记录高度,否则在条件表达式中比较完左右子树大小后又要重新计算一遍左子树或右子树大小,大大降低了效率。
当前树的第k层个数
int TreeKLevel(BTNode* root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
int lnum = TreeKLevel(root->left, k - 1);
int rnum = TreeKLevel(root->right, k - 1);
return lnum + rnum;
}
此处根结点代表第一层,也就是最低层。当前树的第k层个数等于左子树的k-1层个数+右子树的k-1层个数,递归调用时每次调用都将层数-1,表示子树往更深一层除查找,当递归k==1时就到了目标层。若目标层不为空则找到一个结点返回1,否则返回0。
查找结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode*lret=BinaryTreeFind(root->left,x);
if (lret)
{
return lret;
}
BTNode*rret=BinaryTreeFind(root->right,x);
if (rret)
{
return rret;
}
return NULL;
}
因为返回值是BTNode型指针,故不能使用逻辑运算符结果返回,通过逐层递归来查找结点,return是返回到上一层递归中而不是结束整个程序,所以设置了lret和rret两个指针来分别记录左子树和右子树的查找结果,若返回的不是空指针,证明找到目标结点并逐层返回其指针,空指针同理证明找不到没有。逻辑图如下
层序遍历
void Leveloeder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
QueueDestroy(&q);
}
思路:运用队列先进先出性质实现,出上一层结点,带入下一层不为空的结点可完美实现层序遍历。
需注意要将队列中存放data数据的类型改为树的结点指针,这样data就是指向树结点的指针。可以复制一份之前队列实现的代码
代码实现:首先创建一个队列并初始化,树的根结点不为空就压入队列,while循环的成立条件是队列不为空,用front指针提前保存队列头元素,再删除掉他,打印front中data指向的树结点,接下来若左右子树不为空就入队列,最后销毁。
判断是否为完全二叉树
bool Treecomplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q,root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front== NULL)
{
break;
}
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
//判断是不是完全二叉树
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
思路:采用层序遍历的方法来判断,完全二叉树按层序走,非空结点一定连续,所以可以通过找到第一个空结点后,判断后续结点是否全为空,是则符合完全二叉树性质,否则不是。
和目录上一章不同,注意这里空结点也要入队,if语句中return前记得销毁。
二叉树的销毁
void TreeDestroy(BTNode* root)
{
if (root == NULL)
{
return;
}
TreeDestroy(root->left);
TreeDestroy(root->right);
free(root);
}
这里采用后序遍历的方法来销毁二叉树,确保所有子结点先于父结点被释放,若先销毁父结点则要提前保存子结点,效率低下。调用完后记得手动置空,或者传二级指针进来内部置空。