哈夫曼树及哈夫曼编码详解及代码实现[C/C++]

news2024/11/17 15:34:30

文章目录

  • 哈夫曼树及哈夫曼编码
    • 哈夫曼树的定义与原理
      • 引例
      • 路径长度
        • 定义
      • 带权路径长度
      • 哈夫曼树
      • 哈夫曼树的构造方法
        • 构造方法示例
    • 哈夫曼树的代码实现
      • 哈夫曼树的结点定义
      • 哈夫曼树的构造
    • 哈夫曼编码
      • 哈夫曼编码方式
      • 哈夫曼编码解码的准确性
    • 哈夫曼编码的代码实现
      • 运行示例

哈夫曼树及哈夫曼编码

在日常的网络办公中,文件压缩是一种常见的节省空间提高效率的操作,那么这种压缩技术是如何实现的呢?其实就是对我们要传输的文本进行重新编码,以减少不必要的空间。。尽管现在最新技术在编码上已经很好很强大,但这一切都来自于曾经的技术积累,我们今天就来介绍-一下最基本的压缩编码方法——哈夫曼编码。

哈夫曼树的定义与原理

1951年,哈夫曼MIT信息论课程的导师Robert M. Fano给他们两个选择,一是完成学期报告的题目寻找最有效的二进制编码二是完成期末考试。只能说大佬不愧是大佬,毅然决然选择了第一种方式,弃对已有编码的研究,转向新的探索,最终发现了基于有序频率二叉树编码的想法并很快证明了这个方法是最有效的。由于这个算法,学生终于青出于蓝,超过了他那曾经和信息论创立者香农共同研究过类似编码的导师。

为了更好地接受哈夫曼树的概念,我们来看下面这个引例。

引例

过去我们小学、中学一般考试都是用百分制来表示学科成绩的。但为了避免以分取人的现象,如今很多学科特别是小学学科学科成绩都改作了优秀、良好、中等、及格和不及格这样模糊的词语。

不过对于老师来讲,他在对试卷评分的时候,显然不能凭感觉给优良或及格不及格等成绩,因此一般都还是按照百分制算出每个学生的成绩后,再根据统一的标准换算得出五级分制的成绩。比如下面的代码就实现了这样的转换。

if (a < 60)
b="不及格”;
else if (a < 70)
b="及格“;
else if (a < 80)
b="中等" ;
else if (a < 90)
b="良好“;
else
b="优秀" ;

下图粗略看没什么问题,可是通常都认为,一张好的考卷应该是让学生成绩大部分处于中等或良好的范围,优秀和不及格都应该较少才对。而上面这样的程序,就使得所有的成绩都需要先判断是否及格,再逐级而上得到结果。输入量很大的时候,其实算法是有效率问题的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果在实际的学习生活中,学生的成绩在5个等级上的分布规律如下表所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于大于70分共80%的成绩都要经过至少三次判断才可以得到结果,这显然不合理,那么有没有好一些的办法呢?

我们有一个思想叫做加速大概率时间(make the common fast),我们发现中等成绩(7079)的比例最高,其次是良好(8089),那么我们基于加速大概率事件的思想,把上图二叉树重新分配,变成如下图的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如此一来,我们的效率应该要高一些了,但是这种二叉树是如何构造出来的呢?

路径长度

定义

从树中一个结点到另一个结点之间的分支构成两个结点之间的路径,路径上的分支数目称做路径长度树的路径长度即树根到每一结点的路径长度之和

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如上图左边二叉树根到D的路径长度就是4,右边二叉树根到D路径长度为2.

左树的路径长度为1+1+2+2+3+3+4+4=20,右树的路径长度为1+2+3+3+2+1+2+2=16。

带权路径长度

**结点的带权路径长度(WPL)**为从该节点到树根之间的路径长度与结点上权的乘积。

哈夫曼树

带权路径长度(WPL)最小的二叉树称做哈夫曼树(Huffman Tree),不少书中也称为最优二叉树。

现在有了带权路径长度(WPL)的定义,我们计算下上图两颗二叉树的WPL值。

左树WPL:5*1 + 15*2 + 40*3 + 30*4 +10*4 = 315
右树WPL:5*3 + 15*2 + 40*2 + 30*2 +10*2 = 220

