树应该是数据结构中最难的吧, 复习到这块做二叉树的练习题, 不是很好做, 大部分都是递归题, 树的考点也很多。
首先要区分树和森林, 其次树又分为二叉树和非二叉树。
我们主要以二叉树为主复习, 因为往年考点较高是二叉树。
一、树的存储分为两种,一种是线性存储, 一种是非线性存储。
1、线性存储我们在heap这章提及过, 它的实现很简单, 需要注意的是父结点、孩子结点、最后一个非叶子结点之间运算关系。 这块不作为重点讨论。
2、非线性存储, 也就是链式存储, 每个结点中间至少包含(值、左孩子指针、右孩子指针等)这些信息, 在逻辑上,左孩子指针指向左孩子结点地址, 有孩子指针指向右孩子结点。 这样一次类推, 形成一种逻辑上有树规律,但物理上没有树规律的结构。
二、如何构建一个树呢
如何构建一颗树,这是一个刚开始接触二叉树就必须要考虑的问题。
我这块不卖关子。 我第一次构建是看别人写的模板写的。
一般来说,构建一棵树, 我们会把构建的内容存放在一个数组中。 然后通过递归来构建。 听起来可能难, 其实看代码品一会就懂得了。我直接放代码。
这是给我们数组:
char a[] = { ‘1’, ‘5’, ‘#’, ‘#’, ‘3’, ‘#’, ‘#’ };
调用方式:
int pi = 0;
BTNodeInit(a, &pi);
最后BTNodeInit接口的返回值就是构建树的头结点head。
三、树的遍历方式
分别是: 先序遍历、中序遍历、后序遍历、层序遍历。
先序遍历, 口诀就是根左右; 中序遍历左根右; 后序遍历左右根。
层序遍历就是每一层一层的遍历。
一般来说对于先序、中序、后续序遍历的设计有两种方式。
1、递归实现。
2、非递归实现(需要借助栈)
层序遍历采用队列去实现。
(这些我在面试题总结会写实现过程)
面试当中一般常考给我们先序遍历,中序遍历,后序遍历当中的两个序列,
让我们判断给哪两个序列可以画出二叉树的问题?
如果想要通过两个序列去画二叉树, 这两个序列中一定要包含中序遍历, 如果只有先序、后序遍历画不出二叉树的。
四、二叉树的分类
1、满二叉树
其实我们发现树是分一层一层的, 而每一层最多的结点个数为2^(n-1), n为层数。
在这里如果每一层的结点个数都是最大个数2^(n-1), 则这个二叉树就是满二叉树。
2、完全二叉树
在满二叉树的基础上做一些区分, 就是在最后一层, 最后一层可以不用满足2^(n-1)个数, 但是有个条件,最后一层结点从左右往数都是连续存放的,不可能说两个之间有一个空结点。
下面这个就不满足条件(最后一层1和7之间有空结点), 不是完全二叉树。
二叉树还有一个有意思的,那就是排序二叉树。
所谓排序二叉树是按一定规律存放的树。
如果每个结点的值大于左孩子结点值, 但是小于右孩子结点值。
或者每个结点的值小于左孩子结点值, 大于右孩子结点值。
这种结构的树就是排序二叉树。
这种排序二叉树在中序遍历会得到有序的序列。
上图左边中序遍历后:1, 2, 4, 5, 6, 9从小到大的序列。
同理右边的是从大到小的序列。
并且排序二叉树的查找效率很高logn。 决定效率取决于高度n
这也是后面平衡二叉树、 红黑树诞生的起源。
下面是模拟写出来的树
BTNodeTree.h
#pragma once
/*
project: 模拟实现二叉树
date: 2020-6-19
name: HMW
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef char BTNDataType;
typedef struct BTNode
{
BTNDataType val; //数据域
struct BTNode* _lchild; //左孩子节点
struct BTNode* _rchild; //右孩子节点
}BTNode;
//初始化创建树
BTNode* BTNodeInit(BTNDataType* a, int* pi);
//树的销毁
void BTNodeDestroy(BTNode** root);
//先序遍历
void BinaryTreePrevOrder(BTNode* root);
//中序遍历
void BinaryTreeInOrder(BTNode* root);
//后序遍历
void BinaryTreePostOrder(BTNode* root);
//镜像转化
void MirrorRecursively(BTNode* root);
//判断是否为对称二叉树
int isSameTree(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);
//树的节点个数
int BinaryTreeSize(BTNode* root);
//树的叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
//第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//第k层节点的叶子个数
int BinaryTreeLevelKLeafSize(BTNode* root, int k);
//返回查找的节点的值
BTNode* BinaryTreeFind(BTNode* root, BTNDataType x);
//层序遍历
void BinaryTreeLevelOrder(BTNode* root);
//非递归实现的先序遍历
void BinaryTreePrevOrderNonR(BTNode* root);
//非递归实现的中序遍历
void BinaryTreeInOrderNonR(BTNode* root);
//非递归实现的后序遍历
void BinaryTreePostOrderNonR(BTNode* root);
BTNodeTree.c
//#include"BTNodeTree.h"
#include"Stack.h"
#include"Queue.h"
//初始化创建树
BTNode* BTNodeInit(char* a, int* pi)
{
//a是传入构建数组首元素的地址, pi是一个全局的下标,用它来取构建的元素。
if (!a)
return NULL;
//'#' -- 代表着这个值为空
if (a[*pi] != '#') //这个元素不为空
{
BTNode* root = (BTNode*)malloc(sizeof(BTNode)); //创建一个结点
root->val = a[*pi]; //把数组中的值赋给结点
++(*pi); //下标偏移
root->_lchild = BTNodeInit(a, pi); //结点左孩子指针 = 递归的返回值
++(*pi); //下标偏移
root->_rchild = BTNodeInit(a, pi);
return root; //返回构建的结点
}
else
{
return NULL; //这个元素为空,返回null指针
}
}
//树的销毁
void BTNodeDestroy(BTNode** root)
{
//树的销毁采用后序遍历的方式释放, 采用二级指针来释放。
if (*root)
{
BTNodeDestroy(&(*root)->_lchild);
BTNodeDestroy(&(*root)->_rchild);
free(*root);
*root = NULL;
}
else
return;
}
//先序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root)
{
printf("%c ", root->val);
BinaryTreePrevOrder(root->_lchild);
BinaryTreePrevOrder(root->_rchild);
}
else
return;
}
//中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root)
{
BinaryTreeInOrder(root->_lchild);
printf("%c ", root->val);
BinaryTreeInOrder(root->_rchild);
}
}
//后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root)
{
BinaryTreePostOrder(root->_lchild);
BinaryTreePostOrder(root->_rchild);
printf("%c ", root->val);
}
}
//非递归实现的先序遍历
void BinaryTreePrevOrderNonR(BTNode* root)
{
if (!root)
return;
//需要栈实现.
Stack st;
StackInit(&st);
BTNode* cur = root;
while (cur || !StackEmpty(&st))
{
while (cur)
{
printf("%c ", cur->val);
StackPush(&st, cur);
cur = cur->_lchild;
}
//出来就是cur == NULL, 左到底
BTNode* Top = StackTop(&st);
StackPop(&st);
if (Top->_rchild)
cur = Top->_rchild;
}
StackDestroy(&st);
}
//非递归实现的中序遍历
void BinaryTreeInOrderNonR(BTNode* root)
{
if (!root)
return;
Stack st;
StackInit(&st);
BTNode* cur = root;
while (cur || !StackEmpty(&st))
{
while (cur)
{
StackPush(&st, cur);
cur = cur->_lchild;
}
BTNode* Top = StackTop(&st);
printf("%c ", Top->val);
StackPop(&st);
if (Top->_rchild)
cur = Top->_rchild;
}
}
//非递归实现的后序遍历
void BinaryTreePostOrderNonR(BTNode* root)
{
if (!root)
return;
Stack st;
StackInit(&st);
BTNode* cur = root;
BTNode* prev = NULL;
while (cur || !StackEmpty(&st))
{
while (cur)
{
StackPush(&st, cur);
cur = cur->_lchild;
}
BTNode* Top = StackTop(&st);
if (!Top->_rchild || Top->_rchild == prev)
{
printf("%c ", Top->val);
StackPop(&st);
prev = Top;
}
else
cur = Top->_rchild;
}
}
//层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
if (!root)
return;
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c ", front->val);
if (front->_lchild)
QueuePush(&q, front->_lchild);
if (front->_rchild)
QueuePush(&q, front->_rchild);
}
QueueDestroy(&q);
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
//0代表不是完全二叉树的情况, 1代表是完全二叉树的情况
if (!root)
return 0;
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
break;
else
{
QueuePush(&q, front->_lchild);
QueuePush(&q, front->_rchild);
}
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front != NULL)
{
QueueDestroy(&q);
return 0;
}
}
return 1;
}
//树的节点个数
int BinaryTreeSize(BTNode* root)
{
if (root)
{
return BinaryTreeSize(root->_lchild) + BinaryTreeSize(root->_rchild)
+ 1;
}
else
return 0;
}
//树的叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (!root)
return 0;
if (root->_lchild == NULL && root->_rchild == NULL)
return 1;
return BinaryTreeLeafSize(root->_lchild) + BinaryTreeLeafSize(root->_rchild);
}
//第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL || k <= 0)
return 0;
if (root && k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->_lchild, k - 1) + BinaryTreeLevelKSize(root->_rchild, k - 1);
}
//第k层节点的叶子个数
int BinaryTreeLevelKLeafSize(BTNode* root, int k)
{
if (!root || k <= 0)
return 0;
if (root && k == 1)
{
if (root->_lchild == NULL && root->_rchild == NULL)
return 1;
return 0;
}
return BinaryTreeLevelKLeafSize(root->_lchild, k - 1) +
BinaryTreeLevelKLeafSize(root->_rchild, k - 1);
}
//返回查找的节点的值
BTNode* BinaryTreeFind(BTNode* root, BTNDataType x)
{
if (!root)
return NULL;
if (root->val == x)
return root;
else
{
return BinaryTreeFind(root->_lchild, x);
return BinaryTreeFind(root->_rchild, x);
}
}
//镜像转化
void MirrorRecursively(BTNode* root)
{
if (!root)
return;
if (root->_lchild == NULL && root->_rchild == NULL) //可有效提高效率
return;
BTNode* tmp = root->_lchild;
root->_lchild = root->_rchild;
root->_rchild = tmp;
if(root->_lchild)
MirrorRecursively(root->_lchild);
if(root->_rchild)
MirrorRecursively(root->_rchild);
}
// -- two
int Judge_Same(BTNode* root1, BTNode* root2)
{
if (root1 == NULL && root2 == NULL)
return 1;
else if (root1 != NULL && root2 == NULL)
return 0;
else if (root1 == NULL && root2 != NULL)
return 0;
if (root1->val == root2->val)
{
return Judge_Same(root1->_lchild, root2->_rchild)
&& Judge_Same(root1->_rchild, root2->_lchild);
}
else
return 0;
}
//判断一个数是否是另一棵树的子树。 - one
int isSameTree(BTNode* root)
{
if (!root)
return 0; //0代表失败
if (root->_lchild != NULL && root->_rchild != NULL)
{
return Judge_Same(root->_lchild, root->_rchild);
}
else if (root->_lchild == NULL && root->_rchild == NULL)
return 1;
else
return 0;
}
test.c
#include"BTNodeTree.h"
#include"Stack.h"
#include"Queue.h"
void test_BTNodeTree()
{
char str[] = { '8', '6', '5', '#', '4', '#', '#', '7', '#', '#', '6', '7',
'#', '#', '9', '#', '#' };
//char str[] = { '1', '5', '#', '#', '3', '#', '#' };
//char str[] = { '1', '#', '#' };
int pi = 0;
BTNode* root = BTNodeInit(str, &pi);
puts("先序遍历");
BinaryTreePrevOrder(root);
puts("\n中序遍历");
BinaryTreeInOrder(root);
puts("\n后序遍历");
BinaryTreePostOrder(root);
puts("\n层序遍历");
BinaryTreeLevelOrder(root);
puts("\n非递归实现的先序遍历");
BinaryTreePrevOrderNonR(root);
puts("\n非递归实现的中序遍历");
BinaryTreeInOrderNonR(root);
puts("\n非递归实现的后序遍历");
BinaryTreePostOrderNonR(root);
if (BinaryTreeComplete(root))
{
puts("\n完全二叉树");
}
else
puts("\n非完全二叉树!");
printf("二叉树节点的个数为%d\n", BinaryTreeSize(root));
printf("二叉树叶子节点的个数为:%d\n", BinaryTreeLeafSize(root));
printf("第k层的节点个数为:%d\n", BinaryTreeLevelKSize(root, 3));
printf("第k层的节点叶子个数为:%d\n", BinaryTreeLevelKLeafSize(root, 3));
BTNode* p = BinaryTreeFind(root, '3');
if (p)
printf("返回的值为:%c\n", p->val);
else
puts("没有这个值的节点");
puts("");
BTNodeDestroy(&root);
if (root == NULL)
puts("清空");
else
puts("内存泄漏!");
}
int main()
{
test_BTNodeTree();
//test_Swordoffer();
system("pause");
return 0;
}
测试结果:
那个, 这是以前还没有学习c++写的, 所以关于栈、队列没有使用STL标准库, 自己的接口, 这两个接口我就不在这里发了, 我在github上传, 有兴趣的朋友自己弄着玩。
https://github.com/haomengwudaydayup/learn_c_language/tree/master/PORT