拿捏-哈夫曼树构建及编码生成(建议收藏)

news2024/11/28 12:42:36

文章目录

  • 哈夫曼树的基本概念
  • 哈夫曼树的构建
    • 构建思路
    • 代码实现
  • 哈夫曼编码的生成
    • 编码生成思路
    • 代码实现
  • 完整代码展示及代码测试

哈夫曼树的基本概念

在认识哈夫曼树之前,你必须知道以下几个基本术语:

1、什么是路径?

在一棵树中,从一个结点往下可以达到的结点之间的通路,称为路径。

在这里插入图片描述
如图,从根结点A到叶子结点I的路径就是A->C->F->I

2、什么是路径长度?

某一路径所经过的“边”的数量,称为该路径的路径长度

在这里插入图片描述

如图,该路径经过了3条边,因此该路径的路径长度为3

3、什么是结点的带权路径长度?

若将树中结点赋给一个带有某种含义的数值,则该数值称为该结点的权。从根结点到该结点之间的路径长度与该结点的权的乘积,称为该结点的带权路径长度。

在这里插入图片描述
如图,叶子结点I的带权路径长度为 3 × 3 = 9

4、什么是树的带权路径长度?

树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。

在这里插入图片描述

如图,该二叉树的带权路径长度 WPL= 2 × 2 + 2 × 6 + 3 × 1 + 3 × 3 + 2 × 2 = 32

5、什么是哈夫曼树?

给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,则称该二叉树为哈夫曼树,也被称为最优二叉树。

根据树的带权路径长度的计算规则,我们不难理解:树的带权路径长度与其叶子结点的分布有关。
即便是两棵结构相同的二叉树,也会因为其叶子结点的分布不同,而导致两棵二叉树的带权路径长度不同。

在这里插入图片描述

那如何才能使一棵二叉树的带权路径长度达到最小呢?
 根据树的带权路径长度的计算规则,我们应该尽可能地让权值大的叶子结点靠近根结点,让权值小的叶子结点远离根结点,这样便能使得这棵二叉树的带权路径长度达到最小。

哈夫曼树的构建

构建思路

下面给出一个非常简洁易操作的算法,来构造一棵哈夫曼树:
 1、初始状态下共有n个结点,结点的权值分别是给定的n个数,将他们视作n棵只有根结点的树。
 2、合并其中根结点权值最小的两棵树,生成这两棵树的父结点,权值为这两个根结点的权值之和,这样树的数量就减少了一个。
 3、重复操作2,直到只剩下一棵树为止,这棵树就是哈夫曼树。

例如,现给定5个数,分别为1、2、2、3、6,要求构建一棵哈夫曼树。

动图展示:
在这里插入图片描述

1、初始状态:有5棵只有根结点的树。

在这里插入图片描述

2、合并权值为1和2的两棵树,生成这两棵树的父结点,父结点权值为3。

在这里插入图片描述

3、合并权值为2和3的两棵树,生成这两棵树的父结点,父结点权值为5。

在这里插入图片描述

4、合并权值为3和5的两棵树,生成这两棵树的父结点,父结点权值为8。

在这里插入图片描述

5、合并权值为6和8的两棵树,生成这两棵树的父结点,父结点权值为14。

在这里插入图片描述

6、此时只剩下一棵树了,这棵树就是哈夫曼树。

在这里插入图片描述

观察这棵哈夫曼树,我们还可以发现,哈夫曼树不存在度为1的结点。因为我们每次都是选择两棵树进行合并,自然不存在度为1的结点。
由此我们还可以推出,若给定n个数要求构建哈夫曼树,则构建出来的哈夫曼树的结点总数为2n-1,因为对于任意的二叉树,其度为0的叶子结点个数一定比度为2的结点个数多1。

在这里插入图片描述

总结一下:
构建哈夫曼树就是反复选择两个最小的元素进行合并,直到只剩下一个元素为止。

代码实现

代码实现中,单个结点的类型定义如下:

typedef double DataType; //结点权值的数据类型

typedef struct HTNode //单个结点的信息
{
	DataType weight; //权值
	int parent; //父节点
	int lc, rc; //左右孩子
}*HuffmanTree;

代码实现时,我们用一个数组存储构建出来的哈夫曼树中各个结点的基本信息(权值、父结点、左孩子以及右孩子)。该数组的基本布局如下:

在这里插入图片描述

我们以“用数字7、5、4、2构建一棵哈夫曼树”为例,代码的基本实现步骤如下:

第一阶段
所构建的哈夫曼树的总结点个数为2 × 4 − 1 = 7 2\times4-1=72×4−1=7,但是这里我们开辟的数组可以存储8个结点的信息,因为数组中下标为0的位置我们不存储结点信息,具体原因后面给出。
我们先将用于构建哈夫曼树的数字7、5、4、2依次赋值给数组中下标为1-4的权值位置,其余信息均初始化为0。
在这里插入图片描述

第二阶段:

从数组中下标为1-4的元素中,选取权值最小,并且父结点为0(代表其还没有父结点)的两个结点,生成它们的父结点:
 1、下标为5的结点的权值等于被选取的两个结点的权值之和。
 2、两个被选取的结点的父结点就是下标为5的结点。
 3、下标为5的结点左孩子是被选取的两个结点中权值较小的结点,另外一个是其右孩子。

在这里插入图片描述
再从数组中下标为1-5的元素中,选取权值最小,并且父结点为0的两个结点,生成它们的父结点。

在这里插入图片描述

继续从数组中下标为1-6的元素中,选取权值最小,并且父结点为0的两个结点,生成它们的父结点。

在这里插入图片描述
此时,除了下标为0的元素以外,数组中所有元素均已有了自己的结点信息,哈夫曼树已经构建完毕。

在这里插入图片描述
根据数组信息,我们可以画出所构建的哈夫曼树:
在这里插入图片描述

观察该数组中的数据,我们可以发现,权值为7、5、4、2的结点的左孩子和右孩子均为0,也就是它们没有左右孩子,因为它们是叶子结点。此外,数组中父结点为0的结点其实就是所构建的哈夫曼树的根结点。

现在我们来说说为什么数组中下标为0的元素不存储结点信息?
 因为在数组中叶子结点的左右孩子是0,根结点的父结点是0,我们若是用数组中下标为0元素存储结点信息,那么我们将不能区分左右孩子为0的结点是叶子结点还是说该结点的左右孩子是下标为0的结点,同时也不知道哈夫曼树的根结点到底是谁。

代码示例:

//在下标为1到i-1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值
void Select(HuffmanTree& HT, int n, int& s1, int& s2)
{
	int min;
	//找第一个最小值
	for (int i = 1; i <= n; i++)
	{
		if (HT[i].parent == 0)
		{
			min = i;
			break;
		}
	}
	for (int i = min + 1; i <= n; i++)
	{
		if (HT[i].parent == 0 && HT[i].weight < HT[min].weight)
			min = i;
	}
	s1 = min; //第一个最小值给s1
	//找第二个最小值
	for (int i = 1; i <= n; i++)
	{
		if (HT[i].parent == 0 && i != s1)
		{
			min = i;
			break;
		}
	}
	for (int i = min + 1; i <= n; i++)
	{
		if (HT[i].parent == 0 && HT[i].weight < HT[min].weight&&i != s1)
			min = i;
	}
	s2 = min; //第二个最小值给s2
}