这意味着什么?
我们如果有10000个学生的百分制成绩需要转化为五级分制成绩的话,用左树的方法要做315 * 10000 * 0.01 = 31500次,对于右树只需要22000次!性能大大提升

哈夫曼树的构造方法

对于哈夫曼树构造方法的正确性很多教材书上都有,有兴趣的自行查阅,这里仅给出构造方法。

(1)根据给定的n个权值{ w1,w2,……wn }构成n棵二叉树的集合F=(T1、T2……Tn},其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树均为空。
(2)在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且新的二叉树的根结点的权值为左右子树上根结点的权值之和
(3)在F中删除这两棵树,同时将新得到的二叉树加入F中。
(4)重复步骤(2)和(3),直到F只含一棵树为止。这棵树便是哈夫曼树。

构造方法示例

先以上图左树给出示例
(1)先把有权值的叶子结点按照从小到大的顺序排列成一个有序序列,即A5,E10,B15,D30,C40。
(2)取头两个最小权值的结点作为一个新结点N1的两个子结点,注意相对较小的是左孩子,这里就是A为N1的左孩子,E为N1的右孩子,如下图所示。新结点的权值为两个叶子权值的和5+10=15。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)将N1替换A与E,插入有序序列中,保持从小到大排列。即N1,15。 B1S,D30,C40。
(4)重复步骤(2)。将N1与B作为一个新结点N2的两个子结点。如下图所示。N2的权值=15+15=30.
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(5)将N2替换N1与B,插入有序序列中,保持从小到大排列。即N2,30, D30, C40。
(6)重复步骤(2)。将N2与D作为一个新结点N3的两个子结点。如下图所示。N3的权值=30+30=60. .

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(7)将N3替换N2与D,插入有序序列中,保持从小到大排列。即C40,,N3,60。
(8)重复步骤(2)。将C与N3作为一个新结点T的两个子结点,如左下图所示。由于T即是根结点,完成哈夫曼树的构造。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此时的左上图二叉树的带权路径长度WPL=40x1+30x2+15x3+10x4+5x4=205.与右上图二叉树的WPL=5x3+15x3+40x2+30x2+10x2= =220相比,还少了15。显然此时构造出来的二叉树才是最优的哈夫曼树。

哈夫曼树的代码实现

哈夫曼树的结点定义

template <class T = double>
struct HuffmanTreeNode
{
    HuffmanTreeNode(const T &w = T(), int l = 0, int r = 0, int p = 0) : _weight(w), _left(l), _right(r), _parent(p)
    {
    }
    T _weight;//权值
    int _left;//左孩子
    int _right;//右孩子
    int _parent;//父节点
};

哈夫曼树的构造

我们初始有n个叶子结点,构建过程中每次减少一个结点,最后只剩一个结点,也就是说我们增加了n - 1个扩展结点,最后一共有2 * n - 1个结点。

我们选择用数组来实现这一树形结构,由于有2 * n - 1个结点,我们开2 * n空间的数组,多出来的一个位置作为叶子节点的空指针域

对于每次取两个最小权值叶子结点的操作,我们可以把所有叶子结点放入小根堆(用C++的priority_queue)内,每次弹出两个结点,再将新结点压入,这样即保证了每次取出两个最小权值的结点又保证了不会取到扩展结点的叶子结点

