堆的概念、堆的向下调整算法、堆的向上调整算法、堆的基本功能实现

news2024/11/15 13:31:41

目录

堆的介绍

堆的概念

堆的性质

堆的结构

堆的向下调整算法

基本思想(以建小堆为例)

 代码

堆的向上调整算法

基本思想(以建小堆为例)

代码

 堆功能的实现

堆的初始化 HeapInit

销毁堆 HeapDestroy

打印堆 HeapPrint

堆的插入 HeapPush

堆的删除 HeapPop

获取堆顶的数据 HeapTop

获取堆的数据个数 HeapSize

堆的判空 HeapEmpty


堆的介绍

堆的概念

        堆:如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足ki<=k2i+1且ki<=k2i+2(或满足ki>=k2i+1且ki>=k2i+2),其中i=0,1,2,…,则称该集合为堆。
        小堆:将根结点最小的堆叫做小堆,也叫最小堆或小根堆。
        大堆:将根结点最大的堆叫做大堆,也叫最大堆或大根堆。

堆的性质

        堆中某个结点的值总是不大于或不小于其父结点的值。
        堆总是一棵完全二叉树。

堆的结构

堆的向下调整算法

        现在我们给出一个数组,逻辑上看作一棵完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。

但是,使用向下调整算法需要满足一个前提
        若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
        若想将其调整为大堆,那么根结点的左右子树必须都为大堆。 

基本思想(以建小堆为例)

        1.从根结点处开始,选出左右孩子中值较小的孩子。
        2.让小的孩子与其父亲进行比较。
        若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。
        若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

 代码

// 定义一个交换函数,用于交换两个整数变量的值
void Swap(int* x, int* y)
{
    // 创建一个临时变量存储x指向的值
    int tmp = *x;
    
    // 将y指向的值赋给x指向的位置
    *x = *y;
    
    // 将临时变量tmp的值(原x的值)赋给y指向的位置
    *y = tmp;
}

// 定义一个堆的向下调整函数(针对小顶堆)
void AdjustDown(int* a, int n, int parent)
{
    // 初始化child为当前节点的左孩子的下标,假设左孩子初始时值较小
    int child = 2 * parent + 1;

    // 当孩子节点下标小于数组长度时,循环执行以下操作
    while (child < n)
    {
        // 如果右孩子存在,并且右孩子的值小于左孩子的值
        if (child + 1 < n && a[child + 1] < a[child])
        {
            // 更新child为较小孩子的下标(即右孩子)
            child++;
        }
        
        // 如果孩子(左右中较小的一个)的值小于其父节点的值
        if (a[child] < a[parent])
        {
            // 使用交换函数交换父节点和较小的孩子节点的值
            Swap(&a[child], &a[parent]);

            // 更新parent为刚交换后较小孩子的下标,准备检查新的子树是否满足堆性质
            parent = child;

            // 重新计算下一个待检查的孩子节点的下标
            child = 2 * parent + 1;
        }
        else // 如果当前节点以及其子节点均满足堆性质,则结束调整
        {
            break;
        }
    }
}

        使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = log2(N+1)(N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN) 。 

        上面说到,使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆呢?
        答案很简单,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。

// 建堆过程
for (int i = (n - 1 - 1) / 2; i >= 0; i--) 
{
    // 从最后一个非叶子节点开始,依次对每个节点调用向下调整函数
    // 这样做可以确保整个堆结构满足小顶堆的性质
    AdjustDown(php->a, php->size, i);
}

 在这段代码中:

  • `n` 表示堆中的元素个数。
  • 通过 `(n - 1 - 1) / 2` 计算得到最后一个非叶子节点的索引。
  • 遍历从最后一个非叶子节点到根节点(索引为0),依次对每个节点执行向下调整操作,使得每个节点及其子树构成一个小顶堆。
  • `php->a` 是指向堆数组的指针,`php->size` 是堆的大小(元素个数)。
  • 调用 `AdjustDown` 函数逐个对每个父节点进行调整,以保证堆的特性。当遍历完成后,整个数组便构成了一个小顶堆。

