写在前面
最近工作中遇到了一个问题,某个进程占用cpu过高,初步分析得知应该是链表的频繁创建,插入,删除导致的。所以思考有没有什么方法可以改进的。
因为遇到的问题本质是一个表达式求值的问题,所以想到使用二叉树的方式来解决。由此刚好重新整理了以前写的二叉树笔记。
参考书籍
数据结构 ——严蔚敏
代码
.h
#ifndef __BITREE__
#define __BITREE__
typedef struct {
int key;
}TBiData;
typedef struct BiNode {
TBiData BiData;
struct BiNode* lchild, * rchild; //左右两个指针
}BiNode;
//初始化根节点
BiNode* InitRootBiTree(TBiData data);
//判断完全二叉树的某一节点是否存在,如果存在返回当前节点地址,如果不存在返回空
BiNode* BITreeNodeExit(BiNode* T, int node);
/*
*创建二叉树。以补全为完全二叉树的方法创建
*T 二叉树根节点
* node 要新加的二叉树节点的位置
* data 新建节点的数据域 将会重新复制其中的数据
*/
int CreateBiTree(BiNode* T, int node, TBiData data);
// 后序遍历销毁二叉树
void DestroyBiTree(BiNode* root);
//=============================================================================
typedef struct BiNode_Seq {
TBiData BiData;
//struct BiNode* lchild, * rchild; //左右两个指针 顺序二叉树不需要左右指针来指示左右子树关系
}BiNode_Seq;
#define SIZEOFFSET 100000
//初始化顺序二叉树
BiNode_Seq* InitBiTree_Seq(int size);
//创建顺序二叉树
int CreateBiTree_Seq(BiNode_Seq* T, int node, TBiData data);
//前序遍历顺序二叉树
void InOrderTraverse_Seq(BiNode* T);
// 销毁顺序二叉树
void DestroyBiTree_Seq(BiNode* root);
#endif // !1
.c
#include "BiTree.h"
#include "stdio.h"
#include "malloc.h"
#include "string.h"
//前序遍历树 递归
void InOrderTraverse(BiNode *T) {
if (T) {
InOrderTraverse(T->lchild);
//对节点数据进行操作
printf("key = %d \n", T->BiData.key);
InOrderTraverse(T->rchild);
}
}
//初始化根节点
BiNode* InitRootBiTree(TBiData data) {
BiNode* T = (BiNode*)(0);
if (!T) {
T = (BiNode*)malloc(sizeof(BiNode));
if(!T) return T;
T->lchild = (BiNode*)(0); //新节点的左右孩子指针置为0
T->rchild = (BiNode*)(0);
//memset(&(T->BiData), 0, sizeof(TBiData));
memcpy(&(T->BiData), &data, sizeof(TBiData));
}
return T;
}
//判断完全二叉树的某一节点是否存在,如果存在返回当前节点地址,如果不存在返回空
BiNode* BITreeNodeExit(BiNode* T ,int node) {
if (!T)
return NULL;
int BiTreeStack[10] = { 0 };//栈
int i = 0;
if (node == 1) return T;
BiNode* mT = NULL;
while (node != 1) {
if (node % 2) {//奇数
node = (node - 1) / 2;
BiTreeStack[i] = 2; //右边
}
else {
node = node / 2;
BiTreeStack[i] = 1; //左边
}
i++;
if (i >= 10) return NULL;
}
i--;
mT = T;
for (i; i >= 0; i--) {//倒叙出栈 得出找到目的节点的顺序
//printf("%d ", BiTreeStack[i]);
if (mT) {
if (BiTreeStack[i] == 2) {//右边
mT = mT->rchild;
}
else if (BiTreeStack[i] == 1) {//左边
mT = mT->lchild;
}
}
else
return NULL;
}
return mT;
}
/*
*创建二叉树。以补全为完全二叉树的方法创建
* T 二叉树根节点 ,如果未分配内存且node为1 则自动分配内存
* node 要新加的二叉树节点的位置
* data 新建节点的数据域 将会重新复制其中的数据
*/
int CreateBiTree(BiNode *T,int node,TBiData data) {
BiNode* mT = NULL;
if (!T)
return -1;
mT = BITreeNodeExit(T, node);
if (mT) {//若节点存在就替换数据
memcpy(&(mT->BiData), &data, sizeof(TBiData));
return 0;
}
else {//结点不存在
mT = NULL;
int fnode = node % 2 ? (node - 1) / 2 : node / 2; //算出父节点
mT = BITreeNodeExit(T, fnode);
if(mT){//若父节点存在 分配内存,塞入数据
BiNode** newnode = NULL;
newnode = node % 2 ? &(mT->rchild) : &(mT->lchild);
*newnode = (BiNode*)malloc(sizeof(BiNode));
if (*newnode == NULL) return -1; //内存分配失败
(*newnode)->lchild = (BiNode*)(0); //新节点的左右孩子指针置为0
(*newnode)->rchild = (BiNode*)(0);
//memset(&((*newnode)->BiData), 0, sizeof(TBiData));
memcpy(&((*newnode)->BiData), &data, sizeof(TBiData));
return 0;
}else
return -1;
}
return -1;
}
// 后序遍历销毁二叉树
void DestroyBiTree(BiNode* root) {
if (root == NULL) {
return;
}
DestroyBiTree(root->lchild); // 销毁左子树
DestroyBiTree(root->rchild); // 销毁右子树
//printf("删除 %d \n", root->BiData.key);
free(root); // 销毁当前节点
//printf("删除 %d \n", root->BiData.key); 这里因为内存已释放 可能出现乱码
}
//==================================================================================
//初始化顺序二叉树
BiNode_Seq* InitBiTree_Seq(int size) {
BiNode_Seq* T = (BiNode_Seq*)(0);
if (size <= 0)
return T;
//这里我加1的目的是为了 空第一个空间不使用,让下标就等于二叉树节点号,同时在第一个节点的key存储顺序二叉树的长度 偏移SIZEOFFSET
T = (BiNode_Seq*)malloc(sizeof(BiNode_Seq) * (size + 1));
if(!T) return T;
T[0].BiData.key = size + SIZEOFFSET; //第一个节点记录长度
return T;
}
//创建顺序二叉树
int CreateBiTree_Seq(BiNode_Seq* T,int node,TBiData data) {
if (!T)
return -1;
if (node > (T[0].BiData.key - SIZEOFFSET)) //超长了
return -1;
memcpy(&T[node].BiData, &data, sizeof(TBiData));
return 0;
}
//前序遍历顺序二叉树
void InOrderTraverse_Seq(BiNode* T) {
if (T) {
int count = 1;
while (count<(T[0].BiData.key - SIZEOFFSET))
{
printf("key = %d \n", T[count].BiData.key);
count++;
}
}
}
// 销毁顺序二叉树
void DestroyBiTree_Seq(BiNode* root) {
if (root == NULL) {
return;
}
free(root); // 销毁当前节点
}
测试代码
int main(void) {
TBiData testdata;
testdata.key = 1;
BiNode* T = InitRootBiTree(testdata);
testdata.key = 2;
T->lchild = InitRootBiTree(testdata);
testdata.key = 3;
T->rchild = InitRootBiTree(testdata);
testdata.key = 7;
T->rchild->rchild = InitRootBiTree(testdata);
if (BITreeNodeExit(T, 8)) {
printf("8 存在\n");
}
else
printf("8 不存在\n");
if (BITreeNodeExit(T, 3)) {
printf("3 存在\n");
}else
printf("3 不存在\n");
if (BITreeNodeExit(T, 1)) {
printf("1 存在\n");
}
else
printf("1 不存在\n");
if (BITreeNodeExit(T, 7)) {
printf("7 存在\n");
}
else
printf("7 不存在\n");
if (BITreeNodeExit(T, 4)) {
printf("4 存在\n");
}
else
printf("4 不存在\n");
//CreateBiTree(T,1, testdata);
testdata.key = 8;
if (CreateBiTree(T, 8, testdata) == 0)
printf("创建8 成功\n");
else
printf("创建8 失败\n");
testdata.key = 4;
if (CreateBiTree(T, 4, testdata) == 0)
printf("创建4 成功\n");
else
printf("创建4 失败\n");
testdata.key = 8;
if (CreateBiTree(T, 8, testdata) == 0)
printf("创建8 成功\n");
else
printf("创建8 失败\n");
//testdata.key = 3;
//CreateBiTree(T, 3, testdata);
//testdata.key = 4;
//CreateBiTree(T, 4, testdata);
DestroyBiTree(T);
BiNode_Seq* Tl = InitBiTree_Seq(11);
for (int i = 1; i < 15; i++)
{
testdata.key = i;
if(CreateBiTree_Seq(Tl, i, testdata)==0)
printf("Seq创建%d 成功\n",i);
else
printf("Seq创建%d 失败\n", i);
}
return 0;
}
2024-8-8新增表达式求值
这里以5+34+8-62为例
由于项目中的原因转换为逆波兰式子 534*+862*-+
构造数组依次按照逆波兰式的顺序构造二叉树,然后通过二叉树表达式求值,并缓存所有中间结果
更新之后代码如下
.h
#ifndef __BITREE__
#define __BITREE__
typedef enum {
exp_in = 1,
exp_lo,
exp_out
}EXPNode;
typedef enum {
sym_add = 1, //+
sym_sub, //-
sym_mul, //*
sym_div, // /
sym_and, // &&
sym_or, // ||
sym_not, // !
sym_gt, // >
sym_lt, // <
sym_eq, // ==
sym_gte, // >=
sym_lte // <=
}EXPsymNode;//符号
typedef struct {
int key;
EXPNode exp_type;
EXPsymNode sym_type;
int Ivalue;
float Fvalue;
}EXPTBiData;
typedef struct {
int key;
EXPTBiData expdata;
}TBiData;
typedef struct BiNode {
TBiData BiData;
struct BiNode* lchild, * rchild; //左右两个指针
}BiNode;
//初始化根节点
BiNode* InitRootBiTree(TBiData data);
//判断完全二叉树的某一节点是否存在,如果存在返回当前节点地址,如果不存在返回空
BiNode* BITreeNodeExit(BiNode* T, int node);
/*
*创建二叉树。以补全为完全二叉树的方法创建
*T 二叉树根节点
* node 要新加的二叉树节点的位置
* data 新建节点的数据域 将会重新复制其中的数据
*/
int CreateBiTree(BiNode* T, int node, TBiData data);
// 后序遍历销毁二叉树
void DestroyBiTree(BiNode* root);
//创建表达式二叉树
BiNode* CreateEXPBiTree(TBiData* datalist);
// 递归函数来计算表达式树的值
float EvaluateTree(BiNode* node);
//=============================================================================
typedef struct BiNode_Seq {
TBiData BiData;
//struct BiNode* lchild, * rchild; //左右两个指针 顺序二叉树不需要左右指针来指示左右子树关系
}BiNode_Seq;
#define SIZEOFFSET 100000
//初始化顺序二叉树
BiNode_Seq* InitBiTree_Seq(int size);
//创建顺序二叉树
int CreateBiTree_Seq(BiNode_Seq* T, int node, TBiData data);
//前序遍历顺序二叉树
void InOrderTraverse_Seq(BiNode* T);
// 销毁顺序二叉树
void DestroyBiTree_Seq(BiNode* root);
#endif // !1
.c
#include "BiTree.h"
#include "stdio.h"
#include "malloc.h"
#include "string.h"
//前序遍历树 递归
void InOrderTraverse(BiNode *T) {
if (T) {
InOrderTraverse(T->lchild);
//对节点数据进行操作
printf("key = %d \n", T->BiData.key);
InOrderTraverse(T->rchild);
}
}
//初始化根节点
BiNode* InitRootBiTree(TBiData data) {
BiNode* T = (BiNode*)(0);
if (!T) {
T = (BiNode*)malloc(sizeof(BiNode));
if(!T) return T;
T->lchild = (BiNode*)(0); //新节点的左右孩子指针置为0
T->rchild = (BiNode*)(0);
//memset(&(T->BiData), 0, sizeof(TBiData));
memcpy(&(T->BiData), &data, sizeof(TBiData));
}
return T;
}
//判断完全二叉树的某一节点是否存在,如果存在返回当前节点地址,如果不存在返回空
BiNode* BITreeNodeExit(BiNode* T ,int node) {
if (!T)
return NULL;
int BiTreeStack[10] = { 0 };//栈
int i = 0;
if (node == 1) return T;
BiNode* mT = NULL;
while (node != 1) {
if (node % 2) {//奇数
node = (node - 1) / 2;
BiTreeStack[i] = 2; //右边
}
else {
node = node / 2;
BiTreeStack[i] = 1; //左边
}
i++;
if (i >= 10) return NULL;
}
i--;
mT = T;
for (i; i >= 0; i--) {//倒叙出栈 得出找到目的节点的顺序
//printf("%d ", BiTreeStack[i]);
if (mT) {
if (BiTreeStack[i] == 2) {//右边
mT = mT->rchild;
}
else if (BiTreeStack[i] == 1) {//左边
mT = mT->lchild;
}
}
else
return NULL;
}
return mT;
}
/*
*创建二叉树。以补全为完全二叉树的方法创建
* T 二叉树根节点 ,如果未分配内存且node为1 则自动分配内存
* node 要新加的二叉树节点的位置
* data 新建节点的数据域 将会重新复制其中的数据
*/
int CreateBiTree(BiNode *T,int node,TBiData data) {
BiNode* mT = NULL;
if (!T)
return -1;
mT = BITreeNodeExit(T, node);
if (mT) {//若节点存在就替换数据
memcpy(&(mT->BiData), &data, sizeof(TBiData));
return 0;
}
else {//结点不存在
mT = NULL;
int fnode = node % 2 ? (node - 1) / 2 : node / 2; //算出父节点
mT = BITreeNodeExit(T, fnode);
if(mT){//若父节点存在 分配内存,塞入数据
BiNode** newnode = NULL;
newnode = node % 2 ? &(mT->rchild) : &(mT->lchild);
*newnode = (BiNode*)malloc(sizeof(BiNode));
if (*newnode == NULL) return -1; //内存分配失败
(*newnode)->lchild = (BiNode*)(0); //新节点的左右孩子指针置为0
(*newnode)->rchild = (BiNode*)(0);
//memset(&((*newnode)->BiData), 0, sizeof(TBiData));
memcpy(&((*newnode)->BiData), &data, sizeof(TBiData));
return 0;
}else
return -1;
}
return -1;
}
// 后序遍历销毁二叉树
void DestroyBiTree(BiNode* root) {
if (root == NULL) {
return;
}
DestroyBiTree(root->lchild); // 销毁左子树
DestroyBiTree(root->rchild); // 销毁右子树
//printf("删除 %d \n", root->BiData.key);
free(root); // 销毁当前节点
//printf("删除 %d \n", root->BiData.key); 这里因为内存已释放 可能出现乱码
}
//创建表达式二叉树
BiNode* CreateEXPBiTree(TBiData* datalist) {
BiNode *DataStack[50] = { 0 };
BiNode *DataStackfree[50] = { 0 }; //释放用
int i = 0;
int top = 0;
while ((*datalist).key != 0) //使用key作为是否有值的依据
{
if (datalist->expdata.exp_type == exp_in) {//如果是输入节点
BiNode* node = InitRootBiTree(*datalist); //根据数据先创建一个节点
DataStackfree[i++] = node;
DataStack[top++] = node; //先入栈
}
else if (datalist->expdata.exp_type == exp_lo) {//如果是运算符
if (top <= 0) return NULL;
top--;
BiNode* right = DataStack[top--];
BiNode* left = DataStack[top];
BiNode* node = InitRootBiTree(*datalist);
DataStackfree[i++] = node;
node->lchild = left;
node->rchild = right;
DataStack[top++] = node;
}
else {//都不是则波兰式有误
while (i--)
free(DataStackfree[i]);
return NULL;
}
datalist++;
}
top--; //最后合并的时候会多加1
if (top != 0) {//如果最后没有消除为1个根节点 那就是波兰式有误
while (i--)
free(DataStackfree[i]);
return NULL;
}
return DataStack[top];
}
// 递归函数来计算表达式树的值
float EvaluateTree(BiNode* node) {
if (node == NULL) return 0; // 空节点返回0
if (node->BiData.expdata.exp_type == exp_in) {
// 如果是操作数(叶子节点),直接返回其值
return node->BiData.expdata.Fvalue;
}
else {
// 如果是操作符,递归计算左右子树的值,并执行相应的运算
float leftVal = EvaluateTree(node->lchild);
float rightVal = EvaluateTree(node->rchild);
float nodedata = 0;//存储运算节点运算出来的值
switch (node->BiData.expdata.sym_type) {
case sym_add:
nodedata = leftVal + rightVal;
break;
case sym_sub:
nodedata = leftVal - rightVal;
break;
case sym_mul:
nodedata = leftVal * rightVal;
break;
case sym_div:
nodedata = leftVal / rightVal;
break;
case sym_and:
nodedata = leftVal && rightVal;
break;
case sym_or:
nodedata = leftVal || rightVal;
break;
case sym_not:
return 0; //不支持非运算
case sym_gt:
nodedata = leftVal > rightVal?1:0;
break;
case sym_lt:
nodedata = leftVal < rightVal ? 1 : 0;
break;
case sym_eq:
nodedata = leftVal == rightVal ? 1 : 0;
break;
case sym_gte:
nodedata = leftVal >= rightVal ? 1 : 0;
break;
case sym_lte:
nodedata = leftVal <= rightVal ? 1 : 0;
break;
default:
return 0;
}
node->BiData.expdata.Fvalue = nodedata;
return node->BiData.expdata.Fvalue;
}
}
//==================================================================================
//初始化顺序二叉树
BiNode_Seq* InitBiTree_Seq(int size) {
BiNode_Seq* T = (BiNode_Seq*)(0);
if (size <= 0)
return T;
//这里我加1的目的是为了 空第一个空间不使用,让下标就等于二叉树节点号,同时在第一个节点的key存储顺序二叉树的长度 偏移SIZEOFFSET
T = (BiNode_Seq*)malloc(sizeof(BiNode_Seq) * (size + 1));
if(!T) return T;
T[0].BiData.key = size + SIZEOFFSET; //第一个节点记录长度
return T;
}
//创建顺序二叉树
int CreateBiTree_Seq(BiNode_Seq* T,int node,TBiData data) {
if (!T)
return -1;
if (node > (T[0].BiData.key - SIZEOFFSET)) //超长了
return -1;
memcpy(&T[node].BiData, &data, sizeof(TBiData));
return 0;
}
//前序遍历顺序二叉树
void InOrderTraverse_Seq(BiNode* T) {
if (T) {
int count = 1;
while (count<(T[0].BiData.key - SIZEOFFSET))
{
printf("key = %d \n", T[count].BiData.key);
count++;
}
}
}
// 销毁顺序二叉树
void DestroyBiTree_Seq(BiNode* root) {
if (root == NULL) {
return;
}
free(root); // 销毁当前节点
}
测试代码
TBiData data123[50] = { 0 };
data123[0].expdata.exp_type = exp_in;
data123[0].expdata.Fvalue = 5;
data123[0].key = 1;
data123[1].expdata.exp_type = exp_in;
data123[1].expdata.Fvalue = 3;
data123[1].key = 1;
data123[2].expdata.exp_type = exp_in;
data123[2].expdata.Fvalue = 4;
data123[2].key = 1;
data123[3].expdata.exp_type = exp_lo;
data123[3].expdata.Fvalue = 0;
data123[3].expdata.sym_type = sym_mul;
data123[3].key = 1;
data123[4].expdata.exp_type = exp_lo;
data123[4].expdata.Fvalue = 0;
data123[4].expdata.sym_type = sym_add;
data123[4].key = 1;
data123[5].expdata.exp_type = exp_in;
data123[5].expdata.Fvalue = 8;
data123[5].key = 1;
data123[6].expdata.exp_type = exp_in;
data123[6].expdata.Fvalue = 6;
data123[6].key = 1;
data123[7].expdata.exp_type = exp_in;
data123[7].expdata.Fvalue = 2;
data123[7].key = 1;
data123[8].expdata.exp_type = exp_lo;
data123[8].expdata.Fvalue = 0;
data123[8].expdata.sym_type = sym_mul;
data123[8].key = 1;
data123[9].expdata.exp_type = exp_lo;
data123[9].expdata.Fvalue = 0;
data123[9].expdata.sym_type = sym_sub;
data123[9].key = 1;
data123[10].expdata.exp_type = exp_lo;
data123[10].expdata.Fvalue = 0;
data123[10].expdata.sym_type = sym_add;
data123[10].key = 1;
BiNode* Tl0= CreateEXPBiTree(data123);
if (!Tl0)
printf("波兰式有误\n");
else
printf("波兰式生成二叉树成功!\n");
printf("波兰式求值为 %f \n",EvaluateTree(Tl0));
实验现象