小肥柴慢慢手写数据结构(C篇)(5-5 Huffuman编码)

小肥柴慢慢学习数据结构笔记(C篇)(5-5 Huffman编码)

  • 目录
    • 5-16 编码案例
    • 5-17 Huffman编码原理
    • 5-18 Huffman编码/解码实现
      • 5-18-1 大致思路
      • 5-18-2 编码实现
      • 5-18-3 解码实现
      • 5-18-4 测试
    • 5-19 实际案例
    • 总结
    • 参考文献

目录

5-16 编码案例

咱们引用一个常见的案例,一步步带着大家理解Huffman编码的出现。

【问题】给定A、B、C、D四个字母组成的字符串(ABAACDC),要求使用数字0、1对其进行二进制编码

方案1:不等长编码

字符二进制编码
A0
B1
C10
D11

编码结果:0100101110

但是这个编码结果在译码阶段会产生歧义,因为:A(0)和B(1)分别是C(10)和D(11)的前缀,即“0100101110”可以被翻译为多种结果:
(1)ABBAACDC or (2)ACACDC
这显然是我们不想看到的,于是有人提出了一种等长编码方案。

方案2:等长编码

字符二进制编码
A00
B01
C10
D11

很容易得到编码结果:000100001011,对比不等长编码的结果“0100101110”,明显长了许多。

小结

(1)等长编码不会产生译码歧义,但是编码长度相对较长,不符合尽量节省传输带宽的通信设计原则。
(2)不等长编码容易产生译码歧义,但能有效缩短编码长度,在传输上是理想的形态。

【注】我们仅站在计算机专业初学者的视角去看待这个问题,我自己是通信/信号类专业出身,知道这样的引入和讨论会造成非议,但为了帮助初学者理解该问题,这样的简化描述是很有必要的,见谅。

那么,有没有其他的编码方式,使得:“在不出现译码歧义的情况下,使得编码长度最短”。

===> 有,就是本帖简要介绍的Huffman编码,且这种编码要借助于二叉树类的数据结构。

5-17 Huffman编码原理

【核心思想】让出现次数最多的字符编码长度最短。(次数也可称为:频率、频次)

【案例】

还是使用开篇的问题来介绍Huffman编码:给定A、B、C、D四个字母组成的字符串(ABAACDC),要求使用数字0、1对其进行二进制编码。
step1:首先统计各个字符出现的次数,并作为节点,各字符节点拥有一个权重(weight)来表示字符串中该字符出现的次数。
step2:(每次)挑选weight最小的两个节点进行合并,即为这两个节点生成一个父节点,且父节点的weight为两个子节点weight之和。
step3:从剩下的节点(包含还未合并的原始字符节点和生成的父节点)循环执行上述操作,不断地合并最小的两个节点,最终只剩下一个根节点为止。

具体过程如下图所示:
在这里插入图片描述

最后得到编码表:

字符二进制编码
A1
B000
C001
D01

编码结果:100011001000001

【观察/性质】

  1. 标记所有左枝路径为0,所有右枝路径为1,则可以得到如下编码,称为Huffman编码
  2. 按照Huffman编码规则得到的编码结果,一定是在不出现歧义的条件下输出的码长度最短的编码。 ⇒ 后续给出相关学习链接
  3. 有关Huffman编码的唯一性,可以绕开数学推导直观地给出证明:
    因为所有的叶子结点都是被编码的字符,对树形数据结构来讲,从根节点出发(编码)到叶节点的路径是唯一的,不是吗? ⇒ 与本章第一节介绍树的基础术语那节对上了!
  4. Huffman编码不是唯一的,因为每次被选中的两个作业节点,总有两种排列方式(且互为镜像)形成新的父节点。
  5. 而出现频次高(weight大)的叶子结点排在后面被合并,相反出现频次低(weight小)的叶子结点排在前面被合并,自然而然使得出现频次高的字符的Huffman编码端,从而使得整体编码长度缩短。

关于 带权路径长度,WPL

  1. WPL——Weighted Path Length of Tree, 简记为 WPL

(1)WPL表示树的所有叶结点的带权路径长度之和。

(2)WPL可用于衡量一颗带权二叉树的优劣。

