C/C++ 数据结构与算法【哈夫曼树】 哈夫曼树详细解析【日常学习,考研必备】带图+详细代码
哈夫曼树(最优二叉树)
1)基础概念
**路径:**从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。
**结点的路径长度:**两结点间路径上的分支数。
**树的路径长度:**从树根到每一个结点的路径长度之和。记作:TL。
结点数目相同的二叉树中,完全二叉树是路径长度最短的二叉树。
**权:**将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
**结点的带权路径长度:**从根结点到该结点之间的路径长度与该结点的权的乘积。
**树的带权路径长度:**树中所有叶子结点的带权路径长度之和。
2)构造哈夫曼树
顺序存储结构——一维结构数组
(1)定义结构
typedef struct {int weight; int parent, lch, rch;
}HTNode, * HuffmanTree;HuffmanTree H;
(2)步骤:
- 初始化HT [1…2n-1]:lch = rch = parent = 0;
- 输入初始几个叶子结点:置HT[1…n]的 weight 值;
- 进行以下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 = i; HT[s2] .parent = i;
- 修改新产生的HT[i]:
HT[i].weight = HT[s1].weight + HT[s2].weight;
HTli].Ich = s1;
HT[i].rch = s2;
void CreatHuffmanTree(HuffmanTree& HT, int n) { //构造哈夫曼树--哈夫曼算法if (n <= 1) return;int m = 2 * n - 1; // 数组共2n-1个元素HT = new HTNode[m + 1]; // 动态分配内存,0号单元未用,HT[m]表示根结点// 初始化2n-1个元素的lch、rch、parent为0for (int i = 1; i <= m; ++i) {HT[i].lch = HT[i].rch = HT[i].parent = 0;}// 输入前n个元素的weight值cout << "请输入" << n << "个字符的频率:" << endl;for (int i = 1; i <= n; ++i) {cin >> HT[i].weight;}// 构建哈夫曼树for (int i = n + 1; i <= m; i++) {int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].lch = s1;HT[i].rch = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}
(3)总代码:
权值为整数:
#include <iostream>
#include <limits.h>using namespace std;// 定义哈夫曼树节点结构
typedef struct {int weight;int parent, lch, rch;
} HTNode;typedef HTNode* HuffmanTree;// 选择两个双亲域为0且权值最小的结点
void Select(const HTNode* HT, int i, int& s1, int& s2) {s1 = s2 = -1;int min1 = INT_MAX, min2 = INT_MAX;for (int j = 1; j <= i; ++j) {if (HT[j].parent == 0 && HT[j].weight < min1) {min2 = min1;s2 = s1;min1 = HT[j].weight;s1 = j;}else if (HT[j].parent == 0 && HT[j].weight < min2) {min2 = HT[j].weight;s2 = j;}}
}void CreatHuffmanTree(HuffmanTree& HT, int n) { //构造哈夫曼树--哈夫曼算法if (n <= 1) return;int m = 2 * n - 1; // 数组共2n-1个元素HT = new HTNode[m + 1]; // 动态分配内存,0号单元未用,HT[m]表示根结点// 初始化2n-1个元素的lch、rch、parent为0for (int i = 1; i <= m; ++i) {HT[i].lch = HT[i].rch = HT[i].parent = 0;}// 输入前n个元素的weight值cout << "请输入" << n << "个字符的频率:" << endl;for (int i = 1; i <= n; ++i) {cin >> HT[i].weight;}// 构建哈夫曼树for (int i = n + 1; i <= m; i++) {int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].lch = s1;HT[i].rch = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}int main() {int n;cout << "请输入叶子节点的数量:";cin >> n;HuffmanTree HT;CreatHuffmanTree(HT, n);// 打印哈夫曼树(示例)cout << "哈夫曼树构造完成,打印结果如下:" << endl;for (int i = 1; i < 2 * n; ++i) {cout << "Node " << i << ": Weight=" << HT[i].weight<< ", Parent=" << HT[i].parent<< ", Left Child=" << HT[i].lch<< ", Right Child=" << HT[i].rch << endl;}delete[] HT; // 释放动态分配的内存return 0;
}
权值为浮点数
#include <iostream>
#include <limits.h> // 如果不再使用 INT_MAX,可以不需要这个头文件using namespace std;// 定义哈夫曼树节点结构,将 weight 改为 double 类型
typedef struct {double weight; // 权值改为 double 类型int parent, lch, rch;
} HTNode;typedef HTNode* HuffmanTree;// 选择两个双亲域为0且权值最小的结点
void Select(const HTNode* HT, int i, int& s1, int& s2) {s1 = s2 = -1;double min1 = DBL_MAX, min2 = DBL_MAX; // 使用 DBL_MAX 作为最大值初始化for (int j = 1; j <= i; ++j) {if (HT[j].parent == 0 && HT[j].weight < min1) {min2 = min1;s2 = s1;min1 = HT[j].weight;s1 = j;}else if (HT[j].parent == 0 && HT[j].weight < min2) {min2 = HT[j].weight;s2 = j;}}
}void CreatHuffmanTree(HuffmanTree& HT, int n) { //构造哈夫曼树--哈夫曼算法if (n <= 1) return;int m = 2 * n - 1; // 数组共2n-1个元素HT = new HTNode[m + 1]; // 动态分配内存,0号单元未用,HT[m]表示根结点// 初始化2n-1个元素的lch、rch、parent为0for (int i = 1; i <= m; ++i) {HT[i].lch = HT[i].rch = HT[i].parent = 0;HT[i].weight = 0.0; // 初始化 weight 为 0.0}// 输入前n个元素的weight值cout << "请输入" << n << "个字符的小数频率:" << endl;for (int i = 1; i <= n; ++i) {cin >> HT[i].weight;}// 构建哈夫曼树for (int i = n + 1; i <= m; i++) {int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].lch = s1;HT[i].rch = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}int main() {int n;cout << "请输入叶子节点的数量:";cin >> n;HuffmanTree HT;CreatHuffmanTree(HT, n);// 打印哈夫曼树(示例)cout << "哈夫曼树构造完成,打印结果如下:" << endl;for (int i = 1; i <= 2 * n - 1; ++i) { // 注意这里应该是 2*n-1 而不是 2*ncout << "Node " << i << ": Weight=" << HT[i].weight<< ", Parent=" << HT[i].parent<< ", Left Child=" << HT[i].lch<< ", Right Child=" << HT[i].rch << endl;}delete[] HT; // 释放动态分配的内存return 0;
}
(4)运行结果:
3)哈夫曼编码
在远程通讯中,要将待传字符转换成由二进制的字符串:
若将编码设计为长度不等的二进制编码,即让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。
问题1 :什么样的前缀码能使得电文总长最短?
——哈夫曼编码
方法:
1、统计字符集中每个字符在电文中出现的平均概率(概率越大要求编码越短)。
2、利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。 则概率越大的结点,路径越短。
3、在哈夫曼树的每个分支上标上0或1:
- 结点的左分支标0,右分支桥 1。
- 把从根到每个吐子的路径上的标号连接起来,作为该叶子代表的字符的编码。
问题2 :为什么哈夫曼编码能够保证是前缀编码?
因为没有一片树叶是另一片树叶的祖先,所以每个叶结点的编码就不可能是其它叶结点编码的前缀。
问题 3 :为什么哈夫曼编码能够保证字符编码总长最短?
因为哈夫曼树的带权路径长度最短,故字符编码的总长最短。
- 性质1 哈夫曼编码是前缀码
- 性质2 哈夫曼编码是最优前缀码
算法实现:
// 从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
void CreatHuffmanCode(const HuffmanTree& HT, HuffmanCode& HC, int n) {HC = new char*[n + 1]; // 分配n个字符编码的头指针数组char* cd = new char[n]; // 分配临时存放编码的动态数组空间cd[n - 1] = '\0'; // 编码结束符for (int i = 1; i <= n; ++i) {int start = n - 1;int c = i;int f = HT[i].parent;// 从叶子结点开始向上回溯,直到根结点while (f != 0) {--start;if (HT[f].lch == c)cd[start] = '0'; // 结点c是f的左孩子,则生成代码0elsecd[start] = '1'; // 结点c是f的右孩子,则生成代码1c = f;f = HT[f].parent;}// 计算编码长度并分配适当的空间int codeLength = n - start;HC[i] = new char[codeLength];strncpy(HC[i], &cd[start], codeLength);HC[i][codeLength - 1] = '\0'; // 确保字符串以空字符终止}delete[] cd; // 释放临时空间
}
strncpy(HC[i], &cd[start], codeLength);
语句在C++中确实可以用于复制字符数组,但它有一些潜在的问题和局限性。特别是当你使用strncpy
时,如果目标缓冲区没有足够的空间来包含源字符串加上终止空字符(\0
),它不会自动添加终止空字符,这可能会导致后续操作出现问题。
此外,在现代C++中,更推荐使用std::string
来处理字符串,因为它们更安全、更方便,并且可以避免手动管理内存的复杂性和风险。
// 从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
void CreatHuffmanCode(const HuffmanTree& HT, HuffmanCode& HC, int n) {HC.resize(n + 1); // 分配n个字符编码的空间for (int i = 1; i <= n; ++i) {string code = "";int c = i;int f = HT[i].parent;// 从叶子结点开始向上回溯,直到根结点while (f != 0) {if (HT[f].lch == c)code = '0' + code; // 结点c是f的左孩子,则生成代码0elsecode = '1' + code; // 结点c是f的右孩子,则生成代码1c = f;f = HT[f].parent;}HC[i] = code;}
}
总代码实现:
#include <iostream>
#include <cstring> // 用于 strcpy 和 strlen
#include <limits> // 用于 std::numeric_limitsusing namespace std;// 定义哈夫曼树节点结构
typedef struct HTNode {double weight; // 权重改为 double 类型int parent, lch, rch;
} HTNode;typedef HTNode* HuffmanTree;// 定义哈夫曼编码结构
typedef char** HuffmanCode;// 选择两个双亲域为0且权值最小的结点
void Select(const HTNode* HT, int i, int& s1, int& s2) {s1 = s2 = -1;double min1 = numeric_limits<double>::max(), min2 = numeric_limits<double>::max();for (int j = 1; j <= i; ++j) {if (HT[j].parent == 0 && HT[j].weight < min1) {min2 = min1;s2 = s1;min1 = HT[j].weight;s1 = j;} else if (HT[j].parent == 0 && HT[j].weight < min2) {min2 = HT[j].weight;s2 = j;}}
}// 构造哈夫曼树--哈夫曼算法
void CreatHuffmanTree(HuffmanTree &HT, int n) {if (n <= 1) return;int m = 2 * n - 1; // 数组共2n-1个元素HT = new HTNode[m + 1]; // 动态分配内存,0号单元未用,HT[m]表示根结点// 初始化2n-1个元素的lch、rch、parent为0for (int i = 1; i <= m; ++i) {HT[i].lch = HT[i].rch = HT[i].parent = 0;HT[i].weight = 0.0; // 初始化权重为 0.0}// 输入前n个元素的weight值cout << "请输入" << n << "个字符的频率(浮点数):" << endl;for (int i = 1; i <= n; ++i) {cin >> HT[i].weight;}// 构建哈夫曼树for (int i = n + 1; i <= m; i++) {int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].lch = s1;HT[i].rch = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}// 从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n) {HC = new char*[n + 1]; // 分配n个字符编码的头指针数组char* cd = new char[n]; // 分配临时存放编码的动态数组空间cd[n - 1] = '\0'; // 编码结束符for (int i = 1; i <= n; ++i) {int start = n - 1;int c = i;int f = HT[i].parent;// 从叶子结点开始向上回溯,直到根结点while (f != 0) {--start;if (HT[f].lch == c)cd[start] = '0'; // 结点c是f的左孩子,则生成代码0elsecd[start] = '1'; // 结点c是f的右孩子,则生成代码1c = f;f = HT[f].parent;}// 计算编码长度并分配适当的空间int codeLength = n - start;HC[i] = new char[codeLength];strncpy(HC[i], &cd[start], codeLength);HC[i][codeLength - 1] = '\0'; // 确保字符串以空字符终止}delete[] cd; // 释放临时空间
}// 测试函数
int main() {int n;cout << "请输入叶子节点的数量:";cin >> n;HuffmanTree HT;CreatHuffmanTree(HT, n);HuffmanCode HC;CreatHuffmanCode(HT, HC, n);// 打印哈夫曼编码cout << "哈夫曼编码如下:" << endl;for (int i = 1; i <= n; ++i) {cout << "Character " << i << ": " << HC[i] << endl;}// 清理资源for (int i = 1; i <= n; ++i) {delete[] HC[i];}delete[] HC;delete[] HT;return 0;
}
改进后的代码:
#include <iostream>
#include <vector>
#include <string>
#include <limits>using namespace std;// 定义哈夫曼树节点结构
typedef struct HTNode {double weight; // 权重改为 double 类型int parent, lch, rch;
} HTNode;typedef HTNode* HuffmanTree;// 选择两个双亲域为0且权值最小的结点
void Select(const vector<HTNode>& HT, int i, int& s1, int& s2) {s1 = s2 = -1;double min1 = numeric_limits<double>::max(), min2 = numeric_limits<double>::max();for (int j = 1; j <= i; ++j) {if (HT[j].parent == 0 && HT[j].weight < min1) {min2 = min1;s2 = s1;min1 = HT[j].weight;s1 = j;} else if (HT[j].parent == 0 && HT[j].weight < min2) {min2 = HT[j].weight;s2 = j;}}
}// 构造哈夫曼树--哈夫曼算法
void CreatHuffmanTree(vector<HTNode>& HT, int n) {if (n <= 1) return;int m = 2 * n - 1; // 数组共2n-1个元素// 初始化2n-1个元素的lch、rch、parent为0,权重为0.0HT.resize(m + 1);for (int i = 1; i <= m; ++i) {HT[i] = {0.0, 0, 0, 0};}// 输入前n个元素的weight值cout << "请输入" << n << "个字符的频率(浮点数):" << endl;for (int i = 1; i <= n; ++i) {cin >> HT[i].weight;}// 构建哈夫曼树for (int i = n + 1; i <= m; i++) {int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].lch = s1;HT[i].rch = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}// 从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
void CreatHuffmanCode(const vector<HTNode>& HT, vector<string>& HC, int n) {HC.resize(n + 1); // 分配n个字符编码的空间for (int i = 1; i <= n; ++i) {string code = "";int c = i;int f = HT[i].parent;// 从叶子结点开始向上回溯,直到根结点while (f != 0) {if (HT[f].lch == c)code = '0' + code; // 结点c是f的左孩子,则生成代码0elsecode = '1' + code; // 结点c是f的右孩子,则生成代码1c = f;f = HT[f].parent;}HC[i] = code;}
}// 测试函数
int main() {int n;cout << "请输入叶子节点的数量:";cin >> n;vector<HTNode> HT;CreatHuffmanTree(HT, n);vector<string> HC;CreatHuffmanCode(HT, HC, n);// 打印哈夫曼编码cout << "哈夫曼编码如下:" << endl;for (int i = 1; i <= n; ++i) {cout << "Character " << i << ": " << HC[i] << endl;}return 0;
}
改进后:
#include <iostream>
#include <cstring> // 用于 strcpy 和 strlen
#include <limits> // 用于 std::numeric_limitsusing namespace std;// 定义哈夫曼树节点结构
typedef struct HTNode {double weight; // 权重改为 double 类型int parent, lch, rch;
} HTNode;typedef HTNode* HuffmanTree;// 定义哈夫曼编码结构
typedef char** HuffmanCode;// 选择两个双亲域为0且权值最小的结点
void Select(const HTNode* HT, int i, int& s1, int& s2) {s1 = s2 = -1;double min1 = numeric_limits<double>::max(), min2 = numeric_limits<double>::max();for (int j = 1; j <= i; ++j) {if (HT[j].parent == 0 && HT[j].weight < min1) {min2 = min1;s2 = s1;min1 = HT[j].weight;s1 = j;} else if (HT[j].parent == 0 && HT[j].weight < min2) {min2 = HT[j].weight;s2 = j;}}
}// 构造哈夫曼树--哈夫曼算法
void CreatHuffmanTree(HuffmanTree &HT, int n) {if (n <= 1) return;int m = 2 * n - 1; // 数组共2n-1个元素HT = new HTNode[m + 1]; // 动态分配内存,0号单元未用,HT[m]表示根结点// 初始化2n-1个元素的lch、rch、parent为0for (int i = 1; i <= m; ++i) {HT[i].lch = HT[i].rch = HT[i].parent = 0;HT[i].weight = 0.0; // 初始化权重为 0.0}// 输入前n个元素的weight值cout << "请输入" << n << "个字符的频率):" << endl;for (int i = 1; i <= n; ++i) {cin >> HT[i].weight;}// 构建哈夫曼树for (int i = n + 1; i <= m; i++) {int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].lch = s1;HT[i].rch = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}// 从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n) {HC = new char*[n + 1]; // 分配n个字符编码的头指针数组for (int i = 1; i <= n; ++i) {string code = ""; // 使用string来构建编码int c = i;int f = HT[i].parent;// 从叶子结点开始向上回溯,直到根结点while (f != 0) {if (HT[f].lch == c)code = '0' + code; // 结点c是f的左孩子,则生成代码0elsecode = '1' + code; // 结点c是f的右孩子,则生成代码1c = f;f = HT[f].parent;}// 将string转换为C风格字符串并分配适当的空间HC[i] = new char[code.length() + 1];strcpy(HC[i], code.c_str());}
}// 测试函数
int main() {int n;cout << "请输入叶子节点的数量:";cin >> n;if (n <= 0) {cerr << "叶子节点数量必须大于0." << endl;return 1;}HuffmanTree HT;CreatHuffmanTree(HT, n);HuffmanCode HC;CreatHuffmanCode(HT, HC, n);// 打印哈夫曼编码cout << "哈夫曼编码如下:" << endl;for (int i = 1; i <= n; ++i) {cout << "Character " << i << ": " << HC[i] << endl;}// 清理资源for (int i = 1; i <= n; ++i) {delete[] HC[i];}delete[] HC;delete[] HT;return 0;
}
运行结果:
相关文章:

C/C++ 数据结构与算法【哈夫曼树】 哈夫曼树详细解析【日常学习,考研必备】带图+详细代码
哈夫曼树(最优二叉树) 1)基础概念 **路径:**从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。 **结点的路径长度:**两结点间路径上的分支数。 **树的路径长度:**从树根到每一个结点的路径…...

