【数据结构】5.7 哈夫曼树及其应用

news2024/11/19 16:21:18

文章目录

  • 前言
  • 5.7.1 哈夫曼树的基本概念
    • 哈夫曼树的特点
  • 5.7.2 哈夫曼树的构造算法
    • 哈夫曼树的构造过程
    • 哈夫曼算法的实现
      • 算法思路
      • 算法实现
  • 5.7.3 哈夫曼编码
    • 哈夫曼编码思想
      • 前缀编码
      • 哈夫曼编码
      • 哈夫曼编码的性质
    • 哈夫曼编码的算法实现
    • 文件的编码和解码

前言

编程:将学生的百分之成绩转换成五分制(ABCDE)成绩

  • 如:<60:E,60-69:D,70-79:C,80-89:B,90-100:A

在这里插入图片描述

可以将这样一个判断的过程画成一棵二叉树,每次判断的结果都有两个分支。这样一棵树就称为判断树

判断树:*用于描述分类过程的二叉树。

假设出现这样一种情况

  • 小于 60 分的同学只有 5%,大于 90 的同学只有 10%。
  • 最多的同学是在 60 -90 之间。

在这里插入图片描述

如果每次的输入量很大,则应该考虑程序的操作时间。
若学生的成绩数据有 10000 个:

  • 60-70 分的同学就需要比较两次(先判断是否小于60再判断是否小于70)
  • 同理:70-80 的同学需要比较三次,80-90 要4次,90分以上也是4次。
  • 每个分数线的人数百分比数据总量比较次数,这样的操作量就很大了。
    • 如:处于 60-70 的同学的成绩数据操作数为 15% * 10000 * 2,要比较这么多次,其余同理。

在这里插入图片描述

怎样才能让操作次数变少?

在这里插入图片描述

如果将判断树设计成这样

  • 先判断成绩是否小于 80 ,小于走左子树,大于则走右子树(类似于二分法)。

    • 小于 60 分和 60-70 分的数据需要比较三次。
    • 70-80 、 80-90 以及大于 90 的这三种情况都只需要比较两次。
  • 将值判断三次的数据占比相加,将只判断两次的数据占比相加

  • 这种情况总共需要比较:10000(3 * 20% +2 * 80%) = 22000次

问题:怎样才能找到一种效率最高的判断树呢?这时候就轮到哈夫曼树(最优二叉树)来干活了。

5.7.1 哈夫曼树的基本概念

哈夫曼树又称为最优树,作用是找到一种效率最高的判断树

  1. 路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
  2. 结点的路径长度:两个结点之间路径上的分支数
    • 如图 a :从 A-D 的路径长度就是是2。从 A 到 B C D E F G F I 的路径长度分别为 1 1 2 2 3 3 4 4
    • 如图 b:从 A 到 B C D E F G H I 的路径长度分别为 1 1 2 2 2 2 3 3。

在这里插入图片描述

  1. 树的路径长度:从树根到每一个结点的路径长度之和称为树的路径长度。记作:TL(根节点到它自身的结点路径长度为0)
    • 结点数目相同的二叉树中,完全二叉树路径长度最短的二叉树,但是路径长度最短的不一定是完全二叉树。

在这里插入图片描述

  1. 权(weight):将树中结点赋个一个有着某种含义的数值,则称这个数值为该结点的权
    • 如前言中的那两棵树的每个结点的百分数,权不固定,取什么都可以。
  2. 结点的带权路径长度:从结点到该结点之间的路径长度与该结点的乘积
    • 如前言中的树中的 小于60分的人数占比 X 到该分支的判断次数( 5% X 3,)5%即使权,3就是路径长度。
  3. 树的带权路径长度:树中所有叶子结点的带权路径之和

在这里插入图片描述

  • 例:有 4 个结点 a b c d,权值分别为 7 5 2 4,构造以此 4 个结点为叶子结点的二叉树。

在这里插入图片描述
在这里插入图片描述

  1. 哈夫曼树
  • 最优树:树的带权路径长度(WPL)最短的
    • 带权路径长度最短是在树的度相同的树中比较而得到的结果,因此有最优二叉树,最优三叉树之称等等。
  • 最优二叉树:树的带权路径长度(WPL)最短的二叉树

