目录
1.二叉树链式结构的实现
链式二叉树就是普通二叉树是通过类似链表的形式构成的,如果使用顺序结构去存储会造成很多的空间浪费,它是通过定义两个左右孩子指针节点分别指向左右孩子,并且通过图中来看,二叉树定义是递归式的,因此后序基本操作中都是按照该概念实现的。
typedef int BTDataType; typedef struct BinaryTreeNode { BTDataType _data; struct BinaryTreeNode* left; struct BinaryTreeNode* right; }BTNode;
2.二叉树的遍历
2.1前序、中序、以及后序遍历
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。(先访问根、再访问左子树、然后接着右子树)
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。(先访问左子树、再访问根、接着访问右子树)
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。 (先访问左子树、再访问右子树、最后访问根)
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
前序遍历:
中序遍历:
后序遍历:
运行结果:
通过代码发现前序、中序、后序遍历,这三个函数代码都是一样的,只不过是访问根的时机不同而已。
前序遍历递归展开图:
求树的节点个数:
图中if直接return就行,不用return 0,复制粘贴忘记删除了。
注:禁止使用全局变量和使用static修饰局部变量:
如果使用全局变量每次运行函数都会进行一次累加,所以每次都要初始化为0,甚至在多线程的情况还有其他情况发生。
使用static修饰局部变量有着和全局变量差不多的作用,甚至无法进行再次初始化为0。
从代码上看起始就是一个后序遍历。
求树的高度:
求当前树第k层的节点个数:
二叉树查找值为x的结点:
层序遍历:
判断二叉树是否是完全二叉树:
通过二叉树的性质可以选择使用层序遍历来判断二叉树是否是完全二叉树,完全二叉树遇到第一个空节点时后面一定是全空节点,如果有非空节点则一定是普通二叉树。
二叉树的销毁:
注:学习二叉树最好的方法就是画递归展开图。
完整代码:
main.c
#include "Queue.h"
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
BTNode* CreatBinaryTree(BTDataType x)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == NULL)
{
perror("malloc fail\n");
return NULL;
}
newnode->data = x;
newnode->left = NULL;
newnode->right = NULL;
return newnode;
}
//快速构建二叉树
BTNode* CreatTree()
{
BTNode* root1 = CreatBinaryTree(1);
BTNode* root2 = CreatBinaryTree(2);
BTNode* root3 = CreatBinaryTree(3);
BTNode* root4 = CreatBinaryTree(4);
BTNode* root5 = CreatBinaryTree(5);
BTNode* root6 = CreatBinaryTree(6);
//增加两个节点
BTNode* root7 = CreatBinaryTree(7);
BTNode* root8 = CreatBinaryTree(8);
root1->left = root2;
root1->right = root4;
root2->left = root3;
root2->right = root5;
root4->left = root6;
root4->right = root7;
root3->left = root8;
return root1;
}
//前序遍历
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;
}
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);
}
//层序遍历
void LevelOrder(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);
}
QueueDestory(&q);
}
//第一种方法
//求这颗树的节点的个数
int TreeSize(BTNode* root)
{
if (root == NULL)//遇到空返回0
return 0;
int leftSize = TreeSize(root->left);
int rightSize = TreeSize(root->right);
return leftSize + rightSize + 1;//+1加的是当前根
//思路就是把一颗二叉树所有节点当成根。
}
//第二种方法
void TreeSize2(BTNode* root, int* size)
{
if (root == NULL)//遇到空返回0
return;
(*size)++;//除了NULL,每次进行函数调用压栈都会累计一次
TreeSize2(root->left, size);
TreeSize2(root->right, size);
}
//求树的高度
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->left)+ 1;
int rightHeight = TreeHeight(root->right) + 1;
return leftHeight > rightHeight ? leftHeight : rightHeight;
}
//求第k层的节点个数
int TreeKLevel(BTNode* root, int k)
{
if (k == 0)
return 0;
if (root == NULL)
return 0;
if (k == 1)
return 1;
int leftSize = TreeKLevel(root->left, k - 1);
int rightSize = TreeKLevel(root->right, k - 1);
return leftSize + rightSize;
}
//二叉树查找值为x的结点
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* lret = BinaryTreeFind(root->left, x);
//if (lret)
// return lret;
//return BinaryTreeFind(root->right, x);
}
//判断二叉树是否是完全二叉树
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;
}
else
{
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front != NULL)
{
QueueDestory(&q);
return false;
}
}
QueueDestory(&q);
return true;
}
int main()
{
BTNode* root = CreatTree();
//前序遍历
printf("前序遍历:");
PreOrder(root);
printf("\n");
//中序遍历
printf("中序遍历:");
InOrder(root);
printf("\n");
//后序遍历
printf("后序遍历:");
PostOrder(root);
printf("\n");
int size = TreeSize(root);
printf("树节点个数:%d\n", size);
int size2 = 0;
TreeSize2(root, &size2);
printf("树节点个数:%d\n", size2);
int height = TreeHeight(root);
printf("树的高度:%d\n", height);
int k = 3;
int n = TreeKLevel(root, k);
printf("第k层节点的个数:%d\n", n);
BTDataType x = 7;
BTNode* ret = BinaryTreeFind(root, x);
printf("查找值为x的结点:%d\n", ret->data);
printf("层序遍历:");
LevelOrder(root);
printf("\n");
printf("判断二叉树是否是完全二叉树:%d\n", TreeComplete(root));
return 0;
}
队列代码:
Queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail\n");//输出错误原因,但不会终止程序,需要return
return;
}
newnode->next = NULL;
newnode->data = x;
if (pq->head == NULL)
{
assert(pq->tail == NULL);
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq && pq->size > 0);
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
pq->size--;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
Queue.h
#define _CRT_SECURE_NO_WARNLNGS 1
#pragma once
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* head;//队头
QNode* tail;//队尾
int size;//记录数据个数
}Queue;
void QueueInit(Queue* pq);//创建队列
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
int QueueSize(Queue* pq);//返回数据个数
bool QueueEmpty(Queue* pq);//判断队列是否为空
QDataType QueueFront(Queue* pq);//返回队头数据
QDataType QueueBack(Queue* pq);//返回队尾数据