哈夫曼树构造函数代码如下:

    HuffmanTree(int n, const vector<T> &weights)
    {
        _HT.resize(2 * n); // 开辟2 * n的空间
        int i = 0, newweight = 0, l = 0, r = 0;

        function<bool(const pair<T, int> &, const pair<T, int> &)> cmp = [&n](const pair<T, int> &p1, const pair<T, int> &p2) -> bool
        {
            return p1.first > p2.first || ((p1.first == p2.first) && ((p1.second > n || p2.second > n) && p1.second < p2.second)) || ((p1.first == p2.first) && ((p1.second <= n && p2.second <= n) && p1.second > p2.second));
        };//这里比较条件保证小根堆,如果权值相同,如果是扩展结点那么扩展结点优先下标大的在前,两个不含扩展结点,那么下标小的优先在前
        //由于哈夫曼树不是唯一的,所以这里的判断条件也不是唯一的,只是我们和我们的示例吻合

        priority_queue<pair<T, int>, vector<pair<T, int>>, function<bool(const pair<T, int> &, const pair<T, int> &)>> pq(cmp);
        for (i = 0; i < n; i++)
        {
            _HT[i + 1] = Node(weights[i]);
            pq.emplace(make_pair(weights[i], i + 1));
        } // 先把给定的叶子节点都放入小根堆内
        for (i = 1; i < n; i++)
        {
            newweight = 0;
            l = pq.top().second;
            newweight += pq.top().first;
            pq.pop();
            r = pq.top().second;
            newweight += pq.top().first;
            pq.pop();
            _HT[n + i] = Node(newweight, l, r); // newweight即为扩展结点的权值,孩子分别为l和r

            _HT[l]._parent = _HT[r]._parent = n + i; // 把孩子结点的_parent置位n + i
            pq.emplace(make_pair(newweight, n + i)); // 把新结点放入小根堆内
        }
        // 打印我们哈夫曼树的信息,验证我们哈夫曼树是否正确
        printf("HuffmanTree各结点信息:\n");
        printf("%-6s%-8s%-8s%-8s%-9s\n", "下标", "权值", "左孩子", "右孩子", "父亲");

        for (int i = 1; i < n * 2; i++)
        {
            printf("%-6d%-8.2lf%-8d%-8d%-8d\n", i, _HT[i]._weight, _HT[i]._left, _HT[i]._right, _HT[i]._parent);
        }
    }

哈夫曼编码

当然,哈夫曼研究这种最优树的目的不是为了我们可以转化一下成绩。他的更大目的是为了解决当年远距离通信(主要是电报)的数据传输的最优化问题。

比如我们有一段文字内容“BADCADFEED”要通过网络传输给别人,显然用二进制的数字(0和1)来表示是很自然的想法。现在这段文字只有六个字母A、B、C、D、E、F,那么我们可以用相应的二进制字符表示,如下表所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样真正传输的数据就是编码后的“001000011010000011101100100011”,对方接收时可以按照三位一分来译码。如果一篇文章很长,这样的二进制串也将非常的可怕。但任何语言文本中,不同字符的出现频率显然不同。

仍然是基于加速大概率事件的思想,我们假设六个字母频率为A27,B8,C15,D 15, E30,F5,合起来刚好100%。那就意味着,我们完全可以重新按照哈夫曼树来规划它们。

哈夫曼编码方式

我们先以频率为权值建立哈夫曼树,然后将每个结点的左分支权值设置为0,右分支设置为1,就得到了如下哈夫曼树

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此时,我们对这六个字母用其从树根到叶子所经过路径的0或1来编码,可以得到如下表所示这样的定义。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们将文字内容“BADCADFEED”再次编码,对比可以看到结果串变小了。

原编码二进制串: 001000011010000011101100100011 (共30个字符)
新编码二进制串: 1001010010101001000111100 (共25个字符)

也就是说,我们的数据被压缩了,节约了大约17%的存储或传输成本。随着字符的增加和多字符权重的不同,这种压缩会更加显出其优势。

哈夫曼编码解码的准确性

当我们接收到10010100101010001011100这样压缩过的新编码时,我们应该如何把它解码出来呢?
编码中非0即1,长短不等的话其实是很容易混淆的,所以若要设计长短不等的编码,则必须是任一字符的编码都不是另一 个字符的编码的前缀,这种编码称做前缀编码

你仔细观察就会发现,上表中的编码就不存在容易与1001、1000混淆的“10” 和“100”编码。我们哈夫曼编码的解码具有优越的准确性。
可仅仅是这样是不足以让我们去方便地解码的,因此在解码时,还是要用到哈夫曼树,即发送方和接收方必须要约定好同样的哈夫曼编码规则。
当我们接收到10010100101010000111100时,由约定好的哈夫曼树可知,1001得到第一个字母是B,接下来01意味着第二个字符是A,如下图所示,其余的也相应的可以得到,从而成功解码。

一般地, 设需要编码的字符集为{ d1,d2,……,dn },各个字符在电文中出现的次数或频率集合为{ W1,W2……,Wn },以d1,d2……dn作为叶子结点,以w1,w2……wn作为相应叶子结点的权值来构造一棵哈夫曼树。规定哈夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,这就是哈夫曼编码

哈夫曼编码的代码实现

对于我们的编码长度一定不大于我们叶子节点的个数,也就是说我们开一个长度为n的字符串来存储我们的Huffman编码即可

