注:二叉树还是很难的,主要思想是递归,二叉树的创建,遍历是关键,做题还需掌握二叉树的拷贝翻转等等
1.二叉树节点结构体的定义
typedef char BTDataType; //方便修改二叉树中的数据类型
typedef struct BinaryTreeNode //定义二叉树结点的结构体
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
二叉树节点的结构体包含三个内容(定义的数据类型,左指针,右指针)
想要改二叉树中的数据类型只需要把上面的char改了就ok了
2.通过遍历数组实现二叉树
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* str, int* i) //这里写成str,是因为BTDataType是char类型,好区分,一般a是int类型
{
if (str[*i] == '#' )
{
(*i)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode)); //为二叉树结点申请空间并指向root指针
if (root == NULL)
{
printf("malloc fail");
exit(-1);
}
root->_data = str[(*i)++];
root->_left = BinaryTreeCreate(str, i);
root->_right = BinaryTreeCreate(str, i);
return root;
}
很简单的递归思想,根节点的左指针指向左子树,左子树怎么来,创建,左子树的根节点的左子树怎么来,创建,一步步递归 ,右边同理)
递归思想弄明白加几个判断就ok了
1.数组里的数据如果是‘#’返回NULL空指针
2.如果不是‘#’,新建一个二叉树节点(用malloc在堆上开辟空间,申请失败吧啦吧啦)
3.存数据,左指针递归,右指针递归,都完事了return刚刚建好的二叉树节点的指针
注:通过传过来的指针变量控制数组下标(递归的实现跨越了很多函数,必须通过地址控制变量)
3.二叉树的拷贝
怎么说呢,二叉树的拷贝还是很重要的,先拷贝之后在进行操作,这样可以保留原来的二叉树
//二叉树拷贝
struct TreeNode* CopyBinaryTree(struct TreeNode* root)
{
if (root == NULL)
{
return NULL;
}
//拷贝左子树
struct TreeNode* left = CopyBinaryTree(root->left);
//拷贝右子树
struct TreeNode* right = CopyBinaryTree(root->right);
//创建结点
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
//将当前结点的信息拷贝到新结点中
newNode->val = root->val;
newNode->left = left;
newNode->right = right;
return newNode;
}
这里建立两个指针 left 和 right 来接收递归的值,其实也可以不接收,直接左右指针指向递归值
newNode->left = Copy_invertBinaryTree(root->left);
newNode->right = Copy_invertBinaryTree(root->right);
想法就是尽量保存递归值,避免多次递归(比如递归值不保存直接用来判断,结果还得算一次)
4.二叉树翻转
二叉树翻转就是用了拷贝的思想,想明白很简单,左边指向右边 (拷贝加翻转)
//226. 翻转二叉树
struct TreeNode* Copy_invertBinaryTree(struct TreeNode* root)
{
if (root == NULL)
{
return NULL;
}
//左指针指向源节点右子树
struct TreeNode* left = Copy_invertBinaryTree(root->right);
//右指针指向源节点左子树
struct TreeNode* right = Copy_invertBinaryTree(root->left);
//创建结点
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
//将当前结点的信息拷贝到新结点中
newNode->val = root->val;
newNode->left = left;
newNode->right = right;
return newNode;
}
5.二叉树的层序遍历
层序遍历,主要思想是利用一个队列,队列的特点是先进先出,这样插入的顺序就是后面打印的顺序,总结就是打印并头删根节点,尾插子节点
具体实现过程:
1.把根节点的地址尾插到队列中
2.写一个while循环,判断条件是队列不为空
3.新建front指针来保存队首元素(队列存放的是int类型,得转换成结构体指针才能使用箭头)
4.打印front指针指向的二叉树节点的值
5.队列头删
6.队列尾插左子树地址,右子树地址(不为空的时候才插入)
7.队列为空,也尽量销毁队列,因为不清楚是否有哨兵位
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) //利用一个队列进行层序遍历
{
if (root == NULL)
return 0;
Queue q;
QueueInit(&q);
QueuePush(&q, root); //这里是把地址进行尾插(因为root的值就是一个地址)
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q); //获取队列头部元素(一个地址)保存到front指针中
printf("%c ", front->_data); //打印一下队头元素的值
QueuePop(&q);
if (front->_left)
QueuePush(&q, front->_left); //把左子树的地址尾插
if (front->_right)
QueuePush(&q, front->_right); //再把右子树的地址尾插
}
printf("\n");
QueueDestroy(&q);
}
6.判断二叉树是否是完全二叉树
判断是否是完全二叉树的思想与层序遍历的思想相似,都是按层将数据存入队列,不过这次空指针也要存入队列(值为0)
具体实现过程:
1.和层序遍历一样,先把队头元素尾插到队列中
2.建立一个while循环(进入循环条件是队列不为空)
3.新建二叉树结构体指针 front 用来存放队首元素的值
4.增加一条 if 语句,如果队首值为0( front 是空指针),退出循环(如果是完全二叉树的话后面的应该都为0了)
5..建立第二次while循环(进入循环条件还是队列不为空)
6.如果队首是非零元素,销毁队列,返回false
7.头删,继续循环
8.循环完毕都没有返回false,说明全是0(空指针),销毁队列,返回true
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
if (root == NULL)
return false;
Queue q; //定义一个队列
QueueInit(&q); //初始化
QueuePush(&q, root); //把root中的地址存入队首
while (!QueueEmpty(&q)) //队列不为空进入循环
{
BTNode* front = QueueFront(&q); //定义一个指针变量front,指针中存放的值为队首元素的值
if (!front) //队首存的值为0,退出循环
break;
QueuePush(&q, front->_left); //左子结点的地址数放入队列
QueuePush(&q, front->_right); //右子节点的地址数放入队列
QueuePop(&q); //删除队首元素
}
while (!QueueEmpty(&q))
{
if (QueueFront(&q)) //队首为非零元素
{
QueueDestroy(&q);
return false; //非完全二叉树
}
QueuePop(&q);
}
QueueDestroy(&q);
return true; //队列全为0,就是完全二叉树
}
7.二叉树的其他功能
1)二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
if (root==NULL)
{
return;
}
BinaryTreeDestory(root->_left);
BinaryTreeDestory(root->_right);
free(root);
}
2)二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
//如果是空节点,为0,返回左子树节点个数加上右子树节点个数
return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
3)二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL) //空节点返回0
{
return 0;
}
if (root->_left == NULL && root->_right == NULL) //没有左右子结点那么就返回一个1(本结点)
{
return 1;
}
return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right); //左子树结点个数加右子树节点个数
}
4)二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) //传过来的是根节点指针, 和在第k层结点
{
if (root == NULL) //空节点返回0
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
5)二叉树查找值为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;
}
6)二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
7)二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
8)二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NUll ");
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
8.全部代码,包含队列
BinaryTree.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef char BTDataType;
typedef struct BinaryTreeNode //定义二叉树结点的结构体
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi); //传过去两个参数,数组和初始值为0的参数i的地址
//BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi); //传过去的是遍历的数组,数组元素个数,初始值为0的参数i的地址
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
BinaryTree.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"BinaryTree.h"
#include"Queue.h"
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
//法一(传两个参数)
BTNode* BinaryTreeCreate(BTDataType* str, int* i) //这里写成str,是因为BTDataType是char类型,好区分,一般a是int类型
{
if (str[*i] == '#' )
{
(*i)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode)); //为二叉树结点申请空间并指向root指针
if (root == NULL)
{
printf("malloc fail");
exit(-1);
}
root->_data = str[(*i)++];
root->_left = BinaryTreeCreate(str, i);
root->_right = BinaryTreeCreate(str, i);
return root;
}
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
// 法二(三个参数)-其实没啥区别
//BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi) //传过去的是遍历的数组,数组元素个数,初始值为0的参数地址
//{
// if (a[*pi] == '#' || *pi >= n) //注意这里'#'别写成双引号了,这个错误找了好久
// {
// (*pi)++;
// return NULL;
// }
//
// BTNode* node = (BTNode*)malloc(sizeof(BTNode));
// if (node == NULL)
// {
// printf("malloc fail");
// exit(-1);
// }
// node->_data = a[(*pi)++];
// node->_left = BinaryTreeCreate(a, n, pi);
// node->_right = BinaryTreeCreate(a, n, pi);
// return node;
//}
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
if (root==NULL)
{
return;
}
BinaryTreeDestory(root->_left);
BinaryTreeDestory(root->_right);
free(root);
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
//如果是空节点,为0,返回左子树节点个数加上右子树节点个数
return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL) //空节点返回0
{
return 0;
}
if (root->_left == NULL && root->_right == NULL) //没有左右子结点那么就返回一个1(本结点)
{
return 1;
}
return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right); //左子树结点个数加右子树节点个数
}
// 二叉树第k层节点个数(定义k>=1)也就是根节点所在层为第一层
int BinaryTreeLevelKSize(BTNode* root, int k) //传过来的是根节点指针, 和在第k层结点
{
if (root == NULL) //空节点返回0
{
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;
}
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NUll ");
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) //利用一个队列进行层序遍历
{
if (root == NULL)
return 0;
Queue q;
QueueInit(&q);
QueuePush(&q, root); //这里是把地址进行尾插(因为root的值就是一个地址)
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q); //获取队列头部元素(一个地址)保存到front指针中
printf("%c ", front->_data); //打印一下队头元素的值
QueuePop(&q);
if (front->_left)
QueuePush(&q, front->_left); //把左子树的地址尾插
if (front->_right)
QueuePush(&q, front->_right); //再把右子树的地址尾插
}
printf("\n");
QueueDestroy(&q);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
if (root == NULL)
return false;
Queue q; //定义一个队列
QueueInit(&q); //初始化
QueuePush(&q, root); //把root中的地址存入队首
while (!QueueEmpty(&q)) //队列不为空进入循环
{
BTNode* front = QueueFront(&q); //定义一个指针变量front,指针中存放的值为队首元素的值
if (!front) //队首存的值为0,退出循环
break;
QueuePush(&q, front->_left); //左子结点的地址数放入队列
QueuePush(&q, front->_right); //右子节点的地址数放入队列
QueuePop(&q); //删除队首元素
}
while (!QueueEmpty(&q))
{
if (QueueFront(&q)) //队首为非零元素
{
QueueDestroy(&q);
return false; //非完全二叉树
}
QueuePop(&q);
}
QueueDestroy(&q);
return true; //队列全为0,就是完全二叉树
}
Queue.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDataType; //这样不用每次都写 int QDataType
typedef struct QueueNode //定义结点
{
QDataType data; //存放数据
struct QueueNode* next; //存放指针指向下一个结点
}QNode;
typedef struct Queue //凡是结构都写一下typedef,不然每次都写struct很难受
{
QNode* head;
QNode* tail;
int size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x); //尾插传参数传的队列指针和尾插的数据
void QueuePop(Queue* pq); //头部删除
QDataType QueueFront(Queue* pq); //获取队列头部元素
QDataType QueueBack(Queue* pq); // 获取队列队尾元素
bool QueueEmpty(Queue* pq); //为空为1,不为空为0
int QueueSize(Queue* pq); // 获取队列中有效元素个数
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 QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
//del = NULL; //这个不用置空,因为del是局部变量,出while就销毁了
}
pq->head = pq->tail = NULL; //free之后变为野指针,要置空
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x) //尾插
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
//新建一个结点,由于只有这个函数需要开辟内存,所以不用写BuyNode函数
if (newnode == NULL) //开辟失败
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq) //头部删除
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL) //只有一个结点
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head; //保存当前节点
pq->head = pq->head->next; //后移
free(del);
}
pq->size--;
}
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;
}
bool QueueEmpty(Queue* pq) //为空为1,不为空为0
{
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
int QueueSize(Queue* pq) //获取队列中有效元素个数
{
assert(pq);
//int size = 0;
//QNode* cur = pq->head;
//while (cur) //size的时间复杂度是o(n),可以优化一下,在结构体中定义一个size
//{
// cur = cur->next;
// ++size;
//}
//return size;
return pq->size;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"BinaryTree.h"
#include"Queue.h"
int main()
{
//char a[] = "ABD##E#H##CF##G##";
char a[] = "ABD##E##CF##G##";
int i = 0;
int n = sizeof(a) / sizeof(a[0]);
BTNode* root1 = BinaryTreeCreate(a, &i); //传过去两个参数,数组和初始值为0的参数i的地址
//BTNode* root1 = BinaryTreeCreate(a, n, &i); //传过去的是遍历的数组,数组元素个数,初始值为0的参数i的地址
BinaryTreePrevOrder(root1); //前序遍历
printf("\n");
BinaryTreeInOrder(root1); //中序遍历
printf("\n");
BinaryTreePostOrder(root1); //后序遍历
printf("\n");
//int ksize = BinaryTreeLevelKSize(root1, 3);
//printf("%d\n", ksize);
//BTNode* root2 = BinaryTreeFind(root1,'h'); //寻找存放数据D的结点,找到了返回指针,找不到返回空
//printf("%c", root2->_data);
BinaryTreeLevelOrder(root1); //层序遍历
int ret = BinaryTreeComplete(root1);
printf("%d", ret);
}