基于NodeMCU的物联网窗帘控制系统设计
最终效果 基于NodeMCU的物联网窗帘控制系统设计 项目介绍 该项目是“物联网实验室监测控制系统设计(仿智能家居)”项目中的“家电控制设计”中的“窗帘控制”子项目,最前者还包括“物联网设计”、“环境监测设计”、“门禁系统设计计”和“小…...

喜报 | 擎创科技入围上海市优秀信创解决方案
近日,由上海市经信委组织的“2024年上海市优秀信创解决方案”征集遴选活动圆满落幕,擎创科技凭借实践经验优秀的《擎创夏洛克智能预警与应急处置解决方案》成功入选“2024年上海市优秀信创解决方案”名单。 为激发创新活力,发挥标杆作用&…...

windows10下使用沙盒多开uiautoanimation可行性验证
文章目录 ⭐前言⭐sandboxie下载使用⭐pyinstaller打包python的uiautoanimation成exe⭐结论⭐结束 ⭐前言 大家好,我是yma16,本文分享windows下使用沙盒多开uiautoanimation可行性验证。 背景 实现多开应用程序从而进行自动化控制,批量处理大…...

电脑报错wsdprintproxy.dll丢失?修复wsdprintproxy.dll文件缺失的实用方法
在使用电脑的过程中,我们可能会遇到各种各样的错误提示,其中之一就是系统提示wsdprintproxy.dll文件丢失。这个DLL文件是Windows操作系统中的一个重要组件,它通常与Windows的打印功能相关。当这个文件丢失或损坏时,可能会导致打印…...
Kubernetes 的资源管理方式
集群架构 Docker 是每一个节点(包括 Master 节点和 Node 节点)的运行时环境。 kubelet 负责控制所有容器的启动和停止等,保证每个节点(包括 Master 节点和 Node 节点)正常工作,并且帮助 Node 节点和 Maste…...