堆的向上调整算法

        当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。

基本思想(以建小堆为例)

        1.将目标结点与其父结点比较。

        2.若目标结点的值比其父结点的值小,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整。若目标结点的值比其父结点的值大,则停止向上调整,此时该树已经是小堆了。

代码

// 定义一个交换函数,用于交换两个自定义类型 HPDataType 指针所指向的数据
void Swap(HPDataType* x, HPDataType* y)
{
    // 创建一个临时变量存储x指向的数据
    HPDataType tmp = *x;
    
    // 将y指向的数据赋给x指向的位置
    *x = *y;
    
    // 将临时变量tmp的数据(原x的数据)赋给y指向的位置
    *y = tmp;
}

// 定义一个堆的向上调整函数(针对小顶堆)
void AdjustUp(HPDataType* a, int child)
{
    // 根据孩子节点的下标计算其父节点的下标
    int parent = (child - 1) / 2;

    // 当孩子节点的下标大于0(即未到达根节点)时,循环执行以下操作
    while (child > 0)
    {
        // 如果孩子节点的值小于其父节点的值
        if (a[child] < a[parent])
        {
            // 使用交换函数交换孩子节点和父节点的数据
            Swap(&a[child], &a[parent]);

            // 更新孩子节点为刚刚交换后的父节点,以便继续向上调整
            child = parent;

            // 重新计算新的父节点下标
            parent = (child - 1) / 2;
        }
        else // 如果孩子节点与其父节点的值关系已经满足堆的性质,则停止调整
        {
            break;
        }
    }
}

在上述代码中:

  • HPDataType 是用户自定义的数据类型,这里假设它支持 < 运算符用于比较大小。
  • AdjustUp 函数主要用于将新插入或更新后的元素调整到正确位置,以保持小顶堆的性质。从下标为 child 的节点开始,如果其值小于父节点,则两者交换位置,直到无法继续交换(即已达到根节点或者不再违反堆的性质)。

 堆功能的实现

堆的初始化 HeapInit

        首先,必须创建一个堆类型,该类型中需包含堆的基本信息:存储数据的数组、堆中元素的个数以及当前堆的最大容量。

// 数据类型定义
typedef int HPDataType; // 定义堆中存储数据的类型为整型

// 结构体定义
typedef struct Heap
{
	// 堆中存储数据的数组,动态分配内存来存储堆中的元素
	HPDataType* a;

	// 记录堆中已有元素的个数
	int size;

	// 记录堆的最大容量,即数组a可容纳的元素数量
	int capacity;
} HP; 

        创建完堆类型后,我们还需要一个初始化函数,对刚创建的堆进行初始化,注意在初始化期间要将传入数据建堆。

// 定义一个初始化堆的函数
void HeapInit(HP* php, HPDataType* a, int n)
{
    // 断言检查传入的堆结构指针是否有效
    assert(php);

    // 动态申请一块足够容纳n个HPDataType类型数据的内存空间
    HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType)*n);
    
    // 检查内存申请是否成功
    if (tmp == NULL)
    {
        printf("动态内存分配失败!\n");
        exit(-1); // 若分配失败,程序终止运行
    }

    // 将新申请的内存空间赋值给堆结构的数组成员
    php->a = tmp;

    // 使用memcpy函数将输入数组a中的数据复制到堆结构的数组中
    memcpy(php->a, a, sizeof(HPDataType)*n);

    // 设置堆的已有元素个数为输入的n
    php->size = n;

    // 同时设置堆的容量也为n
    php->capacity = n;

    // 初始化堆:从最后一个非叶子节点开始,依次对每个节点进行向下调整操作
    int i = 0;
    for (i = (php->size - 1 - 1) / 2; i >= 0; i--)
    {
        AdjustDown(php->a, php->size, i);
    }
}

        此函数的作用是初始化一个堆结构,并根据传入的数据构建一个小顶堆。首先动态分配内存以存储堆中的数据,然后将输入数组a中的数据复制到堆结构的数组中。接着设置堆的大小和容量,并从最后一个非叶子节点开始,使用 AdjustDown 函数对每一个节点进行调整,最终使整个数据结构满足小顶堆的性质。 