哈夫曼树的特点

  • 权值越大的结点,离根节点越近,权值越小的结点,离根节点越远,能使总路径越短

在这里插入图片描述

  • 满二叉树不一定是哈夫曼树。
  • 具有相同带权节点的哈夫曼树不唯一,如上图的 3树和4树,都是哈夫曼树但并不是同一棵树。

5.7.2 哈夫曼树的构造算法

在前面知道了哈夫曼树中权越大的叶子离根越近,那么现在我们就要用这样的一个权值来构造哈夫曼树。

贪心算法

  • 构造哈夫曼树时首选选择权值小的叶子结点。
  • 这样就可以将权值大的叶子放到后面构造,大的留在最后就可以离根近一些。

哈夫曼树的构造过程

  1. 根据 n 个给定的权值 {W₁,W₂,W₃…,Wn} 构成 n 棵二叉树的森林 F = {T₁,T₂,T₃…,Tn},其中 Ti 只有一个带权威 Wi 的根节点。

    • 构造的森林全是根。这个森林中,给了几个权值就有几棵树。
  2. 在森林 F 中选取两棵根节点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根节点的权值为其左右子树上根结点的权值之和。

    • 选用两小造新树
  3. 在森林 F 中删除这两棵树,同时将新得到的二叉树加入森林中。

    • 删除两小添新人
  4. 重复上面的 步骤2 和 步骤3,直到森林中只有一棵树为止,这棵树即为哈夫曼树。

    • 重复 2、3 剩单根

举个栗子

有 4 个结点分别是 a b c d 权值分别为 7 5 2 4,试构造哈夫曼树。

  1. 构造的森林全是根。

在这里插入图片描述

  1. 选用两小造新树。

在这里插入图片描述

  1. 删除两小添新人。

在这里插入图片描述

  1. 重复 2、3 剩单根

在这里插入图片描述

因为构造哈夫曼树的时候是,选用两小造新树。

  • 所以:哈夫曼树的结点的度为 0 或 2,没有度为 1 的结点
  • 包含 n 个叶子结点的哈夫曼树中共有 2n -1 个结点。

在这里插入图片描述

总结

  1. 在哈夫曼算法中,初始时有 n 棵二叉树,要经过 n-1 次合并最终成为哈夫曼树。
  2. 经过 n-1 次合并产生 n-1个新结点,且这 n-1 个新结点都是具有两个孩子的分支结点
  3. 可见:哈夫曼树中共有 n+n-1 = 2n-1 个结点,且其所有的分支结点的度均不为1.

哈夫曼算法的实现

  • 采用顺序存储结构—— 一维结构数组
  • 结点类型定义
typedef struct
{
		int weight;//结点的权重
		int parent;//结点的双亲
		int lchild,rchild;//结点的左、右孩子下标

	}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
  • 哈夫曼树中共有 2n-1 个结点,不使用 0 下标,数组大小为 2n。
    • 例如:第一个结点的权值为 5,即可表示为 H[i].weight = 5

在这里插入图片描述

算法思路

  • 有 8 个权值为 W ={7,19,2,6,32,3,21,10} 的叶子结点,构造哈夫曼树。

  • 这棵哈夫曼树则有 2n-1=2*8-1=15 个结点。

  • 那么现在就应该构造有 2n = 2*8= 16 个元素的数组。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

算法实现

  1. 初始化 HT [1…2n-1]:lchild = rchild = parent = 0;
  2. 输出初始 n 个叶子结点:置为 HT[1…n] 的weight值
  3. 进行以下 n-1 此合并,依次产生 n-1 个结点 HT[i],i = n+1…2n-1:
    • 在HT[1…i-1] 中选两个未被选过(从parent == 0 的结点中选)的 weight 最小的两个结点 HT[s1]HT[s2],s1、s2为两个最小结点下标
    • 修改 HT[s1] 和 HT[s2] 的 parent 值:HT[s1].parent = iHT[s2].parent = i
    • 修改新产生的 HT[i]:
      • HT[i].weight = HT[s1].weight + HT[s2].weight;
      • HT[i],lchild = s1;HT[i].rchild = s2