layui动态拼接生成下拉框验证必填项失效问题
利用 jQuery 动态拼接下拉框时,lay-verify"required" 失效了,有以下几种原因。 1. <form></form>标签 加入 layui 类,class"layui-form" 。提交按钮上加自动提交,lay-submit ""; 。需…...
VUE3+VITE简单的跨域代理配置
出于安全考虑,未设置前端白名单,前端开发时,需要配置代理。 在本地创建一个虚拟服务器,发送请求数据,同时接受请求的数据, 利用服务器与服务器间,交互,不会有跨域问题,也…...

Xdebug
1、开启xdebug扩展 2、修改一下php.ini文件 xdebug.remote_enable 1 xdebug.remote_autostart 13、vscode安装插件php debug 4、生成launch.json文件,好像啥都不用改 5、vscode没有配置php路径的,需要去配置: 6、发起请求 8、代码断…...

IDEA | SpringBoot 项目中使用 Apifox 上传接口
目录 1 安装 Apifox Helper 插件2 获取 Apifox 的 API 访问令牌3 IDEA 中设置 API 访问令牌4 IDEA 中上传接口5 常见问题5.1 如何自动设置目录名5.2 如何自动设置接口名5.3 如何更改上传位置 Apifox 官方指南: https://apifox.com/help/applications-and-p…...
列表分页返回对象
列表分页返回对象 仅针对于新项目,因为一般进入公司后项目都是已经搭建好的,只需要在原有框架基础上操作就可以了,但是遇到从0开始的项目并且还没有架构需要自己搭框架的时候就需要自己想办法找各种封装格式。 下面记录分页列表返回的封装格式…...