销毁堆 HeapDestroy

        为了避免内存泄漏,使用完动态开辟的内存空间后都要及时释放该空间,所以,一个用于释放内存空间的函数是必不可少的。

// 定义一个销毁堆的函数
void HeapDestroy(HP* php)
{
    // 断言检查传入的堆结构指针是否有效
    assert(php);

    // 释放之前动态分配给堆结构数组的空间
    free(php->a);

    // 置堆结构的数组指针为空,防止野指针问题
    php->a = NULL;

    // 将堆中元素个数清零
    php->size = 0;

    // 将堆的容量也清零
    php->capacity = 0;
}

        此函数用于销毁堆结构,释放堆占用的内存资源并将相关状态信息重置为初始状态,以便后续可能的再次初始化或避免产生内存泄漏等问题。 

打印堆 HeapPrint

        打印堆中的数据,这里用了两种打印格式。第一种打印格式是按照堆的物理结构进行打印,即打印为一排连续的数字。第二种打印格式是按照堆的逻辑结构进行打印,即打印成树形结构。

// 计算具有n个节点的完全二叉树的深度
int depth(int n)
{
    assert(n >= 0);

    if (n > 0)
    {
        int m = 2;
        int height = 1;
        while (m < n + 1)
        {
            m *= 2;
            height++;
        }
        return height;
    }
    else
    {
        return 0;
    }
}

// 打印堆
void HeapPrint(HP* php)
{
    assert(php);

    // 按照数组形式打印堆内容
    int i = 0;
    for (i = 0; i < php->size; i++)
    {
        printf("%d ", php->a[i]);
    }
    printf("\n");

    // 按照树形结构打印堆内容
    int tree_depth = depth(php->size);
    int total_nodes = pow(2, tree_depth) - 1; // 获取对应深度的满二叉树的节点总数
    int current_spaces = total_nodes - 1; // 记录每一行前面的空格数
    int current_row = 1; // 当前行数
    int index = 0; // 待打印数据的下标

    while (true)
    {
        // 打印前面的空格
        for (int i = 0; i < current_spaces; i++)
        {
            printf(" ");
        }

        // 打印数据和间距
        int nodes_in_row = pow(2, current_row - 1); // 每一行的数字个数
        while (nodes_in_row--)
        {
            printf("%02d", php->a[index++]); // 打印数据

            if (index >= php->size) // 如果所有数据都已打印,则结束打印
            {
                printf("\n");
                return;
            }

            int spaces_between_numbers = (current_spaces + 1) * 2; // 两个数之间的空格数
            for (int j = 0; j < spaces_between_numbers; j++) // 打印两个数之间的空格
            {
                printf(" ");
            }
        }
        printf("\n"); // 换行

        current_row++; // 下一行
        current_spaces = current_spaces / 2 - 1; // 更新当前行的空格数
    }
}

这段代码实现了两个功能:

  • depth 函数用于计算具有n个节点的完全二叉树的深度。

  • HeapPrint 函数用于打印堆的内容。首先按数组顺序打印堆的所有元素,然后按照树形结构打印堆,使其看起来像一棵二叉树。通过计算堆对应的完全二叉树的深度,确定每一层节点的数量及相应空格数,从而实现树形结构的打印。

堆的插入 HeapPush

        数据插入时是插入到数组的末尾,即树形结构的最后一层的最后一个结点,所以插入数据后我们需要运用堆的向上调整算法对堆进行调整,使其在插入数据后仍然保持堆的结构。