//哈夫曼树的存储表示
typedef struct
{
		int weight;//结点的权重
		int parent;//结点的双亲
		int lchild,rchild;//结点的左、右孩子下标

}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树

//构造哈夫曼树HT
void CreateHuffmanTree(HuffmanTree &HT,int n)
{
		if(n<=1)
		{
				return 0;
		}
		
		int i;
		int m = 2*n-1
		
		HT = new HTNode[m+1];//0号下标未使用,所以需要动态分配m+1(2n)个单元,HT[m]表示根节点
		
		for(i = 1;i<=m;i++)//将1-m个元素的双亲、左右孩子的下标都初始化为0
		{
				H[i].parent = 0;
				H[i].lch = 0;
				H[i].rch = 0;
		}
		for(i = 1;i <= n;i++)//输入前n的元素中,叶子结点的权值
		{
				cin >> HT[i].weight;
		}
//-------------------------初始化结束,下面开始创建哈夫曼树-------------------------//
		//通过n-1次的选择、删除、合并来创建哈夫曼树
		for(i=n+1;i<=m;i++)//从9开始到15结束
		{
				select(HT,i-1,s1,s2);//select函数是自己写的
				//在HT[k](1<=k<=i-1)中选择两个其双亲域为0,且权值最小的结点,并返回他们在 HT 中的序号 s1 和 s2 
				
				HT[s1].parent = i;
				HT[s2].parent = i;
				//得到新结点i,从森林中删除 s1,s2,将 s1 和 s2 的双亲域由0改1

				HT[i].lchild = s1;
				HT[i].rchild = s2;
				//将s1,s2分别作为 i 的左右孩子

				HT[i].weight = HT[si].weight + HT[s2].weight;
				//i的权值为左右孩子的权值之和
		}
}		

5.7.3 哈夫曼编码

哈夫曼编码思想

前缀编码

如果在一个编码方案中,任一字符的编码都不是另一个字符的编码的前缀,这种编码称作前缀编码

举个栗子

  • 在远程通讯汇总,要将待传字符转换成由二进制的字符换

  • 设要传送的字符为:ABACCDA,现要将这7个字符转换成由01构成的二进制编码。每个字符都用1个两位的二进制编码表示。

    • 若编码为:A - 00,B - 01,C - 10,D - 11
    • 那么这一串字符就可以转换成 A B A C C D A ——> 00 01 00 10 10 11 00
  • 这种编码的方式就称为定长编码,定长编码的缺点就是比较浪费空间,这串字符只有4个不同的字母,但是每个字母都需要占用两个 bit 位。

  • 若将斑马设计为长度不等的二进制编码,即让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。

在这里插入图片描述

前缀编码的缺点

这个时候我们又发现了一个问题,这串二进制码中有重码,ABA转换成的0使得前面出现了4个0。这样的编码就出现前缀

在将二进制翻译成字符的时候就会出现多种情况了。

在这里插入图片描述

所以在设计长度不等的编码时,一定不能有重码

关键:要设计长度不等的编码时,则必须使任一字符的编码都不是另一个字符的编码的前缀,这种编码称作前缀编码

哈夫曼编码

对一棵具有 n 个叶子的哈夫曼树,若对树中的每个左分支赋予 0右分支赋予 1 ,则从根到每个叶子的路径上,各个分支的赋值分别构成一个二进制串,该二进制串就称为哈夫曼编码

问题:什么样的前缀码能使的电文总长最短?

  1. 统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)。
  2. 利用哈夫曼树的特点:权越大的叶子离根越近将每个字符的概率作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
  3. 在哈夫曼树的每个分支表上 0 或 1:
    • 结点的左分支标 0,右分支标 1
    • 把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码

举个栗子

