【数据结构】树

树的基本术语

结点:数据元素及指向子树的分支
根节点:非空树中无前驱结点的节点
结点的度:结点拥有子树的个数
树的度:树内各节点度的最大值
叶子节点:度为零
分支节点:度不为0
内部节点:根节点以外的分支节点

森林:m(m>0)棵互不相交的树的集合。

二叉树

任何树都可以与二叉树相互转换,解决了树的存储结构及其运算存在的复杂性。
【说明】二叉树不是树的特殊概念,他们是两个概念。
二叉树结点的子树要区分左子树和右子树,即使只有一棵子树也要区分。
而树只有一个孩子时,无需区分左右次序。

二叉树的性质

  1. 第i层上至多有2^(i -1 )个节点
  2. 深度为k的二叉树至多有2^k -1 个结点
  3. 叶子数为n0,度为2的节点数为n2,则n0 = n2 + 1

满二叉树

深度为k且有2^k - 1个结点的二叉树
对位置进行编号:自根节点,自上而下,自左而右

完全二叉树

深度为k,具有n个结点的二叉树,当且仅当每一个结点都与深度为k的满二叉树中编号1-n的结点一一对应时,成为完全二叉树。
满二叉树从最后一个结点开始,连续去掉任意个结点,得到的是一课完全二叉树。

n个结点的完全二叉树深度为(log2N)的底 + 1

二叉树的存储结构

顺序存储结构

数组:按满二叉树的结点编号,编号为数组下标存放。
缺点:浪费空间,适合满二叉树和完全二叉树

链式存储结构

二叉链表
typedef struct BiNode{
TElemType data;
struct BiNode *Lchild, *Rchile;  //左右孩子指针。
}BiNode, *BiTree;

在n个结点的二叉链表中,有____个空指针域
分析:必有n个链域。
除根节点外每个节点有且只有一个双亲,所以只有n-1个节点的链域存放指针
所以空指针数目为 2n - (n - 1) = n + 1 个

三叉链表
typedef struct TriTNode{
TElemType data;
struct TriTNode *Lchild, *parent, *Rchile;  //左右孩子双亲指针。
}TriTNode, *TriTree;

二叉树的遍历

DLR——先序遍历
LDR——中序遍历
LRD——后序遍历

已知先序和中序,可以构造出相应二叉树(或者中序和后序)

先序遍历递归算法

Statue PreOrderTraverse(BiTree T){
	if(T == NULL) return OK;
	else{
	visit(T);//访问根节点
	// 输出根节点 printf("%d\t",T -> data);
	PreOrderTraverse(T -> lchild);//递归遍历左子树
	PreOrderTraverse(T -> rchild);//递归遍历右子树
	}
}

从起点到终点,每个节点经过3次。
第一次经过时访问:先序遍历
第二次经过时访问:中序遍历
第三次经过时访问:后序遍历

时间效率:O(n)//每个结点只访问一次
空间效率:O(n)//栈占用的最大辅助空间

中序遍历非递归算法

Status InOrderTraverse(BiTree T){
	BiTree p;
	InitStack(S);
	p = T;
	while( p || !StackEmpty(S) ){   //p不为空或栈不为空
		if(p){//根节点入栈,访问左子树
			push(S,p); p = p->lchild;}
		else{
			pop(S,q);  //弹出栈顶放入q
			printf("%c",q->data);
			p = q -> rchild;}  //p访问右子树
	}
	return OK;
}

二叉树的层次遍历

实现:顺序的循环队列

typedef struct{
	BTNode data[MaxSize];  //存放队中元素
	int front, rear;		//队头和队尾指针
}SqQueue;

void LevelOrder(BTNode *b){
	BTNode *p;
	SqQueue *qu;
	InitQueue(qu);		//初始化队列
	enQueue(qu, b);		//根节点入队
	while(!QueueEmpty(qu)) {  //队列不为空,则循环
		deQueue(qu, p);		//当前队头元素出队
		printf("%c", p->data);
		if(p -> lchild != NULL)		enQueue(qu, p->lchild);
		 							//有左孩子就将左孩子进队
		if(p -> rchild != NULL)		enQueue(qu, p->rchild);
		}							//有右孩子就将右孩子进队
}

