【C++】哈希结构

news2024/10/7 8:32:17

目录

一,哈希结构的认识

1-1,哈希思想

1-2,哈希函数

1-3,哈希冲突

1-3-1,闭散列

1-3-2,开散列

二,哈希结构的封装实现

2-1,闭散列封装实现

​编辑

2-2,开散列封装实现


一,哈希结构的认识

1-1,哈希思想

        C++存储结构中,我们接触的顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log N)。其实,C++STL中还有一种平均情况下的常数时间复杂度(O(1))的查找、插入和删除操作的哈希结构,它是一种理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素,在许多应用中都非常高效。

        C++11底层库中有 unordered_map、unordered_multimap、unordered_set、unordered_multiset等关联式哈希容器,都是通过键key与值value一一眏射的关系进行存储,与map、multimap、set、multiset等容器不同的是哈希存储结构内部都是无序的。

        哈希常用于先构造一种哈希表数据结构,然后通过某种函数(哈希函数)计算键(key)获得一个数值(哈希值),此值对应元素的存储位置,这样一来元素的存储位置与它的关键码(键key)之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。具体如下:

        当向该结构中插入元素时:根据待插入元素的关键码(键key值),以哈希函数计算出该元素的存储位置并按此位置进行存放。

        当向该结构中搜索元素时:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

1-2,哈希函数

        运用以上方式进行增删查改叫哈希(或散列)方法,哈希方法中使用的转换函数称为哈希(或散列)函数,构造出来的结构称为哈希表(或者称散列表)。例如:数据集合{1,7,6,4,5,9};哈希函数设置为:hash(key) = key % capacity;其中 capacity 为存储元素底层空间总的大小,这里假设为10,对应的效果图如下:

        类似于以上哈希函数的设置叫做除留余数法,也是最常用的一种方法。哈希函数的设置还有一种常用的方法叫做直接定址法。若使用直接定址法,如上哈希函数的设置为:hash(key) = key或hash(Key)= a*key + b或类似于取关键字的某个线性函数为散列地址。不难看出,此方法非常消耗空间。若存在类似于{1,1000,60000}数据时,哈希表必须设置非常大,也就是说当关键码非常大时,空间开辟也必须同样大,这将导致空间利用率极低,所以这种方法存在局限性,只有适合查找比较小且连续的情况才适用。

1-3,哈希冲突

        哈希函数的设置其实还有很多种方法,但是大多数都不常用,这里我们只说明除留余数法。用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快。但是,按照上述的哈希函数进行插入时,再向表中插入元素44、16、47,类似于这些关键码通过哈希函数计算出的相同的哈希值时,将会产生空间冲突,该现象叫做哈希冲突。

        哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是这里无法避免哈希冲突。直接定址法和除留余数法彻底解决哈希冲突的方式相同,常见的两种方法是:闭散列开散列

1-3-1,闭散列

        闭散列:也叫开放定址法。当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,此方法是将数据存放到冲突位置中的 “下一个” 空位置中去,若找到表尾位置,就要往头往后继续寻找。哈希冲突中,若连续存在的数据越多,哈希效率就越低,因此又有了负载因子(或载荷因子)的概念。负载因子=实际存储数据的个数/表的大小。负载因子越大,哈希冲突概率就越高,发生冲突时,连续存在多个数据的概率越大;负载因子越小,综合性能就越好,但是空间效率就越差,也就是说负载因子是拿空间换时间的做法。因此,通常将负载因子控制一定大小,一旦负载因子超出范围时就对哈希表进行扩容,其中这里的负载因子大于0,小于等于1。

        控制负载因子还不能完全解决连续存在多个数据导致哈希效率低的情况,通常这里发生哈希冲突寻找下一个空位置时采用线性探测或二次探测。线性探测在寻找下一个位置时一次跳一步寻找下一个空位置,即:hash + i(i > 0),二次探测是以二次方的跳跃式探测下一个空位置,即:hash + i^2(i > 0)。二次探测可能会存在查找不到空位置的情况。研究表明:当表的长度为质数且负载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。

        线性探测实现简单,但是当连续存在多个数据时,会影响哈希效率。二次探测虽可以解决效率问题,但会降低空间利用率。

1-3-2,开散列

        开散列:又叫拉链地址法(或哈希桶法)——首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

        从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。

        无论是哪种方法,若哈希冲突越多,哈希效率就越低。因此,这里的负载因子一定要合理设置,通常闭散列开放定址法将负载因子设置为0.7,开散列拉链法将负载因子设置为1。


二,哈希结构的封装实现

2-1,闭散列封装实现

        由于哈希结构是在关联式容器基础上操作的(不完全是依靠关联式容器),不同的是哈希是无序,关联式容器是有序,所以这里也不支持修改操作。下面我们模拟实现插入、删除、修改操作。

插入操作:插入操作的哈希函数是通过键key值作为哈希码来计算哈希值,但问题是若键key值不是整数将会导致失败,因此在使用哈希函数时需要将哈希码转换为正整数,这里采用伪函数实现。至于在插入时的扩容操作,当负载因子超出或等于0.7时进行扩容,而且扩容时不能在原表上扩容,因为眏射位置会不同。比如:原本表的大小是10,根据哈希函数数据14应插入在4的位置,但是在原表上扩容后假设大小变为20,14应在14的位置上,眏射位置不同,导致后面插入或眏射关系混乱。扩容时需要重新建立哈希表,将全部数据重新确定眏射关系进行插入。

template<class K>   
struct Conversion
{
    size_t operator()(const K& data)
    {
        return data;
    }
};
template<>  //处理键是串的情况
struct Conversion<std::string>
{
    size_t operator()(const std::string& data)
    {
        size_t count = 0;
        for (auto e : data)
        {
            count += e;
            //以下进行专门乘法运算防止出现类似"ab","ba"的串,这里专门乘131而不是其它数据是大佬研究出来最终很小概率不会面临重复数字,这里不做研究
            count *= 131;
        }
        return count;
    }
};

//注意:只要是串,这里无论乘什么数据都会面临重复,因为size_t存储的数据大小有限,而string串的长度很大,当数据大到一定程度时将面临溢出,会出现数值相同现象

查找操作:直接通过计算哈希函数进行查找即可,思想跟插入操作一样。
删除操作:删除数据后要进行标记,表示此位置没有被占用,以便下次进行插入或查找。这里采用枚举表示数据状态,当存储数据时顺便也把数据状态一并存储。删除数据时,只讲状态进行更新即可,表示此数据已被删除。

//数据状态
enum State
{
    EXIT,        //存在数据——已插入
    DELETE, //数据被删除——已删除
    EMPTY    //不存在数据——空位置
};

//存储数据
template <class K, class V>
struct HashData
{
    std::pair<K, V> _kv = std::pair<K, V>(); //数据
    State _state = EMPTY;   //状态
};

封装思路:

        哈希表是一个vector顺序存储容器,存储数据类型为HashData。其次这里还需封装数据个数,即便计算负载因子。

//K—键        V—值     Con—转换size_t的类
template<class K, class V, class Con = Conversion<K>>
class HashTable
{
public:

    //........

private:
    std::vector<HashData<K, V>> _table;  //哈希表
    size_t _count;    //数据个数
};

代码封装:

template<class K>
struct Conversion
{
    size_t operator()(const K& data)
    {
        return data;
    }
};
template<>
struct Conversion<std::string>
{
    size_t operator()(const std::string& data)
    {
        size_t count = 0;
        for (auto e : data)
        {
            count += e;
            count *= 131;
        }
        return count;
    }
};
enum State
{
    EXIT, DELETE, EMPTY   
};
template <class K, class V>
struct HashData
{
    std::pair<K, V> _kv = std::pair<K, V>(); 
    State _state = EMPTY;   
};
//K—键        V—值     Con—转换size_t的类
template<class K, class V, class Con = Conversion<K>>
class HashTable
{
public:
    HashTable(size_t n = 10)
        : _count(0)
    {
        _table.resize(n); //默认开辟10大小空间
    }
    //查找: 当没有查找到数据时返回空,查找到数据返回此数据的指针
    std::pair<K, V>* Find(const K& data)
    {
        Con conversion;
        size_t pos = conversion(data) % _table.size();
        while (_table[pos]._state == EXIT && _table[pos]._kv.first != data)
        {
            pos++;
            pos %= _table.size();
        }
        if (_table[pos]._state != EXIT)    return nullptr;
        return &_table[pos]._kv;
    }
    //插入: 分扩容和插入两大步骤
    bool Insert(const std::pair<K, V>& kv)
    {
        if (Find(kv.first)) return false;
        //扩容,当负载因子超出0.7时进行扩容
        if (_count * 1.0 / _table.size() >= 0.7)
        {
            //扩容时不能在原表上扩容,会导致眏射关系混乱
            //_table.resize(2 * _table.size()); 
            

            //注意: 建立新表时需要运用封装的专门算法,方便起见这里最好不要直接创建表vector,直接新创建对象
            HashTable<K, V, Con> newtable;
            newtable._table.resize(2 * _table.size());
            for (auto& e : _table)
            {
                if (e._state == EXIT) newtable.Insert(e._kv);
            }
            _table.swap(newtable._table);
        }
        //插入数据
        Con conversion;
        size_t pos = conversion(kv.first) % _table.size();
        while (_table[pos]._state == EXIT)
        {
            //不断往后面遍历,当遍历到表尾时从头遍历
            pos++;
            pos %= _table.size();
        }
        _table[pos]._kv = kv;
        _table[pos]._state = EXIT;
        _count++;
        return true;
    }
    //删除: 找到数据后更新数据状态
    bool Erase(const K& data)
    {
        if (!Find(data)) return false;
        Con conversion;
        size_t pos = conversion(data) % _table.size();
        while (_table[pos]._kv.first != data)
        {
            pos++;
            pos %= _table.size();
        }
        _table[pos]._state = DELETE; //更新数据状态
        _count--;
        return true;
    }

    void Print()//测试输出
    {
        std::cout << "Size: " << _count << std::endl;
        for (auto& e : _table)
        {
            if (e._state == EXIT)
            {
                std::cout << e._kv.first << ":" << e._kv.second << std::endl;
            }
        }
        std::cout << std::endl;
    }
private:
    std::vector<HashData<K, V>> _table;
    size_t _count;
};

样例测试1:

void HashTest1()
{
    int a[] = { 1,4,24,34,7,44,17,37 };
    HashTable<int, int> h;
    for (auto e : a)
    {
        h.Insert(std::make_pair(e, e));
    }

    h.Print();

    std::pair<int, int>* it = h.Find(17);
    if (it)
        std::cout << it->first << std::endl;
    else
        std::cout << "无此数据" << std::endl;
    std::cout << std::endl;

    h.Erase(17);
    h.Erase(44);

    h.Print();
}

样例测试2:

void HashTest2()
{
    HashTable<std::string, int> h;
    h.Insert(std::make_pair("string", 0));
    h.Insert(std::make_pair("vector", 0));
    h.Insert(std::make_pair("list", 0));
    h.Insert(std::make_pair("ingstr", 0));
    h.Insert(std::make_pair("torvect", 0));
    h.Insert(std::make_pair("stli", 0));

    h.Print();

    std::pair<std::string, int>* it = h.Find("ingstr");
    if (it)
        std::cout << it->first << std::endl;
    else
        std::cout << "无此数据" << std::endl;
    std::cout << std::endl;

    h.Erase("stli");
    h.Erase("torvect");

    h.Print();
}

2-2,开散列封装实现

        开散列插入的数据类型是结点,发生哈希冲突时使用链表结构连接,跟闭散列不同,不需要使用状态。有些人想到链表结构可能会首选库中list,即vector<list>。但是list是双向链表,双向链表的优势在于可在任意位置插入删除,这里显然没多大优势,因此采取这种方式会降低哈希性能,使用单链表足以。

        开散列的查找和删除操作简单,插入操作时的扩容需注意不要使用闭散列的方式建立哈希表进行插入,因为这里的插入数据操作是结点,需要动态开辟空间,若原本数据量过多,这样持续的开辟删除会大大降低性能。这里直接创建表来重新搬运结点数据以重新建立哈希关系。封装如下:

//结点
template<class K, class V>
struct HashNode
{
    pair<K, V> _kv;
    HashNode<K, V>* _next;

    HashNode(const pair<K, V>& kv = pair<K, V>())
        : _kv(kv)
        , _next(nullptr)
    {    }
};

//哈希表

template <class K, class V, class Con = Conversion<K>>
class HashTable
{
    typedef HashNode<K, V> Node;
public:
    HashTable(const size_t n = 10)
        : _count(0)
    {
        _table.resize(n, nullptr);
    }

    ~HashTable()
    {
        for (size_t i = 0; i < _table.size(); i++)
        {
            Node* cur = _table[i];
            while (cur)
            {
                _table[i] = cur->_next;
                delete cur;
                _count--;
                cur = _table[i];
            }
        }
    }
    //查找
    Node* Find(const K& data)
    {
        Con conversion;
        size_t pos = conversion(data) % _table.size();
        Node* cur = _table[pos];
        while (cur)
        {
            if (cur->_kv.first == data)    return cur;
            cur = cur->_next;
        }
        return nullptr;
    }
    //插入
    bool Insert(const pair<K, V>& kv)
    {
        //这里不允许插入相同数据
        if (Find(kv.first)) return false;
        //扩容: 负载因子到1就扩容
        if (_count == _table.size())
        {
            //直接创建表来重新搬运结点数据
            vector<Node*> newtable;
            newtable.resize(2 * _table.size(), nullptr);
            for (size_t i = 0; i < _table.size(); i++)
            {
                Node* cur = _table[i];
                while (cur)
                {
                    Node* next = cur->_next;
                    Con conversion;
                    size_t pos = conversion(cur->_kv.first) % newtable.size();
                    cur->_next = newtable[pos];
                    newtable[pos] = cur;
                    cur = next;
                }
            }
            _table.swap(newtable);
        }
        //插入
        Con conversion;
        size_t pos = conversion(kv.first) % _table.size();
        Node* node = new Node(kv);
        //这里进行头插
        node->_next = _table[pos];
        _table[pos] = node;
        _count++;
        return true;
    }
    //删除
    bool Erase(const K& data)
    {
        if (!Find(data)) return false;
        Con conversion;
        size_t pos = conversion(data) % _table.size();
        Node* cur = _table[pos];
        Node* prv = nullptr;
        while (cur->_kv.first != data)
        {
            prv = cur;
            cur = cur->_next;
        }
        if (cur == _table[pos]) _table[pos] = cur->_next;
        else prv->_next = cur->_next;
        delete cur;
        cur = nullptr;
        _count--;
        return true;
    }

    void Print() //测试输出
    {
        int num = 0, Maxbucketlength = 0, Bucketlength = 0;
        for (size_t i = 0; i < _table.size(); i++)
        {
            Bucketlength = 0;
            Node* cur = _table[i];
            if (cur)
            {
                num++;
                cout << "Bucket " << i << ": ";
            }
            else
            {
                cout << "Bucket " << i << ": " << "nullptr";
            }
            while (cur)
            {
                Bucketlength++;
                cout << cur->_kv.first << ":" << cur->_kv.second << "  ";
                cur = cur->_next;
            }
            if (Bucketlength > Maxbucketlength) Maxbucketlength = Bucketlength;
            cout << endl;
        }
        cout << "Bucket Node Count: " << _count << endl;
        cout << "Bucket Group: " << num << endl;
        cout << "Table Size: " << _table.size() << endl;
        cout << "load factor: " << _count * 1.0 / _table.size() << endl;
        cout << "Maxbucketlength: " << Maxbucketlength << endl;
        cout << endl;
    }
private:
    vector<Node*> _table;  //哈希表
    size_t _count;  //数据个数
};

        注意:若哈希桶连接过长,哈希效率就接近于n。由于这里哈希表存储的都是桶,桶结构是链表,因此,极端情况下若存在桶长度过大,我们可以将此桶改为红黑树结构来提高哈希效率。

测试样例:

void HashTest1()
{
    int a[] = { 1,4,24,34,7,44,17,37 };
    HashTable<int, int> h;
    for (auto e : a)
    {
        h.Insert(make_pair(e, e));
    }

    h.Print();

    HashNode<int, int>* Node = h.Find(17);
    if (Node)
        cout << Node->_kv.first << ":" << Node->_kv.second << endl;
    else
        cout << "无此数据" << endl;
    cout << endl;

    h.Erase(17);
    h.Erase(44);

    h.Print();
}

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

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

相关文章

Sy-linux下常用的网络命令linux network commands

linux下的网络命令非常强大&#xff0c;这里根据教材需要&#xff0c;列出来常用的网络命令和场景实例&#xff0c;供参考。 一、命令列表&#xff1a; Command Description ip Manipulating routing to assigning and configuring network parameters traceroute Identi…

分组查询得到一列多行数据后,怎么用来和表中的某列数据进行一一比较

#10&#xff09;查询每个学生超过他自己选修课程平均成绩的课程号 这里要先进行分组得到每个学号对应的平均成绩&#xff0c;在用表中的成绩grade与得到的平均成绩一一比较 这里可以进行连表操作&#xff0c;把分组查询得到的结果与原表通过sno学号进行等值连接 &#xff0c;就…

MySQL数据库外键约束打开与关闭 ️

MySQL数据库外键约束打开与关闭 &#x1f6e0;️ MySQL数据库外键约束打开与关闭 &#x1f6e0;️摘要 &#x1f4dd;引言 &#x1f680;正文内容&#xff08;详细介绍&#xff09; &#x1f4a1;关闭外键约束检查外键约束检查关闭的作用风险与最佳实践建议 &#x1f914; QA环…

一分钟成为点灯大师(超简单1行代码-STM32F407的HAL实现按键中断方式点亮LED灯)

一、开发环境 硬件&#xff1a;正点原子探索者 V3 STM32F407 开发板 单片机&#xff1a;STM32F407ZGT6 Keil版本&#xff1a;5.32 STM32CubeMX版本&#xff1a;6.9.2 STM32Cube MCU Packges版本&#xff1a;STM32F4 V1.27.1 使用STM32F407的HAL库实现按键中断方式读取按键…

在Vue项目使用kindEditor富文本编译器以及上传图片

第一步 npm install kindeditor第二步&#xff0c;建立kindeditor.vue组件 <template><div class"kindeditor"><textarea :id"id" name"content" v-model"outContent"></textarea></div> </templa…

49-PCIE转网口电路设计

视频链接 PCIE转网口电路设计01_哔哩哔哩_bilibili PCIe转网口电路设计 1、PCIE转网口电路设计基本介绍 pcie转网口的设计&#xff0c;一般有intel (i350)和网讯&#xff08;wx1860&#xff09;两种方案。 2、PCIE转网口的方案 2.1、I350 2.2、WX1860 (网迅) 国产化&#…

【Java】如何获取客户端IP地址

在项目中往往涉及到“获取客户端IP地址”&#xff0c;常见到下面这样子的代码&#xff1a; package com.utils;import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.http.server.reactive.ServerHttpRequest; import java.net…

NotePad++联动ABAQUS

Abaqus 中脚本运行 1. 命令区kernel Command Line Interface &#xff08;KCLI&#xff09; execfile(C:\\temp\second develop\chapter2\pyTest1.py)2. CAE-Run Script File->Run Script 3. Abaqus command Abaqus cae noGUIscript.py(前后处理都可)Abaqus Python scr…

在PostgreSQL中如何有效地批量导入大量数据,并确保数据加载过程中的性能和稳定性?

文章目录 解决方案1. 使用COPY命令2. 调整配置参数3. 禁用索引和约束4. 使用事务5. 并发导入 总结 在PostgreSQL中&#xff0c;批量导入大量数据是一个常见的需求&#xff0c;特别是在数据迁移、数据仓库填充或大数据分析等场景中。为了确保数据加载过程中的性能和稳定性&#…

【Hadoop3.3.6全分布式环境搭建】

说明: 完成Hadoop全分布式环境搭建,需准备至少3台虚拟机(master slave01 slave02)环境: VMWare + Centos7 + JDK1.8+ Hadoop3.3.6主机规划: 主节点:master从节点:slave01 , slave02 一、准备工作 1、所有主机安装jdk 上传jdk-8u171-linux-x64.tar.gz到/root目录下,然后…