【例】要传输的字符集 D = {C,A,S,T,; }(注意这个分号也是,并没有多打),每个字符出现的频率 W = {2,4,2,3,3}

  1. 以每个字符出现的频率作为权值来构造哈夫曼树:构造的森林全是根,选用两小造新树,删除两小添新人,重复 2 、3 剩单根

在这里插入图片描述

  1. 将这棵哈夫曼树中所有的左分支标 0,右分支标 1

在这里插入图片描述

  1. 从根节点出发,到每一个叶子结点,将路径上经过的 01 标号连起来。
    • 如:从根到 T 字符,经过了 00 分支,所以 T 的编码就是 00。

在这里插入图片描述
在这里插入图片描述

哈夫曼编码的两个问题

  1. 为什么哈夫曼编码能够保证是前缀编码?
  • 因为没有一片叶子是另一片叶子的祖先,所以每个叶子结点的编码就不可能是其他叶子结点编码的前缀。
  1. 为什么哈夫曼编码能够保证字符编码总长最短?
  • 因为哈夫曼树就是树的带权路径长度最短的树,所以整个字符编码的总长最短。

哈夫曼编码的性质

  1. 性质1:哈夫曼编码是前缀编码(任一字符的编码都不是另一字符的编码的前缀)。
  2. 性质2:哈夫曼编码是最优前缀码

小试牛刀

假设组成电文的字符集 D 及其概率分布 W 为:

  • D = {A,B,C,D,E,F,G}
  • W = {0.40,0.30,0.15,0.05,0.04,0.03,0.03}

设计哈夫曼编码

  • 先构造一棵哈夫曼树。

在这里插入图片描述

  • 给所有的左分支标记为 0,给所有的右分支标记为 1。

在这里插入图片描述

  • 从根节点到每个叶子结点所经过的标号,就是对应字符的编码。

在这里插入图片描述

哈夫曼编码的算法实现

  • 由于从根节点到叶子结点构造哈夫曼编码用算法来实现比较困难,所以在构造哈夫曼树后,求哈夫曼编码的主要思想是:
    • 依次以叶子结点为起点,向上回溯至根节点为止。回溯时如果走左分支则生成代码 0 ,走右分支则生成代码 1

假设要找到 G 结点的哈夫曼编码

在这里插入图片描述

  1. 查到 G 在数组当中的位置是 7,取出 7 号结点的 parent 域的值,发现是 8 意思就是 8 号结点是它的双亲。
  2. 在 8 号结点的左孩子域找到了 7,说明 7号结点是 8号的左孩子,是左孩子那么就标注为 0 。
  3. 依次类推直到找到 parent 域为0的结点,就说明G沿着路线走到根节点了。
  • 但是哈弗曼编码是从根到叶子走过的编码,所以要将上述步骤得到的编码翻过来,得到哈夫曼编码
  • 有 n 个字符上面的步骤就要重复 n 次

每一个字符都是由若干个 01 组成的哈夫曼编码,所以n 个字符就需要准备 n 个字符串,

由于每个哈夫曼编码是变长编码,因此使用一个指针数组来存放每个字符编码串的首地址。

在这里插入图片描述

用一个字符数组来装下 01 编码构成的字符串

在这里插入图片描述

  • 有 n 个字符的话,由这 n 个字符所构成的哈夫曼树最高有 n - 1层,因为哈夫曼树一开始坑定是选用两小造新树。
  • 现在有 7 个字符,所以由 01 构成的字符串最长也就是 n -1 = 6,然后数组要多一个元素的位置来存放 字符串最后的 \0。

算法步骤

  1. 分配存储 n 个字符编码的编码表空间 HC,长度为 n + 1;分配临时存储每个字符编码的动态数组空间 cd,cd[n-1]置为 ‘\0’。

  2. 逐个求解 n 个子字符的编码,循环 n 次,执行以下操作:

    • 设置变量 start 用于几轮编码在 ca 中存放的位置,start 初始时指向最后一个元素的位置,即 编码结束符 ‘\0’ 的位置。
    • 设置变量 c 用于记录从叶子结点向上回溯至根节点所讲过的结点下标, c 初始时为当前待编码字符的下标 i,f 用于记录 i 的双亲结点的下标;
    • 从叶子结点向上回溯至根结点,求得字符 i 的编码,当 f 没有到达根节点时,循环执行以下操作:
      • 回溯一次 start 向前指一个位置,即 --start;
      • 若结点 c 是 f 的左孩子,则生成代码 0,反之生成代码 1,生成的代码 0 或 1保存在 ca[start] 中
      • 继续向上回溯,改变 c 和 f 的值。
    • 根据数组 cd 的字符串长度为第 i 个字符编码分配空间 HC[i],然后将数组 cd 中的编码复制到 HC[i] 中。
  3. 释放临时空间 cd 。

