Bootstrap

【初阶数据结构】8.二叉树(3)


4.实现链式结构二叉树

用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 ,其结构如下:

typedef int BTDataType;
// 二叉链
typedef struct BinaryTreeNode
{
    struct BinTreeNode* left; // 指向当前结点左孩子 
    struct BinTreeNode* right; // 指向当前结点右孩子 
    BTDataType val; // 当前结点值域 
}BTNode;

二叉树的创建方式比较复杂,为了更好的步入到二叉树内容中,我们先手动创建一棵链式二叉树:

BTNode* BuyBTNode(int val)
{
    BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
    if (newnode == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    newnode->val = val;
    newnode->left = NULL;
    newnode->right = NULL;
    return newnode;
}
BTNode* CreateTree()
{
    BTNode* n1 = BuyBTNode(1);
    BTNode* n2 = BuyBTNode(2);
    BTNode* n3 = BuyBTNode(3);
    BTNode* n4 = BuyBTNode(4);
    BTNode* n5 = BuyBTNode(5);
    BTNode* n6 = BuyBTNode(6);
    BTNode* n7 = BuyBTNode(7);
    n1->left = n2;
    n1->right = n4;
    n2->left = n3;
    n4->left = n5;
    n4->right = n6;
    n5->left = n7;
    return n1;
}

回顾二叉树的概念,二叉树分为空树和非空二叉树,非空二叉树由根结点、根结点的左子树、根结点的右子树组成的

img

根结点的左子树和右子树分别又是由子树结点、子树结点的左子树、子树结点的右子树组成的,因此二叉树定义是递归式的,后序链式二叉树的操作中基本都是按照该概念实现的。


4.1 前中后序遍历

二叉树的操作离不开树的遍历,我们先来看看二叉树的遍历有哪些方式

img


4.1.1 遍历规则

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  1. 前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发生在遍历其左右子树之前

    访问顺序为:根结点、左子树、右子树

  2. 中序遍历(Inorder Traversal):访问根结点的操作发生在遍历其左右子树之中(间)

    访问顺序为:左子树、根结点、右子树

  3. 后序遍历(Postorder Traversal):访问根结点的操作发生在遍历其左右子树之后

    访问顺序为:左子树、右子树、根结点


4.1.2 代码实现

void PreOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("N ");
        return;
    }
    printf("%d ", root->val);
    PreOrder(root->left);
    PreOrder(root->right);
}
void InOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("N ");
        return;
    }
    InOrder(root->left);
    printf("%d ", root->val);
    InOrder(root->right);
}
void PostOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("N ");
        return;
    }
    InOrder(root->left);
    InOrder(root->right);
    printf("%d ", root->val);
}

以前序遍历为例:

img


4.2 结点个数以及高度等

Tree.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

//定义二叉树的链式结构
//二叉树结点的结构
//Binary 二进制的意思
typedef int BTDataType;

typedef struct BinaryTreeNode {
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//前序遍历
void PreOrder(BTNode* root);

//中序遍历--左根右
void InOrder(BTNode* root);

//后序遍历 ---左右根
void PostOrder(BTNode* root);

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root);

// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root);

// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

Tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Tree.h"

//递归
//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//走到为空就不遍历了
	{
		return;
	}
	printf("%d ", root->data);//打印根节点
	PreOrder(root->left);//左
	PreOrder(root->right);//右
}

//中序遍历--左根右
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left);//左
	printf("%d ", root->data);//中
	InOrder(root->right);//右
}

//后序遍历 ---左右根
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		//printf("NULL ");
		return;
	}
	PostOrder(root->left);//左
	PostOrder(root->right);//右
	printf("%d ", root->data);//中
}

/*
// ⼆叉树结点个数
//这个会出现错误,因为size是全局变量,如果调用两次,会导致size的值变成两倍
int size = 0;
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	++size;
	BinaryTreeSize(root->left);
	BinaryTreeSize(root->right);
	return size;
}

// ⼆叉树结点个数
//错误的写法
//这个会出现错误,因为size是全局变量,如果调用两次,会导致size的值变成两倍
void BinaryTreeSize(BTNode* root,int* psize)
{
	if (root == NULL)
	{
		return 0;
	}
	++(*psize);
	BinaryTreeSize(root->left,psize);
	BinaryTreeSize(root->right,psize);
}
*/

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

// ⼆叉树叶⼦结点个数
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层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1)
		+ BinaryTreeLevelKSize(root->right, k - 1);
}

//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftDep = BinaryTreeDepth(root->left);
	int rightDep = BinaryTreeDepth(root->right);

	return leftDep > rightDep ? leftDep + 1 : rightDep + 1;
}

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}

	BTNode* leftFind = BinaryTreeFind(root->left, x);
	if (leftFind)
	{
		return leftFind;
	}

	BTNode* rightFind = BinaryTreeFind(root->right, x);
	if (rightFind)
	{
		return rightFind;
	}

	return NULL;
}

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(& ((*root) ->left)  );
	BinaryTreeDestory(& ((*root) ->right) );

	free(*root);
	*root = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Tree.h"

//创建节点
BTNode* buyNode(BTDataType x) {
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL) {
		perror("malloc fail!");
		exit(1);
	}

	newnode->data = x;
	newnode->left = newnode->right = NULL;

	return newnode;
}

void test01() {
	BTNode* node1 = buyNode(1);
	BTNode* node2 = buyNode(2);
	BTNode* node3 = buyNode(3);
	BTNode* node4 = buyNode(4);
	//BTNode* node5 = buyNode(5);
	//BTNode* node6 = buyNode(6);

	node1->left = node2;
	node1->right = node3;
	node2->left = node4;
	//node2->right = node5;
	//node3->left = node6;

	/*PreOrder(node1);
	printf("\n");
	InOrder(node1);
	printf("\n");
	PostOrder(node1);*/

	printf("size:%d\n", BinaryTreeSize(node1));// ⼆叉树结点个数
	printf("leaf size: %d\n", BinaryTreeLeafSize(node1));// ⼆叉树叶⼦结点个数
	printf("第K层 size:%d\n", BinaryTreeLevelKSize(node1, 2));// ⼆叉树第k层结点个数
	printf("depth/height:%d\n", BinaryTreeDepth(node1));//二叉树深度
	BTNode* find = BinaryTreeFind(node1, 33);
	printf("%s\n", find == NULL ? "未找到!" : "找到了!");// ⼆叉树查找值为x的结点
	BinaryTreeDestory(&node1);// ⼆叉树销毁
}

int main() {
	test01();
	return 0;
}

4.3 层序遍历

除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历

设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历.

实现层序遍历需要额外借助数据结构:队列

img

// 层序遍历
void LevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    QueuePush(&q, root);
    while (!QueueEmpty(&q))
    {
        BTNode* top = QueueFront(&q);
        printf("%c ", top->data);
        QueuePop(&q);
        if (top->
            _left) 
        {
            QueuePush(&q, top->
                      _left);
        }
        if (top->
            _right) 
        {
            QueuePush(&q, top->
                      _right);
        }
    }
    QueueDesTroy(&q);
}

4.4 判断是否为完全二叉树

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root) 
{
    Queue q;
    QueueInit(&q);
    QueuePush(&q, root);
    while (!QueueEmpty(&q))
    {
        BTNode* top = QueueFront(&q);
        QueuePop(&q);
        if (top == NULL) 
        {
            break;
        }
        QueuePush(&q, top->
                  _left);
        QueuePush(&q, top->
                  _right);
    }
    while (!QueueEmpty(&q))
    {
        BTNode* top = QueueFront(&q);
        QueuePop(&q);
        if (top != NULL) 
        {
            QueueDesTroy(&q);
            return false;
        }
    }
    QueueDesTroy(&q);
    return true;
}

4.5层序遍历和判断是否为完全二叉树完整代码

tree.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

//定义二叉树的链式结构
//二叉树结点的结构
//Binary 二进制的意思
typedef int BTDataType;

typedef struct BinaryTreeNode {
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

//定义队列结构
//typedef int QDataType;
//typedef struct BinaryTreeNode* QDataType;
typedef struct BTNode* QDataType;

typedef struct QueueNode {
	QDataType data;
	struct QueueNode* next;
}QueueNode;

typedef struct Queue {
	QueueNode* phead;//队头:删
	QueueNode* ptail;//队尾:插
	int size;//保存队列有效数据个数
}Queue;

//初始化队列
void QueueInit(Queue* pq);

// 入队列,队尾
void QueuePush(Queue* pq, QDataType x);

//队列判空
bool QueueEmpty(Queue* pq);

// 出队列,队头
void QueuePop(Queue* pq);

//取队头数据
QDataType QueueFront(Queue* pq);

//销毁队列
void QueueDestroy(Queue* pq);


//层序遍历
//借助数据结构---队列
void LevelOrder(BTNode* root);

tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "LevelOrder.h"

//初始化队列
void QueueInit(Queue* pq) {
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

// 入队列,队尾
void QueuePush(Queue* pq, QDataType x) {
	assert(pq);
	//申请新结点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL) {
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->phead == NULL) {//判断队列是否为空
		pq->phead = pq->ptail = newnode;
	}
	else {
		//队列不为空
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;//入一次,size++ 一次
}

//队列判空
bool QueueEmpty(Queue* pq) {
	assert(pq);
	return (pq->phead == NULL) && (pq->ptail == NULL);
}

// 出队列,队头
void QueuePop(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));

	//只有一个结点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead) {
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else {
		//删除队头元素
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	--pq->size;//出一次,size-- 一次
}

//取队头数据
QDataType QueueFront(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));//判断队列不为空

	return pq->phead->data;
}

//销毁队列
void QueueDestroy(Queue* pq) {
	assert(pq);
    /*注意下面这行在这里要注释掉,不然会报错*/
	//assert(!QueueEmpty(pq));//判断队列不为空,空队列不需要销毁

	QueueNode* pcur = pq->phead;
	while (pcur) {
		QueueNode* Next = pcur->next;
		free(pcur);
		pcur = Next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

// ⼆叉树销毁
void BinaryTreeDestory(BTNode * *root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));

	free(*root);
	*root = NULL;
}

//层序遍历
//借助数据结构---队列
void LevelOrder(BTNode * root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		//取队头,打印
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		//队头节点的左右孩子入队列
		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	//队列为空
	QueueDestroy(&q);
}

//判断二叉树是否为完全二叉树
//---队列
bool BinaryTreeComplete(BTNode * root)
{
	Queue q;
	QueueInit(&q);//初始化
	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;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "LevelOrder.h"

//创建节点
BTNode* buyNode(BTDataType x) {
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL) {
		perror("malloc fail!");
		exit(1);
	}

	newnode->data = x;
	newnode->left = newnode->right = NULL;

	return newnode;
}

void test01() {
	BTNode* node1 = buyNode(1);
	BTNode* node2 = buyNode(2);
	BTNode* node3 = buyNode(3);
	BTNode* node4 = buyNode(4);


	node1->left = node2;
	node1->right = node3;
	node2->left = node4;

	LevelOrder(node1);
	bool ret = BinaryTreeComplete(node1);
	printf("%s\n", ret == false ? "不是完全二叉树!" : "是完全二叉树!");
	BinaryTreeDestory(&node1);// ⼆叉树销毁
}

int main() {
	test01();
	return 0;
}

打印:

img

;