//构建哈夫曼树
void CreateHuff(HuffmanTree& HT, DataType* w, int n)
{
	int m = 2 * n - 1; //哈夫曼树总结点数
	HT = (HuffmanTree)calloc(m + 1, sizeof(HTNode)); //开m+1个HTNode,因为下标为0的HTNode不存储数据
	for (int i = 1; i <= n; i++)
	{
		HT[i].weight = w[i - 1]; //赋权值给n个叶子结点
	}
	for (int i = n + 1; i <= m; i++) //构建哈夫曼树
	{
		//选择权值最小的s1和s2,生成它们的父结点
		int s1, s2;
		Select(HT, i - 1, s1, s2); //在下标为1到i-1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值
		HT[i].weight = HT[s1].weight + HT[s2].weight; //i的权重是s1和s2的权重之和
		HT[s1].parent = i; //s1的父亲是i
		HT[s2].parent = i; //s2的父亲是i
		HT[i].lc = s1; //左孩子是s1
		HT[i].rc = s2; //右孩子是s2
	}
	//打印哈夫曼树中各结点之间的关系
	printf("哈夫曼树为:>\n");
	printf("下标   权值     父结点   左孩子   右孩子\n");
	printf("0                                  \n");
	for (int i = 1; i <= m; i++)
	{
		printf("%-4d   %-6.2lf   %-6d   %-6d   %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lc, HT[i].rc);
	}
	printf("\n");
}

哈夫曼编码的生成

编码生成思路

对于任意一棵二叉树来说,把二叉树上的所有分支都进行编号,将所有左分支都标记为0,所有右分支都标记为1。
我们就以7、5、4、2构建的哈夫曼树为例。
在这里插入图片描述

那么对于树上的任何一个结点,都可以根据从根结点到该结点的路径唯一确定一个编号。

对于哈夫曼树上的叶子结点,根据从根结点到该叶子结点的路径所确定的一个编号,就是该叶子结点的哈夫曼编码。

例如,上图中:
叶子结点7的哈夫曼编码为:0
叶子结点5的哈夫曼编码为:1 0
叶子结点4的哈夫曼编码为:1 1 1
叶子结点2的哈夫曼编码为:1 1 0

代码实现

我们首先需要清楚这样一个问题:
用n个数据所构建出来的哈夫曼树,生成的哈夫曼编码的长度最长是多少?

 因为哈夫曼编码是根据从根结点到该叶子结点的路径所确定的一个编号,所以生成的哈夫曼编码最长的叶子结点一定是离根结点最远的结点。要使叶子结点里根结点达到最远,那么生成的哈夫曼树应该是斜二叉树。

在这里插入图片描述

该斜二叉树中最后一层的叶子结点所生成的哈夫曼编码就是最长的,其哈夫曼编码的长度就是从根结点到达该叶子结点的路径长度,即n − 1 n-1n−1。

一个字符串若是想要容纳下“用n个数据生成的哈夫曼编码”中的任意一个编码,那么这个字符串的长度应该为n nn,因为我们还需要用一个字节的位置用于存放字符串的结束标志’\0’。

我们就以数字7、5、4、2构建的哈夫曼树为例,哈夫曼编码生成的基本实现步骤如下:

第一阶段
因为数据个数为4,所以我们开辟一个大小为4的辅助空间,并将最后一个位置赋值为’\0’,用于暂时存放正在生成的哈夫曼编码。
在这里插入图片描述

为了存放这4个数据哈夫曼编码,我们开辟一个字符指针数组,该数组中有5个元素,每个元素的类型为char**,该字符指针数组的基本布局如下:
在这里插入图片描述
注意:这里为了与“构建哈夫曼树时所生成的数组”中的下标相对应,所以该字符指针数组中下标为0的元素也不存储有效数据。

第二阶段
利用已经构建好的哈夫曼树,生成这4个数据的哈夫曼编码。单个数据生成哈夫曼编码的过程如下:
 1、判断该数据结点与其父结点之间的关系,若该数据结点是其父结点的左孩子,则将start指针前移,并将0填入start指向的位置,若是右孩子,则在该位置填1。
 2、接着用同样的方法判断其父结点与其父结点的父结点之间的关系,直到待判断的结点为哈夫曼树的根结点为止,该结点的哈夫曼编码生成完毕。
 3、将字符串中从start的位置开始的数据拷贝到字符指针数组中的相应位置。

这里我们以生成数据5的哈夫曼编码为例。
在这里插入图片描述
注意:在每次生成数据的哈夫曼编码之前,先将start指针指向’\0’。

按照此方式,依次生成7、5、4、2的哈夫曼编码后,字符指针数组的基本布局如下:

在这里插入图片描述
哈夫曼编码生成完毕。

代码示例:

typedef char **HuffmanCode;

//生成哈夫曼编码
void HuffCoding(HuffmanTree& HT, HuffmanCode& HC, int n)
{
	HC = (HuffmanCode)malloc(sizeof(char*)*(n + 1)); //开n+1个空间,因为下标为0的空间不用
	char* code = (char*)malloc(sizeof(char)*n); //辅助空间,编码最长为n(最长时,前n-1个用于存储数据,最后1个用于存放'\0')
	code[n - 1] = '\0'; //辅助空间最后一个位置为'\0'
	for (int i = 1; i <= n; i++)
	{
		int start = n - 1; //每次生成数据的哈夫曼编码之前,先将start指针指向'\0'
		int c = i; //正在进行的第i个数据的编码
		int p = HT[c].parent; //找到该数据的父结点
		while (p) //直到父结点为0,即父结点为根结点时,停止
		{
			if (HT[p].lc == c) //如果该结点是其父结点的左孩子,则编码为0,否则为1
				code[--start] = '0';
			else
				code[--start] = '1';
			c = p; //继续往上进行编码
			p = HT[c].parent; //c的父结点
		}
		HC[i] = (char*)malloc(sizeof(char)*(n - start)); //开辟用于存储编码的内存空间
		strcpy(HC[i], &code[start]); //将编码拷贝到字符指针数组中的相应位置
	}
	free(code); //释放辅助空间
}

完整代码展示及代码测试

完整代码块:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef double DataType; //结点权值的数据类型

typedef struct HTNode //单个结点的信息
{
	DataType weight; //权值
	int parent; //父节点
	int lc, rc; //左右孩子
}*HuffmanTree;

typedef char **HuffmanCode; //字符指针数组中存储的元素类型

//在下标为1到i-1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值
void Select(HuffmanTree& HT, int n, int& s1, int& s2)
{
	int min;
	//找第一个最小值
	for (int i = 1; i <= n; i++)
	{
		if (HT[i].parent == 0)
		{
			min = i;
			break;
		}
	}
	for (int i = min + 1; i <= n; i++)
	{
		if (HT[i].parent == 0 && HT[i].weight < HT[min].weight)
			min = i;
	}
	s1 = min; //第一个最小值给s1
	//找第二个最小值
	for (int i = 1; i <= n; i++)
	{
		if (HT[i].parent == 0 && i != s1)
		{
			min = i;
			break;
		}
	}
	for (int i = min + 1; i <= n; i++)
	{
		if (HT[i].parent == 0 && HT[i].weight < HT[min].weight&&i != s1)
			min = i;
	}
	s2 = min; //第二个最小值给s2
}

//构建哈夫曼树
void CreateHuff(HuffmanTree& HT, DataType* w, int n)
{
	int m = 2 * n - 1; //哈夫曼树总结点数
	HT = (HuffmanTree)calloc(m + 1, sizeof(HTNode)); //开m+1个HTNode,因为下标为0的HTNode不存储数据
	for (int i = 1; i <= n; i++)
	{
		HT[i].weight = w[i - 1]; //赋权值给n个叶子结点
	}
	for (int i = n + 1; i <= m; i++) //构建哈夫曼树
	{
		//选择权值最小的s1和s2,生成它们的父结点
		int s1, s2;
		Select(HT, i - 1, s1, s2); //在下标为1到i-1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值
		HT[i].weight = HT[s1].weight + HT[s2].weight; //i的权重是s1和s2的权重之和
		HT[s1].parent = i; //s1的父亲是i
		HT[s2].parent = i; //s2的父亲是i
		HT[i].lc = s1; //左孩子是s1
		HT[i].rc = s2; //右孩子是s2
	}
	//打印哈夫曼树中各结点之间的关系
	printf("哈夫曼树为:>\n");
	printf("下标   权值     父结点   左孩子   右孩子\n");
	printf("0                                  \n");
	for (int i = 1; i <= m; i++)
	{
		printf("%-4d   %-6.2lf   %-6d   %-6d   %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lc, HT[i].rc);
	}
	printf("\n");
}

//生成哈夫曼编码
void HuffCoding(HuffmanTree& HT, HuffmanCode& HC, int n)
{
	HC = (HuffmanCode)malloc(sizeof(char*)*(n + 1)); //开n+1个空间,因为下标为0的空间不用
	char* code = (char*)malloc(sizeof(char)*n); //辅助空间,编码最长为n(最长时,前n-1个用于存储数据,最后1个用于存放'\0')
	code[n - 1] = '\0'; //辅助空间最后一个位置为'\0'
	for (int i = 1; i <= n; i++)
	{
		int start = n - 1; //每次生成数据的哈夫曼编码之前,先将start指针指向'\0'
		int c = i; //正在进行的第i个数据的编码
		int p = HT[c].parent; //找到该数据的父结点
		while (p) //直到父结点为0,即父结点为根结点时,停止
		{
			if (HT[p].lc == c) //如果该结点是其父结点的左孩子,则编码为0,否则为1
				code[--start] = '0';
			else
				code[--start] = '1';
			c = p; //继续往上进行编码
			p = HT[c].parent; //c的父结点
		}
		HC[i] = (char*)malloc(sizeof(char)*(n - start)); //开辟用于存储编码的内存空间
		strcpy(HC[i], &code[start]); //将编码拷贝到字符指针数组中的相应位置
	}
	free(code); //释放辅助空间
}

//主函数
int main()
{
	int n = 0;
	printf("请输入数据个数:>");
	scanf("%d", &n);
	DataType* w = (DataType*)malloc(sizeof(DataType)*n);
	if (w == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	printf("请输入数据:>");
	for (int i = 0; i < n; i++)
	{
		scanf("%lf", &w[i]);
	}
	HuffmanTree HT;
	CreateHuff(HT, w, n); //构建哈夫曼树

	HuffmanCode HC;
	HuffCoding(HT, HC, n); //构建哈夫曼编码

	for (int i = 1; i <= n; i++) //打印哈夫曼编码
	{
		printf("数据%.2lf的编码为:%s\n", HT[i].weight, HC[i]);
	}
	free(w);
	return 0;
}

我们就以下问题作为测试:

已知某系统在通信网络中只可能出现八种字符,其概率分别为
0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11
试设计哈夫曼编码

运行结果展示:
在这里插入图片描述

如此明了清晰的详解过程,希望各位看官能够一键三连哦💕

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

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

相关文章

Kafka 分区

分区是 Kafka 的核心功能&#xff0c;对于 Kafka 的存储结构、消息的生产消费方式都至关重要。 Partition&#xff08;分区&#xff09; 每 topic 都可以分成多分区&#xff0c;每个分区都是一组有序的、不可变的记录序列&#xff0c;每个分区在存储层面是 append log 文件。…

python中Requests发送json格式的post请求方法

问题&#xff1a;做requests请求时遇到如下报错&#xff1a; {“code”:“500”,“message”:"JSON parse error: Cannot construct instance of com.bang.erpapplication.domain.User (although at least one Creator exists): no String-argument constructor/factory …

唠一唠程序员的那些事

作为一名互联网摸爬滚打多年的老兵&#xff0c;我可以从以下角度提供一些信息&#xff1a; 加班是家常便饭&#xff1a;程序员往往需要加班来满足项目需求或完成任务。这意味着他们经常会在晚上、周末或节假日工作。代码不仅仅是工作&#xff1a;对于大多数程序员来说&#xff…

【C++】成员对象和成员函数分开存储

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、详解 3、代码清单 1 3.1、类中定义成员变量 3.2、类中定义成员函数 4、代码清单 2 5、总结 1、缘起 “成员变量…

SpringBoot 增强Controller方法,@ControllerAdvice注解的使用

参考资料 ControllerAdvice 用法SpringBoot使用ControllerAdvice注解 目录 一. ControllerAdvice注解作用二. ControllerAdvice注解 assignableTypes属性2.1 ControllerAdvice增强类2.2 Controller层2.3 效果 三. ControllerAdvice注解 basePackages属性3.1 ControllerAdvic…

踩坑|以为是Redis缓存没想到却是Spring事务!

前言 最近碰到了一个Bug&#xff0c;折腾了我好几天。并且这个Bug不是必现的&#xff0c;出现的概率比较低。一开始我以为是旧数据的问题&#xff0c;就让测试重新生成了一下数据&#xff0c;重新测试。由于后面几轮测试均未出现&#xff0c;我也就没太在意。 可惜好景不长&…

【LeetCode】260. 只出现一次的数字 III

260. 只出现一次的数字 III&#xff08;中等&#xff09; 思路 这道题是136. 只出现一次的数字 的进阶版&#xff0c;需要找出两个仅出现一次的元素。有了上一题的基础&#xff0c;我们很容易就想到要用异或来解决&#xff0c;但是由于这题最终会剩下两个不同的元素&#xff0…

设置和使用DragGAN:搭建非官方的演示版

DragGAN的官方版还没有发布&#xff0c;但是已经有非官方版的实现了&#xff0c;我们看看如何使用。DragGAN不仅让GAN重新回到竞争轨道上&#xff0c;而且为GAN图像处理开辟了新的可能性。正式版本将于本月发布。但是现在已经可以在一个非官方的演示中试用这个新工具了 DragGAN…

数据结构:二叉树(初阶)

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下二叉树方面的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 …

Unix/C/C++进阶--SocketCAN 编程

Unix/C/C进阶--SocketCAN 编程 1 介绍1.1 socketcan 简介1.2 can 发展历程1.3 can总线优点 2 知识点2.1 CAN详解--书籍、网站2.2 CAN详解--CAN与com口介绍2.3 CAN详解--各家CAN分析仪与软件的比较2.4 转载&#xff1a;CAN总线终端电阻2.5 如何破解汽车--CAN协议&#xff08;can…

3.8 电路布线

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 1.最优子结构的证明&#xff1a; 我的理解&#xff1a; 对于电路布线问题的最优子结构性质&#xff0c;我们可以通过数学推导进行证明。下面是对证明的…

conda在 powershell下不能激活虚拟环境

这里写自定义目录标题 问题原因解决办法增加环境变量修改PowerShell 策略初始化conda环境安装或更新conda 结果 问题原因 powershell正常是不行的&#xff0c;但是在cmd中是可以的 问题产生的原因有很多&#xff1a; 必须无法识别activate.bat激活无反应 解决办法 增加环…

【JavaSE】Java基础语法(四十六):枚举

文章目录 1. 概述2. 定义格式3. 枚举的特点4. 枚举的方法 1. 概述 枚举是一种特殊的数据类型&#xff0c;它列出了一组预定义的常量&#xff0c;并使用标识符来引用这些常量。枚举的用途很广泛&#xff0c;下面列举了几个常见的应用场景&#xff1a; 管理常量&#xff1a;如果您…

计算机组成原理---第三章存储系统 习题详解版

&#xff08;一&#xff09;精选课内习题 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 &#xff08;二&#xff09;精选课后习题 1.设有一个具有20位地址和32位字长的存储器&#xff0c;问&#xff1a; (1)该存储器能存储多少个字节的信息&#xff1f; (2)如果存储器由512k8位的SR…

Linux :: vim 编辑器:详解:文本复制/粘贴/剪切/删除 与 撤销普通操作及撤销撤销操作

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 前文&#x…

chatgpt赋能python:Python去掉None:提高代码效率,优化SEO

Python去掉None&#xff1a;提高代码效率&#xff0c;优化SEO 作为一名有10年Python编程经验的工程师&#xff0c;我发现Python中会频繁出现None类型的变量。这种情况在代码中一旦过多&#xff0c;就会影响程序的效率&#xff0c;同样也会影响SEO的排名。因此&#xff0c;为提…

【数据仓库架构】什么是 Azure Synapse,它与 Azure Data Bricks 有何不同?

Azure Synapse Analytics 是一项针对大型公司的无限信息分析服务&#xff0c;它被呈现为 Azure SQL 数据仓库 (SQL DW) 的演变&#xff0c;将业务数据存储和宏或大数据分析结合在一起。 在处理、管理和提供数据以满足即时商业智能和数据预测需求时&#xff0c;Synapse 为所有工…

Hive学习---5、文件格式和压缩、企业级调优

1、文件格式和压缩 1.1 Hadoop压缩概述 由于Hive是相当于与Hadoop的客户端&#xff0c;所以hadoop会啥压缩&#xff0c;Hive基本就会啥压缩。 压缩格式算法文件扩展名是否可切分DEFLATEDEFLATE.deflate否GzipDEFLATE.gz否bzip2bzip2.bz2是LZOLZO.lzo是SnappySnappy.snappy否…

word恢复和粘贴按钮变灰色,不可用怎么办?

如果 Word 中的恢复和粘贴按钮变成灰色&#xff0c;可能是由于以下原因之一&#xff1a; 1. 文档处于只读模式。 2. 与 Office 相关的某些组件已损坏或缺失。 3. Word 的文件权限被配置为只读。 以下是一些可能的解决方法&#xff1a; 1. 检查文档是否处于只读模式。 如果是…

随机数发生器设计(三)

随机数发生器设计&#xff08;三&#xff09;- 熵估计和健康测试 熵估计健康测试 熵估计 考虑都熵源的多样性&#xff0c;建立一个通用的熵估计模型比较困难。本文采用nist.sp.800-90B推荐的Markov评估。详见 https://doi.org/10.6028/NIST.SP.800-90B。 执行Markov评估时&am…