算法描述

//从叶子到根逆向求每个字符的哈夫曼编码,并且存储在编码表 HC 中
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
		HC - new char*[n+1];//分配存储 n 个字符编码的编码表空间
		cd = new char[n];//分配临时存放每个字符编码的动态数组空间
		cd[n-1] = '\0';//编码结束符(字符串结束符)

		for(i=1;i<=n;i++)//逐个字符求哈夫曼编码
		{
				start = n-1;//start 一开始指向数组最后的位置,即存放 '\0' 的位置
				c = i;
				f = HT[i].parent;//让 f 指向 c 的双亲结点

				while(f != 0)//f没有走到根节点时执行循环
				{
						start--;//回溯一次使 start 向前指一个位置
						if(c == HT[f].lchild)
						{
								cd[start] = '0';//如果结点 c 是结点 f 的左孩子,则生成代码 0
						}
						else
						{
								cd[start] = '1';//如果结点 c 是结点 f 的右孩子,则生成代码 1
						}
						c = f;f = HT[f].parent;//继续向上回溯
				}//求出第 i 个字符的编码

				HC[i] = new char[n-start];//为第 i 个字符的编码分配空间
				strcpy(HC[i],&cd[start]);//将求得的字符编码从空间 cd 当中复制到 HC 的当前行中
		}
		delete cd;//释放临时空间

}//CreatHuffmanCode

文件的编码和解码

  • 等长编码

如果使用的是之前的等长编码对这段明文进行编译的话,就需要占用 3024bit 的空间。

在这里插入图片描述

  • 哈夫曼编码

而用哈夫曼编码则能够节省将近一半的空间。

在这里插入图片描述

能通过编码将字符转换成一堆二进制码,同样的也能通过解码将一堆肉眼看着乱七八糟的二进制码转换成给人看的明文。

编码

有了字符集的哈夫曼编码表之后,对数据文件的编码过程是:依次读入文件的字符 c ,在哈夫曼编码表 HC 中找到此字符,将此字符转换变编码表中存放的编码串。

  1. 输入各字符及其权值。
  2. 构造哈夫曼树 —— HT[i]。
  3. 进行哈夫曼编码 —— HC[i]。
  4. 查 HC[i],得到各字符的哈夫曼编码。

解码

对编码后的文件进行译码的过程必须借助于哈夫曼树。具体过程是:
依次读入文件的二进制码,从哈夫曼树的根节点(HT[m])出发,若当前读入 0,则走向左孩子,反之走向右孩子。
一旦到达某一个叶子结点 HT[i] 时便译出相应的字符编码 HC[i] 。然后重新从根出发继续译码,直到文件结束。

  1. 构造哈夫曼树。
  2. 依次读入二进制码。
  3. 读入 0,则走向左孩子;读入 1,则走向右孩子。
  4. 一旦到达某个叶子结点时,即可译出对应字符。
  5. 然后再从根出发继续译码,指导结束。

举个栗子

现有如下编码,将它翻译成人话。

在这里插入图片描述

  • 接收字符频度表 W:

    • ((u,5),(v,6),(w,2),(x,9),(y,7))
  • 构造哈夫曼树 HT。

在这里插入图片描述

  • 从根节点出发,到达每个叶子结点,求出每个字符的哈夫曼编码:u - 110,v - 00,w - 111,x - 10,y - 01。
  • 通过对应字符编码翻译成明文。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/163303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【精品】k8s(Kubernetes)由基础到实战学法指南