我们知道叶子节点的下标,从叶子结点往上找parent得到的编码是逆序的,所以我们用一个指针start从后往前存放编码

最终的编码就是code.substr(start + 1 , len)(由于从start + 1到末尾都是编码所以len可以省略)

代码如下:

    void HuffmanCode()
    {
        int n = _HT.size() / 2; //_HF开了2 * n个空间
        vector<string> codes;
        string code(n, '\0');
        int i = 1, start = n - 1, parent = 0, child = 0;

        for (i = 1; i <= n; i++)
        {
            start = n - 1;
            child = i;
            parent = _HT[child]._parent;
            while (parent)
            {
                if (_HT[parent]._left == child)
                    code[start--] = '0';
                else
                    code[start--] = '1';
                child = parent;
                parent = _HT[parent]._parent;
            }
            codes.emplace_back(code.substr(start + 1));
        }

        printf("各数据的Huffman编码为:\n");
        for (i = 0; i < n; i++)
        {
            cout << "数据" << i + 1 << ":" << codes[i] << endl;
        }
    }

运行示例

我们以下图为例,展示代码运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <queue>
using namespace std;
template <class T = double>
struct HuffmanTreeNode
{
    HuffmanTreeNode(const T &w = T(), int l = 0, int r = 0, int p = 0) : _weight(w), _left(l), _right(r), _parent(p)
    {
    }
    T _weight;   // 权值
    int _left;   // 左孩子
    int _right;  // 右孩子
    int _parent; // 父节点
};
template <class T = double>
class HuffmanTree
{
private:
    typedef HuffmanTreeNode<T> Node;

public:
    HuffmanTree(int n, const vector<T> &weights)
    {
        _HT.resize(2 * n); // 开辟2 * n的空间
        int i = 0, newweight = 0, l = 0, r = 0;

        function<bool(const pair<T, int> &, const pair<T, int> &)> cmp = [&n](const pair<T, int> &p1, const pair<T, int> &p2) -> bool
        {
            return p1.first > p2.first || ((p1.first == p2.first) && ((p1.second > n || p2.second > n) && p1.second < p2.second)) || ((p1.first == p2.first) && ((p1.second <= n && p2.second <= n) && p1.second > p2.second));
        }; // 这里比较条件保证小根堆,如果权值相同,如果是扩展结点那么扩展结点优先下标大的在前,两个不含扩展结点,那么下标小的优先在前

        priority_queue<pair<T, int>, vector<pair<T, int>>, function<bool(const pair<T, int> &, const pair<T, int> &)>> pq(cmp);
        for (i = 0; i < n; i++)
        {
            _HT[i + 1] = Node(weights[i]);
            pq.emplace(make_pair(weights[i], i + 1));
        } // 先把给定的叶子节点都放入小根堆内
        for (i = 1; i < n; i++)
        {
            newweight = 0;
            l = pq.top().second;
            newweight += pq.top().first;
            pq.pop();
            r = pq.top().second;
            newweight += pq.top().first;
            pq.pop();
            _HT[n + i] = Node(newweight, l, r); // newweight即为扩展结点的权值,孩子分别为l和r

            _HT[l]._parent = _HT[r]._parent = n + i; // 把孩子结点的_parent置位n + i
            pq.emplace(make_pair(newweight, n + i)); // 把新结点放入小根堆内
        }
        // 打印我们哈夫曼树的信息,验证我们哈夫曼树是否正确
        printf("HuffmanTree各结点信息:\n");
        printf("%-6s%-8s%-8s%-8s%-9s\n", "下标", "权值", "左孩子", "右孩子", "父亲");

        for (int i = 1; i < n * 2; i++)
        {
            printf("%-6d%-8.2lf%-8d%-8d%-8d\n", i, _HT[i]._weight, _HT[i]._left, _HT[i]._right, _HT[i]._parent);
        }
    }