数组中两个字符串的最短距离---一题多解(贪心/二分)

点击跳转到题目 方法&#xff1a;贪心 / 二分 目录 贪心&#xff1a; 二分&#xff1a; 贪心&#xff1a; 要找出字符串数组中指定两个字符串的最小距离&#xff0c;即找出指定字符串对应下标之差的最小值 思考&#xff1a;如果是直接暴力求解&#xff0c;需要两层for循环…

浏览器跨标签页通信的方式都有哪些

跨标签页的实际应用场景&#xff1a; 1. 共享登录状态&#xff1a; 用户登录后&#xff0c;多个标签页中需要及时获取到登录状态&#xff0c;以保持一致的用户信息。这种情况&#xff0c;可以使用浏览器的 localStorage 或者 sessionStorage 来存储登录状态&#xff0c;并通过…

面试不慌张:一文读懂FactoryBean的实现原理

大家好&#xff0c;我是石头~ 在深入探讨Spring框架内部机制时&#xff0c;FactoryBean无疑是一个关键角色&#xff0c;也是面试中经常出现的熟悉面孔。 不同于普通Java Bean&#xff0c;FactoryBean是一种特殊的Bean类型&#xff0c;它的存在并非为了提供业务逻辑&#xff0c;…

串口小项目 - 声控刷抖音

项目准备&#xff1a; orangepi02 语言 模块: SU-03T 电脑 接线: 语言模块 - orangepi VCC - 5V GND - GND B7(RX)--RX-5 orangepi 手机 通过usb 连接 实现思路图: 语言模块接收到语言信息&#xff0c;发送到 H616 去处理&#xff0c;H616再控制手机实现语言刷抖音的功能 …

(2022级)成都工业学院数据库原理及应用实验八: 数据库恢复技术

写在前面 1、基于2022级软件工程/计算机科学与技术实验指导书 2、成品仅提供参考 3、如果成品不满足你的要求&#xff0c;请寻求其他的途径 运行环境 window11家庭版 Navicat Premium 16 Mysql 8.0.36 实验要求 1、使用mysqldump实现数据库备份。 2、使用mysqldump实…

LCR 023. 相交链表

给定两个单链表的头节点 headA 和 headB &#xff0c;请找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&#xf…

【HTML】H5新增元素记录

H5 新增元素特性 1. 语义化标签 语义化标签的好处&#xff1a; 对于浏览器来说&#xff0c;标签不够语义化对于搜索引擎来说&#xff0c;不利于SEO的优化 语义化标签&#xff1a; header:头部元素nav&#xff1a;导航section:定义文档某个区域的元素article:内容元素aside…

文心一言 VS 讯飞星火 VS chatgpt (241)-- 算法导论17.3 7题

七、为动态整数多重集 S (允许包含重复值)设计一种数据结构&#xff0c;支持如下两个操作&#xff1a;① INSERT(S,x) 将 x 插入 S 中&#xff1b;② DELETE-LARGER-HALF(S) 将最大的 ⌈|S|/2⌉ 个元素从S中删除。解释如何实现这种数据结构&#xff0c;使得任意 m 个 INSERT 和…

linux-centos虚拟机设置固定ip

环境准备 虚拟机版本&#xff1a;centos7 安装环境&#xff1a;vmware17 1、设置网络连接 虚拟机-设置-网络适配器-NAT模式 2、查看子网信息 编辑-虚拟网络编辑器-NAT模式-NAT设置 查看子网ip和网关ip 下一步要用 3、修改配置文件 vim /etc/sysconfig/network-scripts…

企业上云数字化转型的关键——选对服务器虚拟化

盘点市面上的主流虚拟化软件 虚拟化技术就像可以随意组合的乐高积木&#xff0c;可以高效、灵活地利用计算资源。世面上主流虚拟化很多&#xff0c;各有长处和短板。今天先来盘点一下市面上的主流虚拟化软件&#xff0c;一探究竟。 虚拟化的老牌巨头——VMware 老牌虚拟化巨…