轻松快速学会k8s四招 图1 k8s四招 学完本篇,您会获得什么惊喜? 从初学k8s,到帮助别人学会的过程中,发现朋友们和我,并非不努力,而是没有掌握更好的方法。有方法可让我们学的更快更轻松,这篇文章,以一个networkpolicy的题目,来逐步讲解,帮助大家建立一种,自己可以根…

深入了解延迟队列 DelayQueue

1. 前言 前面我们了解了基于数组&#xff0c;链表实现的阻塞队列&#xff0c;以及优先级队列。今天我们来了解下基于优先级队列的延迟队列&#xff0c;而且今天的内容很核心哦。 大家快搬好小板凳做好&#xff0c;听我慢慢分析 2. 简单实例 Task 类 public class Task implem…

数据结构(字符串)

字符串简称串&#xff0c;由零个或多个字符组成的有限序列&#xff0c;一般记为s&#xff1d;“a0 a1a2…an-1”,&#xff08;n≥0&#xff09;。其中s称作串名&#xff0c;用双引号括起来的字符序列是串的值。字符ai&#xff08;0≤i≤n-1&#xff09;可以是字母、数字或其它字…

开发第三天(Day 03)

首先对ipl.nas进行修改&#xff1a; ; haribote-ipl ; TAB4ORG 0x7c00 ; 这个程序被读入哪里; 以下是标准FAT12格式软盘的描述JMP entryDB 0x90DB "HARIBOTE" ; 可以自由地写引导扇区的名字 (8字节)DW 512 ; 1扇区…

【动态内存管理】-关于动态内存你只知道四个函数是不够的,这里还有题目教你怎么正确使用函数,还不进来看看??

&#x1f387;作者&#xff1a;小树苗渴望变成参天大树 &#x1f4a6;作者宣言&#xff1a;认真写好每一篇博客 &#x1f4a2; 作者gitee&#xff1a;link 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; &#x1f38a;动态内存管理&…

adrp 命令为何能获取符号的地址

疑问所在 在linux 启动时&#xff0c;在如下位置会将bootloader 传入的x0 - x3 参数保存到boot_args[] 变量中。代码如下&#xff1a; /* …

神经网络基础部件-损失函数详解

一&#xff0c;损失函数概述 大多数深度学习算法都会涉及某种形式的优化&#xff0c;所谓优化指的是改变 xxx 以最小化或最大化某个函数 f(x)f(x)f(x) 的任务&#xff0c;我们通常以最小化 f(x)f(x)f(x) 指代大多数最优化问题。 在机器学习中&#xff0c;损失函数是代价函数的…

Spring Security 多过滤链的使用

一、背景 在我们实际的开发过程中&#xff0c;有些时候可能存在这么一些情况&#xff0c;某些api 比如&#xff1a; /api/** 这些是给App端使用的&#xff0c;数据的返回都是以JSON的格式返回&#xff0c;且这些API的认证方式都是使用的TOKEN进行认证。而除了 /api/** 这些API…

Qt扫盲-QTextEdit理论总结

QTextEdit理论总结一、概述二、用途一&#xff1a;富文本阅读器1. 用法2. 快捷键绑定三、用途二&#xff1a;编辑器1. 用法2. 拖拽3. 快捷键绑定四、常用功能五、信号一、概述 QTextEdit是一个先进的所见即所得的 富文本 查看器/编辑器&#xff0c;支持使用 html 风格的标签或…

【QT】.pro 文件

&#x1f33f;作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;QT开发 .pro 文件是什么 .pro就是工程文件(project)&#xff0c;它是qmake自动生成的用于生产makefile的配置文件。类似于VS中的.sln 和vsproj文件 默认情况下&#xff0c;每个 Qt 项目都包含一个后缀名为…

Huffman编码实现文件的压缩和解压缩

一个项目&#xff0c;不过处理起来也比较麻烦&#xff0c;配套可以和文件传输放一起 前提知识&#xff1a; 哈夫曼树和哈夫曼编码的概念和构建 1&#xff1a;n个数构成的哈夫曼树一共有2*n-1个结点>8 -> 15 2&#xff1a;数字越大的数离根节点越近&#xff0c;越小的数离…

无线耳机跑步会不会掉、最适合跑步用的耳机排名

现在&#xff0c;喜欢运动的人越来越多了。大家都有体会&#xff0c;多数运动是相对枯燥的&#xff0c;在运动时听听音乐&#xff0c;那是多么惬意的事情啊。为此&#xff0c;体验过多款耳机&#xff0c;但令我很满意的甚少。相信不少喜欢运动的朋友都有着跟我一样的烦恼吧&…

【Java基础知识3】Java注释:单行、多行、文档注释(如何通过 javadoc 命令生成代码文档、如何在IEDA配置自动为所有的类都添加创建者和创建日期)

本文已收录专栏 &#x1f332;《Java进阶之路》&#x1f332; 目录 本文已收录专栏 &#x1f332;《Java进阶之路》&#x1f332; &#x1f350;01、单行注释 &#x1f350;02、多行注释 &#x1f350;03、文档注释 &#x1f350;04、文档注释的注意事项 &#x1f350;05、注释…

ceres学习笔记(二)

继续关于ceres官方doc里教程的学习&#xff0c;对于powells function的学习。 一、powells function 鲍威尔法&#xff0c;严格来说是鲍威尔共轭方向法&#xff0c;是迈克尔J.D.鲍威尔提出的一种求解函数局部最小值的算法。该函数不能是可微分的&#xff0c;并且不会导出衍生函…

spring用注解读取与获取对象

前言 上一篇博客简单的介绍了spring的功能与使用&#xff0c;可以看到我们创建一个对象&#xff0c;就需要在xml中存储一个bean对象&#xff0c;这种操作非常的繁琐&#xff0c;因此spring发明了使用注解来快捷存储bean对象 配置工作 我们在xml文件中写下面的代码片段 <…

基于风光储能和需求响应的微电网日前经济调度(Python代码实现)【0】

目录 0 引言 1 计及风光储能和需求响应的微电网日前经济调度模型 1.1风光储能需求响应都不参与的模型 1.2风光参与的模型 1.3风光和储能参与模型 1.4 风光和需求响应参与模型 1.5 风光储能和需求响应都参与模型 2 需求侧响应评价 2.1 负载率 2.2 可再生能源消纳率 …

Win10PE_V2.0Nvme网络版.iso 支持Nvme硬盘免费下载无需积分

Win10PE_V2.0Nvme网络版.iso 支持Nvme硬盘免费下载无需积分 V1.0版本发布 2022年1月19日 内置常用PE工具&#xff0c;7-Zip、EasyImageX_x64、XorBoot Uefi修复、NT6修复、Ghost、CGI、Google浏览器、PENetwork、RegWorkshop、迅雷迷你版、、BOOTICEx64、windows安装器、XP安…

路径计数2

路径计数2 题目描述 一个NNN \times NNN的网格&#xff0c;你一开始在(1,1)(1,1)(1,1)&#xff0c;即左上角。每次只能移动到下方相邻的格子或者右方相邻的格子&#xff0c;问到达(N,N)(N,N)(N,N)&#xff0c;即右下角有多少种方法。 但是这个问题太简单了&#xff0c;所以现…

MySQL 数据同步 Elasticsearch 的技术方案选型

文章目录1.同步双写2.异步双写3.定时任务4.数据订阅1.同步双写 优点&#xff1a;实现简单缺点&#xff1a; 业务耦合&#xff0c;商品的管理中耦合大量数据同步代码 影响性能&#xff0c;写入两个存储&#xff0c;响应时间变长 不便扩展&#xff1a;搜索可能有一些个性化需求&…

jvm学习的核心(三)---运行时数据区详解(1)

图片等相关信息来源于&#xff1a;尚硅谷宋红康JVM全套教程 1.程序计数器 程序计数器又叫pc寄存器&#xff0c;中文有两个名字 我们可以反编译字节码文件查看方法中操作指令对应的指令地址 javap -v "对应的class文件"为什么要用pc寄存器&#xff0c;pc寄存器有什…