微软edge浏览器 v131.0.2903.99便携版
前言 Microsoft Edge浏览器是个新浏览器,它用起来很简单,界面也很清爽。这个浏览器功能特别多,里面还带了微软的小助手Contana,能帮用户做不少贴心的事儿。它支持安装各种小工具(插件),还能在网…...
Prometheus 专栏 —— Prometheus入门介绍
Prometheus 是? Prometheus 是一个开源的服务监控系统和时序数据库,主要用于收集、存储、查询和告警时间序列数据,这些数据通常反映了系统或者应用的状态或性能 Prometheus 的基本功能是? 数据采集数据存储数据查询告警通知 Prometheus 监控核心组件?…...

元宇宙在教育行业主要有哪些应用场景?
近两年来,元宇宙风潮在全球范围内迅速掀起了一股新的浪潮,“元宇宙”已成为各行各业探索新发展方向的热门话题,教育行业亦不例外。那么元宇宙在教育行业主要有哪些应用场景呢? 以下为主要适用场景: 课程实践ÿ…...

JZ31 栈的压入、弹出序列
题目来源:栈的压入、弹出序列_牛客题霸_牛客网 题目:如下 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序…...

电脑缺失libcurl.dll怎么解决?详解电脑libcurl.dll文件丢失问题
一、libcurl.dll文件丢失的原因 libcurl.dll是一个用于处理URL传输的库文件,广泛应用于各种基于网络的应用程序。当这个文件丢失时,可能会导致相关应用程序无法正常运行。以下是libcurl.dll文件丢失的一些常见原因: 软件安装或卸载不完整&a…...