二叉树遍历算法的应用

建立二叉树:先序遍历建立二叉树的二叉链表

按顺序读入字符:ABC##DE#G##F###

Status CreateBiTree(BiTree &T){
	scanf(&ch);
	if(ch == '#') T = NULL;
	else{
		if(!(T = (BiTNode*)malloc(sizeof(BiTNode))))
			exit(OVERFLOW);
		T->data = ch;  //生成根节点
		CreateBiTree(T->lchild);  //构造左子树
		CreateBiTree(T->rchild);  //构造右子树
	}
	return OK;
}
复制二叉树:先序遍历复制
int Copy(BiTree T, BiTree &NewT){
	if(T == NULL){
		New T = NULL;
		return 0 ;
	}
	else{
		NewT = new BiTNode;  //分配新空间
		NewT->data = T->data;
		Copy(T->lchild, New->lchild);
		Copy(T->rchild, New->rchild);
		}
}
计算二叉树的深度

空树:深度为0;
有结点:递归计算左子树深度m,递归计算右子树深度n,二叉树深度为m和n较大数加一。

int Depth(BiTree T){
	if(T = NULL)	return 0;
	else{
		m = Depth(T->lchild);
		n = Depth(T->rchild);
		if(m>n)	return (m+1);
		else return (n+1);
	}
}
计算二叉树结点总数

空树:节点数为0;
否则:节点数为左子树节点个数+右子树节点个数+1;

int NodeCount(BiTree T){
	if(T == NULL)	return 0;
	else
	return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
}
计算二叉树叶子节点数

空树:叶子节点为0;
否则:节点数= 左子树叶子节点数+右子树叶子节点个数

int LeadCount(BiTree T){
	if(T == NULL)	return 0; //空树返回0;
	if(T->lchild == NULL && T->rchild == NULL)	return 1;
	//叶子结点返回1
	else  //不是叶子节点
		return LeafCount(T->lchild) + LeafCount(T->rchild);
}

线索二叉树

用二叉链表作为二叉树的存储结构时,找某个结点的左右孩子很方便。
但是无法直接找到该结点在某种遍历序列的前驱和后继结点。

利用二叉链表的空指针域:
若某节点的左孩子为空,将空的左孩子的指针域改为指向其前驱
若某节点的右孩子为空,将空的右孩子指针域改为指向其后继

对二叉树按某种遍历次序使其变成线索二叉树的过程叫线索化。

为区分lchild和rchild指针到底是指向孩子的指针,还是指向前驱或后继的指针,对二叉链表中每个结点增设两个标志域ltagrtag
ltag= 0 lchild指向该节点的左孩子
ltag = 1 lchild指向该节点的前驱
rtag = 0 rchild指向该节点的右孩子
rtag= 1 rchild指向该节点的后继

typedef struct BiThrNode{
	int data;
	int ltag, rtag;
	struct BiThrNode *lchild, rchild;
}BiThrNode, *BiThrTree;

增设一个头节点:
ltag = 0,lchild指向根节点;
rtag = 1,rchild指向遍历序列中最后一个结点
遍历序列中第一个结点的lchild和最后一个结点的rchild都指向头节点。

树和森林

森林:m(m>=0)棵互不相交的树

树的存储表示

双亲表示法(找双亲容易,找孩子难)

实现:结构数组存放树的结点,每个结点包含两个域。
			数据域:存放结点本身信息;
			双亲域:本节点的双亲结点在数组中的位置。(根的值为-1)
 typedef struct PTNode{
 	TElemType data;
 	int parent;
 }PTNode;
 //定义一棵树
#define MAX_TREE_SIZE 100
typedef struct{
	PTNode nodes[MAX_TREE_SIZE];
	int r, n; //根节点位置和节点个数
}PTree;

孩子链表(找孩子容易,找双亲难)

实现:把每个结点的孩子结点排列起来,用单链表存储。(叶子节点的孩子链表为空表)。
n个头指针组成一个线性表,用顺序表(含n个元素的结构数组)存储。
孩子节点结构:

typedef struct CTNode{
	int child;
	struct CTNode *next;
}*ChildPtr;

