C/C++ 数据结构与算法【哈夫曼树】 哈夫曼树详细解析【日常学习,考研必备】带图+详细代码

news2024/12/29 1:46:27

哈夫曼树(最优二叉树)

1)基础概念

**路径:**从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。

**结点的路径长度:**两结点间路径上的分支数。

在这里插入图片描述

**树的路径长度:**从树根到每一个结点的路径长度之和。记作:TL。

在这里插入图片描述

结点数目相同的二叉树中,完全二叉树是路径长度最短的二叉树。

**权:**将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。

**结点的带权路径长度:**从根结点到该结点之间的路径长度与该结点的权的乘积。

**树的带权路径长度:**树中所有叶子结点的带权路径长度之和。

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

2)构造哈夫曼树

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

顺序存储结构——一维结构数组

(1)定义结构

在这里插入图片描述

typedef struct {
	int weight; 
	int parent, lch, rch;
}HTNode, * HuffmanTree;

HuffmanTree H;
(2)步骤:

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

  1. 初始化HT [1…2n-1]:lch = rch = parent = 0;
  2. 输入初始几个叶子结点:置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 = 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为0
    for (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为0
    for (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为0
    for (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*n
        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;
}
(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的左孩子,则生成代码0
            else
                cd[start] = '1'; // 结点c是f的右孩子,则生成代码1

            c = 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的左孩子,则生成代码0
            else
                code = '1' + code; // 结点c是f的右孩子,则生成代码1

            c = f;
            f = HT[f].parent;
        }

        HC[i] = code;
    }
}
总代码实现:

在这里插入图片描述

#include <iostream>
#include <cstring> // 用于 strcpy 和 strlen
#include <limits>  // 用于 std::numeric_limits