(3)具体公式为: W P L = ∑ i = 0 N w i p i WPL=\sum_{i=0}^{ N}w_ip_i WPL=i=0Nwipi,其中 w i w_i wi为权重 p i p_i pi为路径长度,以案例说明
字符A,权重=3,路径长=1;字符B,权重=1,路径长=2…
WPL = 31 + 13 + 22 + 13 = 13
这也是考研/面试/考试经常问到的没有营养的问题。

(4)简单观察这个公式,明显可以看到若能让权重大的字符对应的路径短,则可以减小WPL的值,这与Huffman编码的设计思路是一致的

  1. 平均码长于WPL的关系

L = L ( C ) = ∑ i = 0 N p i l i L=L(C)=\sum_{i=0}^{ N}p_il_i L=L(C)=i=0Npili,其中 l i l_i li为编码长度, p i p_i pi为编号为 i i i的码出现的概率

(1)编码长度 l i l_i li正好对应WPL中的路径长度(path)

(2)编号为 i i i的码出现的概率: p i = w i ∑ i = 0 N w i p_i=\frac{w_i}{\sum_{i=0}^{N}w_i} pi=i=0Nwiwi,和WPL中的权重对应起来

(3)其实这个公式本质和WPL一致,仅在定义和变量的命名方式上不同 ⇒ 请看(2)中的定义,秒懂。

至于WPL相关的数学讨论,偏向通信方向和密码学方向(已经超出多数学习数据结构与算法的普通同学的理解了,咱们先挖个坑,有机会慢慢补齐),参考链接 [1]、[2]。

5-18 Huffman编码/解码实现

5-18-1 大致思路

接下来我们会模仿原理部分介绍的Huffman编码/解码操作步骤,尽量降低理解难度。大致思路如下:

(1)编码

step1 统计字符权重
step2 构建Huffman树
step3 对照Huffman树进行编码

需要注意的点:
(1)考虑到编解码的对象是文本字符,可以实用char对应的ASII码作为存储编码列表的索引(index~i),减轻另外实现一套映射算法的额外工作。
(2)为了构建Huffman树(buildHuffTree),用节点数组模拟“森林”(pNode forest[ ], 对应操作addToForest),然后不断从森林中挑选权重最小的两个节点/子树进行合并(getMinNode,mergeNode),在实际挑选时采用每次挑选最小的一个并标记,连续挑选两次的策略,感觉还有改进的空间。
(3)使用递归函数genHuffCode来生成Huffman编码表,借用strcpy和strcat两个API拼接编码字符串。
(4)在判断当前节点是否为字符节点时,可以不做标记,直接判定是否为叶结点即可(isLeaf)。
(5)打印Huffman编码表,采用中序遍历递归实现(printHuffTreeCode)。

(2)解码

step1 拿到之前编码得到的Huffman树
step2 遍历传入的待解码数据(char* / char[]),对照Huffman树找到叶节点,得到一段数据的解码结果,并重置游标(curNode)为Huffman树根节点,循环往复,直到所有数据使用完毕。

5-18-2 编码实现

(1)头文件 HuffmanTree.h

#ifndef _Huffman_Tree_H
#define _Huffman_Tree_H#define LIST_SIZE 256  					//数据列表长度 
#define FOREST_SIZE (LIST_SIZE * 2 - 1) //构建Huffman树需要产生的森林长度
#define CODE_MAX 512                    //每个字符Huffman编码长度 
#define TEXT_MAX 4028                   //解码文本长度 struct TreeNode {char val;int weight;char code[CODE_MAX];struct TreeNode* left;struct TreeNode* right;
};
typedef struct TreeNode* pNode;char* Encode(char *orgData, int orgLen, pNode root);    //编码,返回编码结果 
char* Decode(char *codeData, int codeLen, pNode root);  //解码,返回解码结果 
void releaseTree(pNode root);                           //递归释放节点 
#endif

(2)编码部分
核心代码