双亲结点结构:

typedef struct{
	TELemType data;
	ChildPtr firstchild;
}CTBox;

树结构:

typedef struct{
	CTBox nodes[MAX_TREE_SIZE];
	int n, r;  //结点数和根节点的位置
}CTree;

带双亲的孩子链表:
在树结构再增加一个元素,存储双亲的下标

孩子兄弟表示法(二叉树表示法)

实现:用二叉链表作为树的存储结构,链表中每个节点的两个指针域分别指向第一个孩子节点下一个兄弟结点

typedef struct CSNode{
	ElemType data;
	struct CSNode *firstchild,*nextsibling;
}CSNode, *CSTree;

树与二叉树的转换

二叉链表作媒介,将树转化为二叉树操作,操作完成后,再将二叉树转换为树。
树变二叉树:兄弟相连留长子
二叉树变树:左孩右右连双亲,去掉原来右孩线

森林与二叉树的转换

每棵树分别转换为二叉树,第一棵树的根节点为二叉树的根。
森林变二叉树: 树变二叉根相连
二叉树变森林:去掉全部右孩线,孤立二叉再还原。

树与森林的遍历

1.树的遍历(三种方法)
先序遍历
后序遍历
层次遍历:从上到下,从左到右访问树的每个节点
2.森林的遍历
把森林看成三部分:

  1. 森林中第一棵树的根节点;
  2. 森林第一棵树的子树森林;
  3. 森林中其他树构成的森林。
    先序遍历:123(对所有的树依次进行先序遍历)
    中序遍历
【论文复现】一种基于价格弹性矩阵的居民峰谷分时电价激励策略【需求响应】(Matlab代码实现)内容概要:本文介绍了一种基于价格弹性矩阵的居民峰谷分时电价激励策略,旨在通过需求响应机制优化电力系统的负荷分布。该研究利用Matlab进行代码实现,构建了居民用电行为与电价变动之间的价格弹性模型,通过分析不同时间段电价调整对用户用电习惯的影响,设计合理的峰谷电价方案,引导用户错峰用电,从而实现电网负荷的削峰填谷,提升电力系统运行效率与稳定性。文中详细阐述了价格弹性矩阵的构建方法、优化目标函数的设计以及求解算法的实现过程,并通过仿真验证了所提策略的有效性。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事需求响应、电价机制研究或智能电网优化等相关领域的科研人员及研究生。; 使用场景及目标:①研究居民用电行为对电价变化的响应特性;②设计并仿真基于价格弹性矩阵的峰谷分时电价激励策略;③实现需求响应下的电力负荷优化调度;④为电力公司制定科学合理的电价政策提供理论支持和技术工具。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解价格弹性建模与优化求解过程,同时可参考文中方法拓展至其他需求响应场景,如工业用户、商业楼宇等,进一步提升研究的广度与深度。
针对TC275微控制器平台,基于AUTOSAR标准的引导加载程序实现方案 本方案详细阐述了一种专为英飞凌TC275系列微控制器设计的引导加载系统。该系统严格遵循汽车开放系统架构(AUTOSAR)规范进行开发,旨在实现可靠的应用程序刷写与启动管理功能。 核心设计严格遵循AUTOSAR分层软件架构。基础软件模块(BSW)的配置与管理完全符合标准要求,确保了与不同AUTOSAR兼容工具链及软件组件的无缝集成。引导加载程序本身作为独立的软件实体,实现了与上层应用软件的完全解耦,其功能涵盖启动阶段的硬件初始化、完整性校验、程序跳转逻辑以及通过指定通信接口(如CAN或以太网)接收和验证新软件数据包。 在具体实现层面,工程代码重点处理了TC275芯片特有的多核架构与内存映射机制。代码包含了对所有必要外设驱动(如Flash存储器驱动、通信控制器驱动)的初始化与抽象层封装,并设计了严谨的故障安全机制与回滚策略,以确保在软件更新过程中出现意外中断时,系统能够恢复到已知的稳定状态。整个引导流程的设计充分考虑了时序确定性、资源占用优化以及功能安全相关需求,为汽车电子控制单元的固件维护与升级提供了符合行业标准的底层支持。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值