// 插入元素到堆中
void HeapPush(HP* php, HPDataType x)
{
    // 断言检查堆结构指针有效性
    assert(php);

    // 判断堆是否已满,如果满了则尝试扩大容量
    if (php->size == php->capacity)
    {
        // 申请两倍于当前容量的新内存空间
        HPDataType* tmp = (HPDataType*)realloc(php->a, 2 * php->capacity * sizeof(HPDataType));

        // 检查内存分配是否成功
        if (tmp == NULL)
        {
            printf("动态内存扩充失败!\n");
            exit(-1); // 分配失败则退出程序
        }

        // 成功分配内存后,更新堆结构的数组指针和容量值
        php->a = tmp;
        php->capacity *= 2;
    }

    // 将新元素添加至堆数组末尾
    php->a[php->size] = x;

    // 堆大小加一
    php->size++;

    // 调整堆结构,确保新插入元素后的堆仍满足堆属性
    AdjustUp(php->a, php->size - 1); // 从新增节点开始向上调整
}

        在这个函数中,我们首先检查堆是否已满,若满则通过realloc函数扩大堆容量。接着将新元素添加至堆数组的末尾,并增加堆的大小计数。最后调用`AdjustUp`函数对新插入的元素进行上浮调整,确保堆仍然满足小顶堆的性质。

堆的删除 HeapPop

        堆的删除,删除的是堆顶的元素,但是这个删除过程可并不是直接删除堆顶的数据,而是先将堆顶的数据与最后一个结点的位置交换,然后再删除最后一个结点,再对堆进行一次向下调整。

        原因:我们若是直接删除堆顶的数据,那么原堆后面数据的父子关系就全部打乱了,需要全体重新建堆,时间复杂度为O(N)。

        若是用上述方法,那么只需要对堆进行一次向下调整即可,因为此时根结点的左右子树都是小堆,我们只需要在根结点处进行一次向下调整即可,时间复杂度为 O(log(N))。

// 删除堆顶元素
void HeapPop(HP* php)
{
    // 断言检查堆结构指针有效性
    assert(php);

    // 断言检查堆是否为空,如果不是空堆才能进行删除操作
    assert(!HeapEmpty(php));

    // 将堆顶元素(即数组的第一个元素)与堆的最后一个元素交换
    Swap(&php->a[0], &php->a[php->size - 1]);

    // 删除堆的最后一个元素,即将堆大小减一
    php->size--;

    // 由于堆顶元素可能不再满足堆的性质,因此需要对新的堆顶元素进行向下调整操作,以恢复堆的有序性
    AdjustDown(php->a, php->size, 0);
}

        此函数首先确认堆不为空,然后通过交换堆顶元素和堆尾元素的位置,实际上将堆尾元素移到了堆顶。接着减少堆的大小表示删除了堆顶元素,最后调用 `AdjustDown` 函数从新的堆顶元素开始向下调整堆,确保剩余元素重新形成符合堆性质的结构。

获取堆顶的数据 HeapTop

        获取堆顶的数据,即返回数组下标为0的数据。

// 获取堆顶元素的值
HPDataType HeapTop(HP* php)
{
    // 断言检查堆结构指针有效性
    assert(php);

    // 断言检查堆是否为空,非空堆才能获取堆顶数据
    assert(!HeapEmpty(php));

    // 返回堆顶元素的值,即堆数组的第一个元素
    return php->a[0];
}

        此函数用于安全地获取堆顶元素的值,在确保堆非空的情况下直接返回堆顶元素(小顶堆中最小的元素)。若堆为空,则会触发断言错误,提示堆为空无法获取堆顶元素。 

获取堆的数据个数 HeapSize

        获取堆的数据个数,即返回堆结构体中的size变量。

// 获取堆中元素个数
int HeapSize(HP* php)
{
    // 断言检查堆结构指针有效性
    assert(php);

    // 返回堆中当前存储的数据个数
    return php->size;
}
        此函数用于获取堆中实际包含的元素数量,只需直接返回堆结构体中的 `size` 成员变量即可。在调用此函数前,需确保堆结构指针有效。

堆的判空 HeapEmpty

        堆的判空,即判断堆结构体中的size变量是否为0。