    void HuffmanCode()
    {
        int n = _HT.size() / 2; //_HF开了2 * n个空间
        vector<string> codes;
        string code(n, '\0');
        int i = 1, start = n - 1, parent = 0, child = 0;

        for (i = 1; i <= n; i++)
        {
            start = n - 1;
            child = i;
            parent = _HT[child]._parent;
            while (parent)
            {
                if (_HT[parent]._left == child)
                    code[start--] = '0';
                else
                    code[start--] = '1';
                child = parent;
                parent = _HT[parent]._parent;
            }
            codes.emplace_back(code.substr(start + 1));
        }

        printf("各数据的Huffman编码为:\n");
        for (i = 0; i < n; i++)
        {
            cout << "数据" << i + 1 << ":" << codes[i] << endl;
        }
    }

private:
    vector<Node> _HT;
};
int main()
{
    vector<double> weights{27, 8, 15, 15, 30, 5};
    HuffmanTree<double> ht(6, weights);
    ht.HuffmanCode();
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

和我们上图中的信息不能说一模一样,只能说毫无差别(

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

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

相关文章

Android绑定式服务

Github:https://github.com/MADMAX110/Odometer 启动式服务对于后台操作很合适&#xff0c;不过需要一个更有交互性的服务。 接下来构建这样一个应用&#xff1a; 1、创建一个绑定式服务的基本版本&#xff0c;名为OdometerService 我们要为它增加一个方法getDistance()&#x…

PDF编辑软件哪个好用?PDF文档怎么编辑修改内容

在生活中&#xff0c;为了方便文件传输&#xff0c;会将文件扫描或转换成PDF格式&#xff0c;但是如果需要编辑修改会比较麻烦&#xff0c;这时一款好用的PDF编辑器显得十分重要。今天就带大家了解下PDF编辑软件哪个好用&#xff0c;PDF文档怎么编辑修改内容。 一、PDF编辑软件…

【Android 从入门到出门】第一章:Android开发技能入门指南

&#x1f935;‍♂️ 个人主页&#xff1a;艾迦洼的个人主页 ✍&#x1f3fb;作者简介&#xff1a;后端程序猿 &#x1f604; 希望大家多多支持&#xff0c;如果文章对你有帮助的话&#xff0c;欢迎 &#x1f4ac;&#x1f44d;&#x1f3fb;&#x1f4c2; 目录 &#x1f44b;第…

C#(Csharp)我的基础教程(三)(我的菜鸟教程笔记)-控件对象与窗体容器集合(Control)的探究与学习

目录 1、窗体类&#xff08;主代码文件窗体设计器后台代码文件&#xff09;2、控件对象与窗体容器集合&#xff08;Control&#xff09; 1、窗体类&#xff08;主代码文件窗体设计器后台代码文件&#xff09; 主窗体对象的创建&#xff1a;在Program类里面&#xff1a; Appli…

Windows操作系统登录双因素认证解决方案 安当加密

实现Windows操作系统登录双因素认证&#xff0c;可以通过安当ASP身份认证平台进行设置。安当ASP身份认证平台提供了多种双因素认证方式&#xff0c;包括USB Key ID、OTP&#xff08;动态口令&#xff09;等&#xff0c;可以灵活选择和配置。 以下是使用安当ASP身份认证平台的US…

2023 家电行业品牌社媒营销洞察报告

过去三年的疫情&#xff0c;给人们的生活和消费方式都带来了极大的改变。直播电商、社交电商的兴起&#xff0c;也带动了传统线下家电零售商向线上渠道发展&#xff0c;传统大家电“重获新生”&#xff0c;新兴小家电“冉冉升起”&#xff0c;成为了业绩增长的新引擎。 2023年&…

c++视觉处理---拉普拉斯金字塔和高斯金字塔

拉普拉斯金字塔和高斯金字塔 拉普拉斯金字塔&#xff08;Laplacian Pyramid&#xff09;和高斯金字塔&#xff08;Gaussian Pyramid&#xff09;是两种不同类型的图像金字塔&#xff0c;用于多尺度图像处理和特征提取。它们之间的主要区别在于它们所包含的信息和用途&#xff…

ROS仿真软件Turtlebot-Gazebo的安装使用以及错误处理[机器人避障]

很多时候由于机器人价格比较贵&#xff0c;而且会因为环境因素、操作失误或者摔坏等&#xff0c;所以我们可以先在仿真软件上做测试&#xff0c;也可以避免这些问题&#xff0c;虽然没有那么真实感&#xff0c;可毕竟是免费的嘛。我们可以在这些仿真的机器人身上去学习如何控制…

格式化硬盘时提示“此驱动器正在使用”解决占用问题

Windows 11 或 Windows 10 的大多数问题都有一个很好的解决方案——格式化系统驱动器并重新安装 Windows。或者有时&#xff0c;我们可能只想擦除数据驱动器上的所有文件和文件夹&#xff0c;例如。D 盘、E 盘等。为此&#xff0c;我们只需右键单击 Windows 文件资源管理器的 P…

深度学习batch、batch_size、epoch、iteration以及小样本中episode、support set、query set关系

batch、batch_size、epoch、iteration关系&#xff1a; epoch&#xff1a;整个数据集 batch&#xff1a; 整个数据集分成多少小块进行训练 batch_size&#xff1a; 一次训练&#xff08;1 batch&#xff09;需要 batch_size个样本 iteration&#xff1a; 整个数据集需要用b…

深度学习简述

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

MT8390(Genio 700)安卓核心板参数_联发科MTK智能模组

MT8390安卓核心板是一款功能强大且高度集成的平台&#xff0c;专为广泛的人工智能(AI)和物联网(IoT)应用案例而设计。它具备高性能边缘处理、先进的多媒体和连接能力、多个高分辨率摄像头、连接的触摸屏显示以及多任务高级操作系统的使用。 MT8390安卓核心板采用了高性能的八核…

保护互联网数据安全:关键方法与最佳实践

在当今数字化时代&#xff0c;互联网数据安全已经成为个人、企业和组织的首要任务之一。随着信息技术的迅猛发展&#xff0c;网络威胁也不断演进&#xff0c;因此保护互联网数据安全变得尤为关键。本文将介绍一些关键方法和最佳实践&#xff0c;帮助您确保互联网数据的安全性。…

从零学算法(LCR 187. 破冰游戏)

社团共有 num 为成员参与破冰游戏&#xff0c;编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。社长抽取一个数字 target&#xff0c;从 0 号成员起开始计数&#xff0c;排在第 target 位的成员离开圆桌&#xff0c;且成员离开后从下一个成员开始计数。请返回游戏结束时最后…

第五节 C++ 循环结构(算法)

文章目录 前言介绍1. for 语句1.1 语法结构1.2 语法流程的执行过程1.2.1 案例 1:循环的正序输入和倒序输入1.2.2 案例2 : 求1~n的平方数1.2.3 案例 3: 求输入a和b,求a~b区间数. 1.3 for 循环案例练习1.3.1 求最大值与最小值1.3.2 计算奇数和和偶数和1.3.3 计算平均气温与最高气…

day03_if_循环

今日内容 1.if.elseif 2.循环 3.控制关键词 复习 一 运算符算术,赋值,关系,逻辑,三目!,非,取反的作用boolean r !true; //falseboolean r2 !(1 > 0);// false 二 键盘录入1) 导包 import java.util.Scanner;2) 创建扫描器 Scanner scanner new Scanner(System.in);3) 调…

3.5 高速以太网

思维导图&#xff1a; 3.5.1 100BASE-T以太网 **3.5 高速以太网** 随着技术进步&#xff0c;以太网的传输速度不断增加。从早期的10 Mbit/s的速度&#xff0c;增长到了1 Gbit/s甚至更快。以下是一些关于高速以太网技术的简介。 **3.5.1 100BASE-T以太网** - **定义**&#…

信息系统项目管理师第四版学习笔记——项目沟通管理

管理基础 沟通模型的关键要素包括&#xff1a;编码、信息和反馈信息、媒介、噪声、解码。 在编制书面或口头信息的时候&#xff0c;应使用书面沟通的5C原则&#xff0c;以减轻理解错误&#xff1a;正确的语法和拼写(Correctness)、简洁的表述(Concise)、清晰的目的和表述(Cla…

传统工厂如何搭建蒸汽流量远程无线抄表系统?

一、应用背景 2021年国务院政府工作报告中指出&#xff0c;扎实做好碳达峰、碳中和各项工作&#xff0c;制定2030年前碳排放达峰行动方案&#xff0c;优化产业结构和能源结构&#xff0c;特别是近期煤炭价格上涨导致蒸汽价格大幅上涨&#xff0c;节能减排显得更加重要&#xf…

深度学习之微调

在现代深度学习领域&#xff0c;精细调整&#xff08;Fine-tune&#xff09;已经成为一种非常重要的技术手段。 预训练模型 在介绍finetune之前&#xff0c;先了解一下什么是预训练模型。在搭建一个网络模型来完成一个特定的图像分类的任务时&#xff0c;首先&#xff0c;需要…