Ribbon、Nacos
目录 Ribbon 常见负载算法 切换负载均衡算法 Nacos注册中心 下载和运行 微服务模块接入注册中心 consumer-80模块 配置类 Controller Nacos配置中心 Nacos分类配置(实现配置隔离) DataID方案 Group方案 Namespace方案 总结 Ribbon Ribbon…...

SpringCloudAlibaba实战入门之路由网关Gateway初体验(十一)
Spring Cloud 原先整合 Zuul 作为网关组件,Zuul 由 Netflix 公司提供的,现在已经不维护了。后面 Netflix 公司又出来了一个 Zuul2.0 网关,但由于一直没有发布稳定版本,所以 Spring Cloud 等不及了就自己推出一个网关,已经不打算整合 zuul2.0 了。 一、什么是网关 1、顾明…...

【C语言练习(18)—指针传递参数练习】
C语言练习(18) 文章目录 C语言练习(18)前言问题问题解析 前言 指针的使用很方便参数之间的传递,通过交换数字,来练习函数之间指针传递数据。 问题 利用函数交换两个数字的大小 问题解析 例如a5;b10;想…...

外网访问 Docker 容器的可视化管理工具 DockerUI
DockerUI 是一个 docker 容器镜像的可视化图形化管理工具,DockerUI 可以用来轻松构建、管理和维护 docker 环境。让用户维护起来更方便。 本文就介绍如何安装使用 DockerUI 并结合路由侠内网穿透来访问 DockerUI。 第一步,安装 DockerUI 1,…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...