char* Encode(char *orgData, int orgLen, pNode root){if(orgData == NULL || orgLen == 0){printf("编码入参错误!\n");return NULL;}if(orgLen == 1){printf("仅有一个字符,不用编码!\n");return NULL;}int i;printf("输入数据:%s\n", orgData);//(1)统计权重 int freq[LIST_SIZE];memset(freq, 0, sizeof(int) * LIST_SIZE);for(i=0; i<orgLen; i++){freq[orgData[i]]++;}//(2)构建Huffman树//C的传参比较繁琐,我不想为了代码看起来漂亮,给buildHuffTree再添加一个引用参数//所以采用了类似深拷贝的做法 pNode tmpTree = buildHuffTree(freq, LIST_SIZE);root->val = tmpTree->val;root->weight = tmpTree->weight;root->left = tmpTree->left;root->right = tmpTree->right;free(tmpTree);printf("\n字符的霍夫曼编码信息如下:\n");printHuffTreeCode(root); //(3)得到编码结果 char* ret = doEncode(orgData, orgLen, root);return ret;
}

干活函数

=========================================

static pNode buildHuffTree(int freq[], int codeListlen){pNode forest[FOREST_SIZE] = {NULL};pNode root = NULL;int i;for(i=0; i<codeListlen; i++){if(freq[i]>0)addToForest(forest, FOREST_SIZE, creatLeafNode(i, freq[i]));}while(1){pNode left = getMinNode(forest, FOREST_SIZE);pNode right = getMinNode(forest, FOREST_SIZE);if(right == NULL) {//仅有一个节点,合并结束 root = left;break; } else {pNode pathNode = mergeNode(left->weight + right->weight);pathNode->left = left;pathNode->right = right;addToForest(forest, FOREST_SIZE, pathNode);}}genHuffCode(root);return root;
}
static void addToForest(pNode forest[], int size, pNode node){int i;for(i=0; i<size; i++){if(forest[i] == NULL){forest[i] = node;break;}}
}
static pNode creatLeafNode(int val, int weight){pNode node = (pNode)malloc(sizeof(struct TreeNode));memset(node, 0, sizeof(struct TreeNode));node->val = val;node->weight = weight;return node;
}
static pNode getMinNode(pNode forest[], int size){pNode node = NULL;int min = -1;int i;for(i=0; i<size; i++){if(forest[i] && (min == -1 || forest[min]->weight > forest[i]->weight))min = i;				}if(min != -1){node = forest[min];forest[min] = NULL;}return node;
}
static pNode mergeNode(int weight){pNode node = (pNode)malloc(sizeof(struct TreeNode));memset(node, 0, sizeof(struct TreeNode));node->weight = weight;return node;
}
static void genHuffCode(pNode curNode){if(curNode){if(curNode->left){strcpy(curNode->left->code, curNode->code);strcat(curNode->left->code, "0");genHuffCode(curNode->left);}if(curNode->right){strcpy(curNode->right->code, curNode->code);strcat(curNode->right->code, "1");genHuffCode(curNode->right);}}
}

=============================================================

static int isLeaf(pNode node){return node->left == NULL && node->right == NULL;
}static void inOrder(pNode curNode){if(curNode){inOrder(curNode->left);if(isLeaf(curNode))printf("字符:%c  权重:%d   编码:%s \n", curNode->val, curNode->weight, curNode->code);inOrder(curNode->right);}
}static void printHuffTreeCode(pNode node){inOrder(node);
}

====================================================

static void transHuffTable(pNode HuffTable[], pNode curNode){if(curNode){if(isLeaf(curNode))HuffTable[curNode->val] = curNode;transHuffTable(HuffTable, curNode->left);transHuffTable(HuffTable, curNode->right);}
}static char* doEncode(char *orgData, int orgLen, const pNode root){pNode HuffTable[LIST_SIZE] = {NULL};transHuffTable(HuffTable, root);//这里写的不好,为了节省空间采取了一个笨办法 int i;int totalSize = 0;for(i=0; i<orgLen; i++)totalSize += strlen(HuffTable[orgData[i]]->code);printf("\n霍夫曼编码长度:%d", totalSize);char* HuffCode = (char *)malloc(sizeof(char) * (totalSize+1));memset(HuffCode, 0, totalSize+1);for(i=0; i<orgLen; i++)strcat(HuffCode, HuffTable[orgData[i]]->code);return HuffCode;
}

5-18-3 解码实现

char* Decode(char *codeData, int codeLen, pNode root){if(codeData == NULL || codeLen == 0 || root == NULL){printf("\n解码入参错误!\n");return NULL;}if(codeLen == 1){printf("仅有一个字符,不用解码!\n");return NULL;}//此处也是一个笨办法,应该去看论文估计一下长度 char* text = (char *)malloc(sizeof(char) * (TEXT_MAX));memset(text, 0, TEXT_MAX);int i,j;pNode curNode = root;//按照之前思路模拟实现,虽然贴近人的自然思维,但是效率不高 for(i=0, j=0; i<codeLen; i++){curNode = codeData[i] == '0' ? curNode->left : curNode->right;if(isLeaf(curNode)){text[j++] = curNode->val;curNode = root;}}return text;
}

=======================================================
完整的 HuffmanTree.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "HuffmanTree.h"static pNode getMinNode(pNode forest[], int size){pNode node = NULL;int min = -1;int i;for(i=0; i<size; i++){if(forest[i] && (min == -1 || forest[min]->weight > forest[i]->weight))min = i;				}if(min != -1){node = forest[min];forest[min] = NULL;}return node;
}static pNode mergeNode(int weight){pNode node = (pNode)malloc(sizeof(struct TreeNode));memset(node, 0, sizeof(struct TreeNode));node->weight = weight;return node;
}static pNode creatLeafNode(int val, int weight){pNode node = (pNode)malloc(sizeof(struct TreeNode));memset(node, 0, sizeof(struct TreeNode));node->val = val;node->weight = weight;return node;
}static void addToForest(pNode forest[], int size, pNode node){int i;for(i=0; i<size; i++){if(forest[i] == NULL){forest[i] = node;break;}}
}static void genHuffCode(pNode curNode){if(curNode){if(curNode->left){strcpy(curNode->left->code, curNode->code);strcat(curNode->left->code, "0");genHuffCode(curNode->left);}if(curNode->right){strcpy(curNode->right->code, curNode->code);strcat(curNode->right->code, "1");genHuffCode(curNode->right);}}
}static pNode buildHuffTree(int freq[], int codeListlen){pNode forest[FOREST_SIZE] = {NULL};pNode root = NULL;int i;for(i=0; i<codeListlen; i++){if(freq[i]>0)addToForest(forest, FOREST_SIZE, creatLeafNode(i, freq[i]));}while(1){pNode left = getMinNode(forest, FOREST_SIZE);pNode right = getMinNode(forest, FOREST_SIZE);if(right == NULL) {//仅有一个节点,合并结束 root = left;break; } else {pNode pathNode = mergeNode(left->weight + right->weight);pathNode->left = left;pathNode->right = right;addToForest(forest, FOREST_SIZE, pathNode);}}genHuffCode(root);return root;
}static int isLeaf(pNode node){return node->left == NULL && node->right == NULL;
}static void inOrder(pNode curNode){if(curNode){inOrder(curNode->left);if(isLeaf(curNode))printf("字符:%c  权重:%d   编码:%s \n", curNode->val, curNode->weight, curNode->code);inOrder(curNode->right);}
}static void printHuffTreeCode(pNode node){inOrder(node);
}static void transHuffTable(pNode HuffTable[], pNode curNode){if(curNode){if(isLeaf(curNode))HuffTable[curNode->val] = curNode;transHuffTable(HuffTable, curNode->left);transHuffTable(HuffTable, curNode->right);}
}static char* doEncode(char *orgData, int orgLen, const pNode root){pNode HuffTable[LIST_SIZE] = {NULL};transHuffTable(HuffTable, root);//这里写的不好,为了节省空间采取了一个笨办法 int i;int totalSize = 0;for(i=0; i<orgLen; i++)totalSize += strlen(HuffTable[orgData[i]]->code);printf("\n霍夫曼编码长度:%d", totalSize);char* HuffCode = (char *)malloc(sizeof(char) * (totalSize+1));memset(HuffCode, 0, totalSize+1);for(i=0; i<orgLen; i++)strcat(HuffCode, HuffTable[orgData[i]]->code);return HuffCode;
}static void doReleaseTree(pNode curNode){if(curNode){doReleaseTree(curNode->left);doReleaseTree(curNode->right);}
}char* Encode(char *orgData, int orgLen, pNode root){if(orgData == NULL || orgLen == 0){printf("编码入参错误!\n");return NULL;}if(orgLen == 1){printf("仅有一个字符,不用编码!\n");return NULL;}int i;printf("输入数据:%s\n", orgData);//(1)统计权重 int freq[LIST_SIZE];memset(freq, 0, sizeof(int) * LIST_SIZE);for(i=0; i<orgLen; i++){freq[orgData[i]]++;}//(2)构建Huffman树//C的传参比较繁琐,我不想为了代码看起来漂亮,给buildHuffTree再添加一个引用参数//所以采用了类似深拷贝的做法 pNode tmpTree = buildHuffTree(freq, LIST_SIZE);root->val = tmpTree->val;root->weight = tmpTree->weight;root->left = tmpTree->left;root->right = tmpTree->right;free(tmpTree);printf("\n字符的霍夫曼编码信息如下:\n");printHuffTreeCode(root); //(3)得到编码结果 char* ret = doEncode(orgData, orgLen, root);return ret;
}char* Decode(char *codeData, int codeLen, pNode root){if(codeData == NULL || codeLen == 0 || root == NULL){printf("\n解码入参错误!\n");return NULL;}if(codeLen == 1){printf("仅有一个字符,不用解码!\n");return NULL;}//此处也是一个笨办法,应该去看论文估计一下长度 char* text = (char *)malloc(sizeof(char) * (TEXT_MAX));memset(text, 0, TEXT_MAX);int i,j;pNode curNode = root;//按照之前思路模拟实现,虽然贴近人的自然思维,但是效率不高 for(i=0, j=0; i<codeLen; i++){curNode = codeData[i] == '0' ? curNode->left : curNode->right;if(isLeaf(curNode)){text[j++] = curNode->val;curNode = root;}}return text;
}void releaseTree(pNode root){doReleaseTree(root);
}

5-18-4 测试

(1)测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "HuffmanTree.h"int main(int argc, char *argv[]) {char test1[] = {'A', 'B', 'A', 'A', 'C', 'D', 'C'}; //ABAACDCstruct TreeNode huffTree1;char* huffEncode1 = Encode(test1, strlen(test1), &huffTree1);printf("\n\n霍夫曼编码结果: %s", huffEncode1);char* huffDecode1 = Decode(huffEncode1, strlen(huffEncode1), &huffTree1);printf("\n\n霍夫曼译码结果: %s", huffDecode1);printf("\n=======================================\n");//write file namechar test2[] = {'w', 'r', 'i', 't', 'e', ' ', 'f', 'i', 'l', 'e', ' ', 'n', 'a', 'm', 'e'}; struct TreeNode huffTree2;char* huffEncode2 = Encode(test2, strlen(test2), &huffTree2);printf("\n\n霍夫曼编码结果: %s", huffEncode2);char* huffDecode2 = Decode(huffEncode2, strlen(huffEncode2), &huffTree2); printf("\n\n霍夫曼译码结果: %s", huffDecode2);printf("\n=======================================\n");return 0;
}

(2)测试结果
在这里插入图片描述

【遗憾】
(1)正如前面讨论的,这个程序是参考[3]修修补补完成的,当然也可以参考[3]和[4]去做成完整版的利用Huffman编码对文件进行压缩和解压的经典案例(重点在于能让大家观察到压缩/解压的操作的时间和空间效率,真正做到学以致用),
(2)对Huffman编解码的对应数学理论我们仅了解到皮毛,因此在具体编码中还有很多申请/释放空间的处理不够智慧,应该抽空研究一下真实开源案例,看看学习别人的实现手法。
(3)黑皮书里给出了相关论文,见[13],[14],[15],[16]。

以上问题,有空我们补一个帖子试试看。

【注】另外,很多Huffman教学贴中,编码实现阶段,为节点定义了一个指向父节点的指针,也不失为一种常见的解决方案,参看[4]

// 定义哈夫曼树节点
typedef struct {int weight;int parent;int l_child;int r_child;char data;
} HTNode, * HuffmanTree;
typedef char** HuffmanCode;

5-19 实际案例

(1)硬件编解码,参考 [6],[7],[8]
(2)其他参考[9],[10],[11],[12]

多媒体方向的水很深,看情况慢慢研究吧。

总结

(1)Huffman编码的应用非常广泛。
(2)Huffman编码是一种变长的编码,可配合类似树状的数据结构存储编码表。
(3)对于森林这个概念,我们没有介绍,直接在实践中学习。

【吐槽】C的传值解决方法不太优雅。

参考文献

[1] 信息与编码系列(一) 源码
[2] 信息与编码系列(二)最优码——Huffman码
[3] C语言实现Huffman的编码和解码 ==> 值得看,树形结构的打印不错,对文件的编解码也挺好
[4] C语言课程设计-文件的哈夫曼编码与解码 ==> 对时间和空间的统计展示值得借鉴
[5] 哈夫曼编码详细证明步骤
[6] 硬件huffman解码器(一):huffman编码原理
[7] 硬件huffman解码器(二):串行解码及其优化
[8] 硬件huffman解码器(三)-并行解码
[9] 语音处理:霍夫曼编码算法原理分析 ==> 提到了Jpeg
[10] MP3 和 AAC 中huffman解码原理,优化设计与参考代码中实现
[11] Android 性能优化 03—Bitmap优化02(huffman压缩)
[12] Android图片压缩原理分析(三)—— 哈夫曼压缩讲解 ==> Android Skia 图像引擎
[13] D. A. Huffman,“AMethod for the Construction of Minimum Redundancy Codes,” Proceedings of the IRE, 40 (1952),1098-1101. ==> 开山鼻祖
[14] D.E. Knuth, “Dynamic Huffman Coding,”Journal of Algorithms, 6 (1985),163-180.
[15] L.L. Larmore, “Height-Restricted Optimal Binary Trees,”SIAM Journal on Computing, 16 (1987),1115-1123.
[16] L. L. Larmoreand D.S. Hirschberg,“A Fast Algorithm for Optimal Length-Limited Huffman Codes,”Journal of the ACM, 37 (1990), 464-473.

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

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

相关文章

✌粤嵌—2024/4/11—合并区间

代码实现&#xff1a; /*** Return an array of arrays of size *returnSize.* The sizes of the arrays are returned as *returnColumnSizes array.* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().*/// 交换 void swap(i…

YOLOV5检测+追踪使用deepstream部署(c++版)

文章目录 一、Deepstream1.1 简介1.2 图架构&#xff08;Graph architecture&#xff09;1.3 应用架构&#xff08;Application Architecture&#xff09; 二、配置文件方式运行Deepstream2.1 环境准备2.2 主机运行2.3 配置文件解析2.4 docker运行 三、代码方式运行Deepstream3…

Python编程之旅:深入探索强大的容器——列表

在Python编程的世界中&#xff0c;容器&#xff08;Containers&#xff09;是一种用于存储多个项目的数据结构。其中&#xff0c;列表&#xff08;List&#xff09;是最常用且功能强大的容器之一。无论是初学者还是资深开发者&#xff0c;掌握列表的使用方法和技巧都是提升Pyth…

【Android】重温Activity生命周期

前言 Android中用得最多的组件是Activity&#xff0c;而它的生命周期也是最基础的知识&#xff0c;从刚接触Android到工作中会频繁依赖这部分知识。可能大多数人能说出页面新建到页面关闭会走的生命周期&#xff1a;onCreate、onStart、onResume、onPause、onStop、onDestory&…

Linux --- 高级IO

目录 1. 什么是IO 2. 阻塞的本质 3. 五种IO模型 3.1. 通过故事认识五种IO模型 3.2. 上述故事的总结 3.3. 具体的五种IO模型 3.3.1. 阻塞IO 3.3.2. 非阻塞轮询式IO 3.3.3. 信号驱动IO 3.3.4. 多路转接IO 3.3.5. 异步IO 4. 非阻塞IO 4.1. fcntl 系统调用 1. 什么是I…

抖店如何打造一款爆品?按照这三个阶段做,爆单很简单

大家好&#xff0c;我是电商笨笨熊 做抖音小店不懂得怎么选品&#xff0c;那还怎么出销量&#xff1f; 选品时很多新手最苦恼的问题&#xff0c;不知道从哪里选品更容易选中爆品、不懂得什么样的品才是爆品&#xff0c;更不懂得如何打造一款爆品。 那么今天&#xff0c;我们…

创建影子用户

文章目录 1.认识影子用户2.创建隐藏账户并加入管理员组3.修改注册表3.删除用户4.添加管理员权限 1.认识影子用户 影子用户通常指的是那些在系统用户列表中不可见&#xff0c;但在某些情况下可以进行操作的用户。在内网渗透过程中&#xff0c;当我们拿到shell时&#xff0c;肯定…

android11 如何修改状态栏的背景

修改status_bar.xml &#xff1a; <LinearLayout android:id"id/status_bar_contents"android:background"#1ABC9C"android:layout_width"match_parent"android:layout_height"match_parent"android:paddingStart"dimen/statu…

【重回王座】ChatGPT发布最新模型gpt-4-turbo-2024-04-09

今天&#xff0c;新版GPT-4 Turbo再次在大型模型排行榜上荣登榜首&#xff0c;成功超越了此前领先的Claude 3 Opus。另外&#xff0c;新模型在处理长达64k的上下文时&#xff0c;性能竟能够与旧版在处理26k上下文时的表现相当。 目前GPT-4 Turbo仅限于ChatGPT Plus的用户&…

Nginx服务 重写功能与反向代理

六、重写功能 rewrite Nginx服务器利用 ngx_http_rewrite_module 模块解析和处理rewrite请求&#xff0c;此功能依靠 PCRE(perl compatible regular expression)&#xff0c;因此编译之前要安装PCRE库&#xff0c;rewrite是nginx服务器的重要功能之一&#xff0c;用于实现URL的…

DBA面试总结(Mysql篇)

一、delete与trancate的区别 相同点 1.两者都是删除表中的数据&#xff0c;不删除表结构 不同点 1.delete支持按条件删除&#xff0c;TRUNCATE不支持。 2.delete 删除后自增列不会重置&#xff0c;而TRUNCATE会被重置。 3.delete是逐条删除&#xff08;速度较慢&#xff09…

【linux编译报错】g++: error:elf_x86_64:没有那个文件或目录

背景 gcc版本已经是高版本了&#xff0c;9开头了&#xff0c;但是在IDE编译的时候报错&#xff1a; 但是记得自己没有配置过这种参数&#xff0c;只能一步步查了 解决方法 步骤1&#xff1a;先google看了下别人是否碰到该问题 找到一个解决方法说&#xff1a; 在Makefile中…

配置路由器实现互通

1.实验环境 实验用具包括两台路由器(或交换机)&#xff0c;一根双绞线缆&#xff0c;一台PC&#xff0c;一条Console 线缆。 2.需求描述 如图6.14 所示&#xff0c;将两台路由器的F0/0 接口相连&#xff0c;通过一台PC 连接设备的 Console 端口并配置P地址&#xff08;192.1…

如何在CentOS安装Firefox并结合内网穿透工具实现公网访问本地火狐浏览器

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器&#xff0c;由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

win11电脑驱动怎么更新,windows11更新驱动

驱动是指计算机里软件的程序,硬件的运作离不开驱动的支持,因为驱动就是使得硬件和电脑系统沟通的桥梁。既然驱动如此重要,那么不装肯定不行,如果有问题,也要及时地修复和更新。最近,有位win11用户,想要了解win11电脑驱动怎么更新?接下来,教程会带来两种更新win11驱动的…

LlamaIndex 组件 - Loading

文章目录 一、概览加载Transformations将所有内容放在一起抽象 二、文档/节点概览1、概念2、使用模式文件节点 三、定义和定制文档1、定义文档2、自定义文档2.1 元数据2.2 自定义id2.3 高级 - 元数据定制1&#xff09;自定义LLM元数据文本2&#xff09;自定义嵌入元数据文本3&a…

【数据结构与算法】最大公约数与最小公倍数

最大公因数&#xff08;英语&#xff1a;highest common factor&#xff0c;hcf&#xff09;也称最大公约数&#xff08;英语&#xff1a;greatest common divisor&#xff0c;gcd&#xff09;是数学词汇&#xff0c;指能够整除多个非零整数的最大正整数。例如8和12的最大公因数…

【Java探索之旅】数组使用 初探JVM内存布局

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、数组的使用1.1 元素访问1.2 数组遍历 二、JVM的内存布局&#x1f324;️全篇总结 …

WP免费主题下载

免费wordpress模板下载 高端大气上档次的免费wordpress主题&#xff0c;首页大图全屏显示经典风格的wordpress主题。 https://www.wpniu.com/themes/289.html 免费WP主题 蓝色简洁实用的wordpress免费主题模板&#xff0c;免费主题资源分享给大家。 https://www.wpniu.com/…

基于Springboot+Vue的Java项目-校园管理系统(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…