// 判断堆是否为空
bool HeapEmpty(HP* php)
{
    // 断言检查堆结构指针有效性
    assert(php);

    // 如果堆中数据个数为0,则认为堆为空
    return php->size == 0;
}
        此函数用于检查堆中是否有数据,通过查看堆结构体中的 `size` 成员变量是否为0来判断。当堆中没有元素时,函数返回 true,表示堆为空;否则返回 false,表示堆中有至少一个元素。在调用此函数前,需确保堆结构指针有效。

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

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

相关文章

如何在群晖NAS部署office系统办公服务并实现无公网IP远程编辑文件

文章目录 本教程解决的问题是&#xff1a;1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 本教程解决的问题是&#xff1a; 1.Word&#xff0c;PPT&#xff0c;Excel等重要文件存在本地环境&#xff0c;如何在编…

Linux 用户和组

理解Linux 用户和组的概念 掌握passwd 文件的组成以及作用 掌握shadow 文件的组成以及作用 了解group 文件的内容 1.用户分类&#xff1a; 超级管理员&#xff08;root&#xff09; 普通用户 程序用户 1.用户信息文件 /etc/passwd 文件中存储了所有用户信息。 1.passwd 格…

2024年大学三下乡社会实践活动投稿——你想知道的都在这里

2024年的夏天,我作为一名大学生,满怀激情地参加了三下乡社会实践活动。在这段难忘的经历中,我深刻体验到了农村生活的艰辛与美好,也收获了许多宝贵的经验和感悟。为了将这段经历传递给更多的人,我决定向媒体投稿,发表一篇关于三下乡社会实践活动的通讯稿件。 起初,我选择了传统…

【多线程】CAS的应用 | CAS的概念 | 实现原子类 | 实现自旋锁

文章目录 一、CAS1.什么是CAS2.实现原子类3.实现自旋锁 一、CAS 1.什么是CAS Compare and swap 比较并交换。 比较交换的是 内存 和 寄存器 比如此时有一个内存 : M。 还有两个寄存器A,B ​ CAS ( M , A , B ) :如果M和A的值相同的话&#xff0c;就把M和B的值进行交换(交换的…

UI设计/交互设计/视觉设计项目汇报/作品集Figma/PPT模板

作为UI设计/交互设计/视觉设计师&#xff0c;创建作品集对于向潜在客户或雇主展示您的技能、创造力和风格至关重要。以下分步指南可帮助您创建令人印象深刻的作品集&#xff1a; 选择您的最佳作品&#xff1a;选择您最强大且最相关的设计项目&#xff0c;将其纳入您的作品集。…

Pytorch入门实战: 06-VGG-16算法-Pytorch实现人脸识别

第P6周&#xff1a;VGG-16算法-Pytorch实现人脸识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 &#x1f3e1; 我的环境&#xff1a; 语言环境&#xff1a;Python3.8 编译器&#xff1a;Jupyter La…

微信小程序vue.js+uniapp服装商城销售管理系统nodejs-java

本技术是java平台的开源应用框架&#xff0c;其目的是简化Sping的初始搭建和开发过程。默认配置了很多框架的使用方式&#xff0c;自动加载Jar包&#xff0c;为了让用户尽可能快的跑起来spring应用程序。 SpinrgBoot的主要优点有&#xff1a; 1、为所有spring开发提供了一个更快…

python3--lxml pytoml.core.TomlError expected_equals报错解决

文章目录 一、问题二. 解决方法&#xff1a;三. 参考&#xff1a;四. 总结 一、问题 在ubuntu的armbian上的python3中安装lxml时报错了 安装命令是 pip3 install lxml报错简略信息如下图 File "/usr/share/python-wheels/pytoml-0.1.2-py2.py3-none-any.whl/pytoml/par…

iptables命令简介

正文共&#xff1a;3456 字 22 图&#xff0c;预估阅读时间&#xff1a;3 分钟 iptables/ip6tables命令&#xff0c;用于在Linux内核中设置、维护和检查IPv4和IPv6数据包过滤规则的表&#xff0c;从而实现IPv4/IPv6数据包过滤和NAT的管理工具。它可以定义多个不同的表&#xff…