using 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为0
    for (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的左孩子,则生成代码0
            else
                cd[start] = '1'; // 结点c是f的右孩子,则生成代码1

            c = 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.0
    HT.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的左孩子,则生成代码0
            else
                code = '1' + code; // 结点c是f的右孩子,则生成代码1

            c = 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_limits

using 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为0
    for (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的左孩子,则生成代码0
            else
                code = '1' + code; // 结点c是f的右孩子,则生成代码1

            c = 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;
}
运行结果:

在这里插入图片描述

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

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

相关文章

2、C#基于.net framework的应用开发实战编程 - 设计(二、三) - 编程手把手系列文章...

二、设计&#xff1b; 二&#xff0e;三、构建数据库&#xff1b; 此例子使用的是SQLite数据库&#xff0c;所以数据库工具用的SQLiteStudio x64&#xff0c;这个是SQLite专用的数据库设计管理工具&#xff0c;其它的数据库管理工具比如DBeaver的使用请见实战工具系列文章。 1、…

Edge SCDN酷盾安全重塑高效安全内容分发新生态

在数字化浪潮不断推进的今天&#xff0c;互联网内容的分发效率与安全性已成为企业业务发展的关键要素。酷盾安全推出的Edge Secure Content Delivery Network&#xff08;Edge SCDN&#xff09;&#xff0c;不仅集成了分布式DDoS防护、CC防护、WAF防护及BOT行为智能分析等安全加…

JAVA HTTP压缩数据

/*** 压缩数据包** param code* param data* param resp* throws IOException*/protected void writeZipResult(int code, Object data, HttpServletResponse resp) throws IOException {resp.setHeader("Content-Encoding", "gzip");// write到客户端resp…

工厂+策略模式之最佳实践(疾病报卡维护模块API设计)

目录 &#x1f4bb;业务场景 &#x1f527;应用技术 ⚙概要流程 ❗开发注意 服务类上标注了 自定义注解 却无法直接利用getDeclaredAnnotation 获取 *Spring代理机制 代理机制的工作原理 代理的工作机制 代理的使用场景 已获取EmrXXXServiceImpl 的Class&#xff0c;如…

帧缓存的分配

帧缓存实际上就是一块内存。在 Android 系统中分配与回收帧缓存&#xff0c;使用的是一个叫 ION 的内核模块&#xff0c;App 使用 ioctl 系统调用后&#xff0c;会在内核内存中分配一块符合要求的内存&#xff0c;用户态会拿到一个 fd&#xff08;有的地方也称之为 handle&…

StarRocks元数据无法合并

一、先说结论 如果您的StarRocks版本在3.1.4及以下&#xff0c;并且使用了metadata_journal_skip_bad_journal_ids来跳过某个异常的journal&#xff0c;结果之后就出现了FE的元数据无法进行Checkpoint的现象&#xff0c;那么选择升级版本到3.1.4以上&#xff0c;就可以解决。 …

图像处理-Ch2-空间域的图像增强

Ch2 空间域的图像增强 文章目录 Ch2 空间域的图像增强Background灰度变换函数(Gray-level Transformation)对数变换(Logarithmic)幂律变换(Power-Law)分段线性变换函数(Piecewise-Linear)对比度拉伸(Contrast-Stretching)灰度级分层(Gray-level Slicing) 直方图处理(Histogram …

c# 2024/12/27 周五

6《详解类型、变量与对象》36 详解类型、变量与对象 _1_哔哩哔哩_bilibili

云手机+Facebook:让科技与娱乐完美结合

移动互联网时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现&#xff0c;则为Facebook营销注入了新的活力&#xff0c;其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…

【蓝桥杯——物联网设计与开发】拓展模块4 - 脉冲模块

目录 一、脉冲模块 &#xff08;1&#xff09;资源介绍 &#x1f505;原理图 &#x1f505;采集原理 &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#xff09;实验现象 二、脉冲模块接口函数封装 三、踩坑日记 &a…

【再谈设计模式】享元模式~对象共享的优化妙手

一、引言 在软件开发过程中&#xff0c;我们常常面临着创建大量细粒度对象的情况&#xff0c;这可能会导致内存占用过高、性能下降等问题。享元模式&#xff08;Flyweight Pattern&#xff09;就像是一位空间管理大师&#xff0c;它能够在不影响功能的前提下&#xff0c;有效地…

Unity 读Excel,读取xlsx文件解决方案

Unity读取表格数据 效果&#xff1a; 思路&#xff1a; Unity可以解析Json&#xff0c;但是读取Excel需要插件的帮助&#xff0c;那就把这个功能分离开&#xff0c;读表插件就只管读表转Json&#xff0c;Unity就只管Json解析&#xff0c;中间需要一个存储空间&#xff0c;使用…

WebRTC服务质量(11)- Pacer机制(03) IntervalBudget

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

STM32完全学习——FATFS0.15移植SD卡

一、下载FATFS源码 大家都知道使用CubMAX可以很快的将&#xff0c;FATFS文件管理系统移植到单片机上&#xff0c;但是别的芯片没有这么好用的工具&#xff0c;就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题&#xff0c;然后再移植FATFS文件管理系统。 二、SD…

Ollama+OpenWebUI+llama3本地部署

引言 llama3在4月19日刚刚发布&#xff0c;官方的对比结果中在开源模型中堪称世界第一&#xff0c;整好周六日有时间&#xff0c;在魔搭社区上测试一下 2 安装Ollama 2.1 下载Ollama 登录Ollama官网下载Ollama安装包 GitHub&#xff1a;https://github.com/ollama/ollama?t…

新品:SA628F39大功率全双工音频传输模块

SA628F39是一款高集成度的8W大功率全双工无线数据语音一体通话模块&#xff0c;专为高效、稳定的远程通信设计。该模块内置高速微控制器、高性能射频芯片、功率放大器、ESD静电保护和硬件看门狗芯片&#xff0c;具备反接保护、过流过压保护和防死机保护等多重安全功能&#xff…

Unity EasyAR入门教程

文章目录 Easy不用下载盗版安装SDK注册应用ID最简单的识别注意点 Easy不用下载盗版 EasyAR可以免费试用&#xff0c;如果不在乎水印&#xff0c;直接去官网下载即可 EasyAR官网 安装SDK 先下载&#xff0c;官网找到下载页&#xff0c;如下选择 下载后不是普通的unityPackag…

ID卡网络读卡器Python小程序开发

UDP网络读卡器Python示例代码。 主页UI设计界面&#xff1a; UI代码&#xff1a; from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Frame(object):def setupUi(self, Frame):Frame.setObjectName("Frame")Frame.resize(1082, 685)font QtGui.QFont()font.set…

yarn list --pattern vuex-module-decorators

dgqdgqdeMac-mini spid-admin % yarn list --pattern vuex-module-decorators yarn list v1.22.22 └─ vuex-module-decorators0.16.1 ✨ Done in 0.24s.好的&#xff0c;这段代码是一个典型的 Vuex 模块定义&#xff0c;使用了 vuex-module-decorators 库。这个库为 Vuex 提…

微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)Proteus中Unknown 1-byte opcode / Unknown 2-byte opcode错误

原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …