【学习记录】二叉树的整理和实现+表达式求值

写在前面

最近工作中遇到了一个问题,某个进程占用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));

实验现象
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值