CH341A/B USB转USART/I2C/SPI介绍

CH341A/B USB转USART/I2C/SPI介绍 &#x1f4cd;CH341官方文档&#xff1a;https://www.wch.cn/downloads/CH341DS2_PDF.html CH341A/B是一个USB总线的转接芯片&#xff0c;通过USB总线提供异步串口、打印口、并口以及常用的2线和4线等同步串行接口。 &#x1f341;芯片封装&a…

基于Springboot的社区疫情返乡管控系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区疫情返乡管控系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

uni-app为图片添加自定义水印(升级版)

前置内容 uni-app为图片添加自定义水印&#xff08;解决生成图片不全问题&#xff09; UI 升级 现在水印样式变成这样了&#xff1a; 代码 <template><canvas v-if"waterMarkParams.display" canvas-id"waterMarkCanvas" :style"canv…

【001_IoT/物联网通信协议基础: HTTP、Websocket、MQTT、AMQP、COAP、LWM2M一文搞懂】

001_IoT/物联网通信协议基础: HTTP、Websocket、MQTT、AMQP、COAP、LWM2M一文搞懂 文章目录 001_IoT/物联网通信协议基础: HTTP、Websocket、MQTT、AMQP、COAP、LWM2M一文搞懂创作背景通信模型ISO/OSI七层模型 和 TCP/IP四层模型网络通信数据包格式&#xff08;Ethernet II&…

Swift-20-基础数据类型

数据定义 语法规则 先来看下下面的代码 import Cocoavar num1 "four" //a var num2: String "four" //b var num3 4 //c var num4: Int 4 //d上面的几行代码都能正常运行&#xff0c;其中a和b行等价&#xff0c;c和d行等价。区另就在于是否声…

docker-compose 安装MongoDB续:创建用户及赋权

文章目录 1. 问题描述2. 分析2.1 admin2.2 config2.3 local 3. 如何连接3.解决 1. 问题描述 在这一篇使用docker-compose创建MongoDB环境的笔记里&#xff0c;我们创建了数据库&#xff0c;但是似乎没有办法使用如Robo 3T这样的工具去连接数据库。连接的时候会返回这样的错误&…

关系抽取与属性补全

文章目录 实体关系抽取的任务定义机器学习框架属性补全 实体关系抽取的任务定义 从文本中抽取出两个或者多个实体之间的语义关系&#xff1b;从文本获取知识图谱三元组的主要技术手段&#xff0c;通常被用于知识图谱的补全。美丽的西湖坐落于浙江省的省会城市杭州的西南面。&am…

IDEA中SVN 的使用

文章目录 前言一、svn安装二、IDEA集成SVN总结 前言 svn可以老牌的代码仓库了 说实话svn还是和git无法相比的,毕竟git有本地仓库的概念,可以很好的处理冲突,然而svn是没有本地仓库的概念的,所以只能拉取别人的代码,然后处理冲突后,才能提交代码; 由于最近的工作换成了用svn仓…

el-menu 有一级二级三级菜单

效果如下 菜单代码如下 <el-menu:default-active"menuDefaultActive"class"el-menu-box":text-color"menuTextColor":active-text-color"menuActiveTextColor":unique-opened"true"><!-- 一级菜单 --><tem…

线程池的核心参数有哪些???

线程池的核心参数包括以下七个&#xff1a; corePoolSize&#xff1a; 这是线程池中的核心线程数&#xff0c;即池中会保留的最少线程数。当提交任务时&#xff0c;如果当前线程数小于核心线程数&#xff0c;线程池会创建新的线程来执行任务。如果当前线程数等于或大于核心线程…

Docker - 简介

原文地址&#xff0c;使用效果更佳&#xff01; Docker - 简介 | CoderMast编程桅杆https://www.codermast.com/dev-tools/docker/docker-introduce.html Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 D…