哈希数据结构的概念、实现和应用

news2024/11/19 1:38:40

一、认识哈希表

1.unordered_set和unordered_map

STL实现了两个数据结构unordered_map和unordered_set顾名思义,因为底层的实现方式不同,它们成为了无序的map和set,但是它们的使用与普通的map和set是一样的。

我们可以通过代码测试两种数据结构搜索数据的速度。

#include<iostream>
#include<map>
#include<set>
#include<string>
#include<cstdlib>
#include<ctime>
#include<unordered_map>
using namespace std;

//改变NUM的值以测试查找效率
#define NUM 1000

//测试unordermap和map的搜索能力
int main()
{
    int arr[NUM] = { 0 };
    srand((unsigned int)time(nullptr));
    int num = rand() * 121;
    map<int, int> m1;
    unordered_map<int, int> m2;
    for (int i = 0; i < NUM; ++i)
    {
        arr[i] = num;
        m1.insert(make_pair(num, num * 2));
        m2.insert(make_pair(num, num * 2));
        num = rand();
    }
    clock_t t1 = clock();
    for (auto e : arr)
    {
        m1.find(e);
    }
    clock_t t2 = clock();
    cout << "map查找使用的时间" << t2 - t1 << endl;

    clock_t t3 = clock();
    for (auto e : arr)
    {
        m2.find(e);
    }
    clock_t t4 = clock();
    cout << "unordered_map查找使用的时间" << t4 - t3 << endl;
    return 0;
}

NUM为1000,10000时二者都差不多,甚至当NUM为10000时也不会差很多。

当数据量很大时,可以很明显地看到,unordered容器的搜索速度明显更快,下面是NUM在50000时的搜索时间。

如果将模式由debug换到release,它们的差距还会变大。

对于普通的map、set是使用红黑树实现的,而unordered系列都使用哈希表实现。

2.哈希表的概念

哈希表(也叫散列表),也是根据值Key,查找对应value的数据结构。它的查找的速度非常快,时间复杂度为O(1)。

二叉搜索树是很高效的用于数据查找的数据结构,但它j还是需要比较元素的大小进行查找,而哈希表可以不经过任何比较,一次就可以直接从表中得到要搜索的value值。哈希表的存储结构,让它可以通过某种哈希函数使元素的存储位置与它的关键值key之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到需要查找的元素。

所谓哈希函数就是哈希表在查找和插入数据时使用的使key和数据储存位置建立映射的函数。在本质上,查找函数就是用户传递一个key值到find函数里,find再调用哈希函数找到该数据对应的储存位置。

对于已经被插入的元素,key映射的位置一定储存在对应位置,返回true;而对于未被插入的元素,key映射的位置没有储存有效数据,返回false。

对于未被插入元素,key映射的位置也正是它应该被插入的位置,所以insert函数直接把函数插入在这里就可以了,如果元素已经被插入也就不再插入。

二、闭散列哈希表的实现

1.底层本质

对于闭散列哈希表我们更多的是需要根据它了解哈希的底层原理,重点在于后面的开散列。

(1)哈希表的存储结构

我们之前学过顺序表,哈希表在底层其实也是一个大数组,数组的下标和key值有着一一对应的关系,这样的映射关系通过哈希函数实现。

而我们在STL的学习中已经学了vector,数组就可以换为STL中的vector,在扩容时使用库中的扩容函数resize。

(2)元素的插入与查找

比方说,我们在哈希表中插入一个key为7,value为3的pair且默认底层vector可以储存10个元素,哈希函数设置为:hash(key) = key % capacity,其中capacity为存储元素底层空间的容量,这里capacity为10。

对于哈希表中数据的查找和插入都是先用哈希函数处理key得到对应位置的下标,在这里就是7%10,此时得到该数据储存的映射位置是下标为7的位置,查找数据就直接到下标处找寻即可,插入就在对应位置插入即可。

(3)哈希冲突

顾名思义,就是不同的key通过哈希函数可以映射到同一个下标位置。

比如说,我再插入一个key为17,value为4的pair,此时17%10=7,对应了同一个位置,就无法插入了。

为了解决这样的问题,哈希表提供了一种存储方式:闭散列。

它对于哈希冲突的解决方式是:先用哈希函数映射对应位置,如果该位置有数据就去向后找,直到找到空位置。(如果找到最后一个下标位置都没找到空,就从头继续找)对于插入函数,找到空位置时直接插入value,对于查找函数,找到空位置即表示该元素不在哈希表中。

(4)负载因子

在上面的闭散列哈希表中,如果哈希表中有大量的数据,所以当哈希表的vecto接近插满的情况下,寻找空位置将变得相当耗时。所以我们引入负载因子,当哈希表中效元素个数和容量的比值大于负载因子时,就对vector扩容,以保证哈希表不会过满,从而保证了O(1)的查找效率。

2.哈希函数的合理设计

(1)设计原则

首先,哈希希冲突在哈希表中是不可避免的,但可以减少。为了减少哈希冲突。它的设计原则有以下几种:

  • 哈希函数的定义域必须包括需要存储的全部关键码key,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
  • 哈希函数计算出来的地址能均匀分布在整个空间中
  • 哈希函数应该比较简单

(2)常用哈希函数

直接定址法--(常用)

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B,优点为简单、均匀,缺点是需要事先知道关键字的分布情况,适用于查找比较小且连续的数据的情况。

除留余数法--(常用)

设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数, 按照哈希函数:Hash(key) = key% p(p将关键码转换成哈希地址)

平方取中法--(了解)

假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址。

平方取中法比较适合不知道关键字的分布,而位数又不是很大的情况。

折叠法--(了解)

折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这 几部分叠加求和,并按散列表表长,取后几位作为散列地址。

折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况。

随机数法--(了解)

选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。通常应用于关键字长度不等时采用此法。

数学分析法--(了解)

设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定 相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只 有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。例如:

假设要存储某家公司员工登记表,如果用手机号作为关键字,那么极有可能前7位都是 相同 的,那么我们可以选择后面的四位作为散列地址,如果这样的抽取工作还容易出现 冲突,还 可以对抽取出来的数字进行反转(如1234改成4321)、右环位移(如1234改成4123)、左环移 位、前两数与后两数叠加(如1234改成12+34=46)等方法。

数字分析法通常适合处理关键字位数比较大的情,如果事先知道关键字的分布且关键字的 若干位分布较均匀的情况

3.类的构建

首先构建一个哈希节点类,里面包含一个pair和一个State类型的枚举变量。

这个State枚举变量的意义在于表示当前节点pair数据的有效性,由于pair中的数据内容你是不确定的,如果你以某种形态作为失效节点则会产生误判。

比如说你决定value值为-1时,该节点的数据无效,那么问题就出现了,如果我插入节点对应的value就是-1呢?那就会发生误判,所以用一个枚举变量标识每一个位置就显得十分有效了。

枚举常量包含DELETE(被删除)、EXIST(有效数据)和EMPTY(未被插入),其中第一和第三都表示无效数据,第二表示有效数据。

哈希表类的构建就很简单了,底层是一个存储节点的vector和一个元素个数统计的变量,成员函数包括insert、find、erase,还要加上一个仿函数用于传递哈希函数。

namespace closehash
{
    enum State
    {
        DELETE,
        EXIST,
        EMPTY,
    };
    
    template<class K, class V>
    struct HashtableData
    {
        std::pair<K, V> _kv;
        State _state = EMPTY;
    };


    template<class K, class V, class Hash = HashFunc<K>>
    class HashTable
    {
        typedef HashtableData<K, V> Data;
    public:
        HashTable()
        :_n(0)
        {
        _table.resize(10);
            //使用resize使vector中每一个位置都是有效元素,替代capacity
        }        
        
        bool Insert(const std::pair<K, V>& kv);       

        Data* Find(const K& key);

        
        bool Erase(const K& key);
        

    private:
        std::vector<Data> _table;
        size_t _n = 0;
    };
}

4.成员函数

(1)插入函数

insert函数就是根据待插入元素的key,用哈希函数计算出该元素的存储位置在该位置进行存放。

bool insert(const std::pair<K, V>& kv)
{
    //能找到对应值就不再插入
    if (Find(kv.first))
    {
        return false;
    }
    
    //用负载因子检测哈希表装满的成都决定是否2倍扩容,此处负载因子为0.7
    if (_n * 10 / _table.size() >= 7)
    {
        HashTable<K, V, Hash> newhastable;
    newhastable._table.resize(_table.size() * 2);
        //映射关系变了,需要重新插入每一个元素           
        for (auto& e : _table)
        {
            if (e._state == EXIST)
            {
                newhastable.Insert(e._kv);
            }
        }
        std::swap(_table ,newhastable._table);
        //交换内部变量,原来的vector出作用域自己会调用析构函数销毁
    }
    
    Hash hf;
    size_t hashi = hf(kv.first) % _table.size();
    //哈希函数得到的值还需要控制在table内部
    while (_table[hashi]._state == EXIST)
    {
        ++hashi;
        hashi %= _table.size();
        //向前不断找空位
    }
    _table[hashi]._kv = kv;
    _table[hashi]._state = EXIST;
    ++_n;
    return true;
}

(2)查找函数

find函数就是根据待插入元素的key,用哈希函数计算出该元素的存储位置在该位置检查数据存在与否。

Data* find(const K& key)
{
    Hash hf;
    size_t hashi = hf(key) % _table.size();
    int starti = hashi;
    while (_table[hashi]._state != EMPTY)
    {
        if (_table[hashi]._state == EXIST && _table[hashi]._kv.first == key)
        {
            return &(_table[hashi]);
        }
        ++hashi;
        hashi %= _table.size();
        
        //找一圈了都没有,这种可能性很小
        //有可能是位置的元素全被删除和所有位置都满了
        //第一种理论上可以出现
        //第二种根据负载因子有可能不出现
        if (starti == hashi)
        {
            return nullptr;
        }
    }
    //全是空状态
    return nullptr;
}

(3)删除函数

删除函数erase最简单,find找到就删并返回true,没找到就返回false

bool erase(const K& key)
{
    Data* ret = Find(key);
    if (ret)
    {
        ret->_state = DELETE;
        return true;
    }
    else
    {
        return false;
    }
}

三、开散列哈希表的实现

1.底层本质

闭散列哈希表这种你占我位置,我就占其他人的位置的方式对于查找数据是十分不利的。所以又出现了开散列哈希表,开散列的本质就是将vector中储存的每一个元素换成一个单链表(节省了父指针的空间),数据会头插到该位置的链表中(因为哈希表中各个元素之间是没有关系的,所以哪个在上哪个在下没有区别,尾插还需要找尾部,头插是最好的方式),哈希冲突也不需要占用他人位置。

2.类的构建

同样也是节点类和开散列哈希类,此时就不再需要枚举变量,vector存储的也变为节点的地址。

namespace openhash
{
    template<class K, class V>
    struct HashNode
    {
        HashNode(const pair<K, V> kv)
            :_kv(kv)
        , _next(nullptr)
        {}
        std::pair<K, V> _kv;
        struct hashNode* _next;
    };
    
    template<class K, class V>
    class Hashtable
    {
        typedef HashNode<K, V> Node;
    public:

        //查找函数
        Node* find(K& key);        
        
        //插入函数
        bool insert(const pair<K, V>& kv);
        
         //删除函数
        bool erase(const K& key)        
    private:
        vector<Node*> _table;
        size_t _size = 0;
    };
}

3.成员函数

(1)插入函数

先查找表中有没有key对应的节点,没有就新建一个节点插入对应位置。

由于开散列的实现是用许多链表储存元素的,所以负载因子可以扩大,我这里取1

扩容一定是异地扩容,重新插入元素。

//插入函数
bool insert(const std::pair<K, V>& kv)
{
    if (find(kv.first))
    {
        return false;
    }
    //当足够满时扩容
    if (_table.size() == _size)//负载因子为1
    {
        //异地扩容,开一个新的vector
        vector<Node*> newtable;

        newtable.resize(_size * 2, nullptr);//每次二倍扩容
        Hash hf;
        for (size_t i = 0; i < _table.size(); ++i)
        {
            Node* cur = _table[i];
            while (cur)
            {
                //构造新节点插入新表
                Node* newnode = new Node(cur->_kv);
                size_t hashi = hf(newnode->_kv.first) % newtable.size();
                //size_t hashi = Hash()(newnode->_kv.first) % newtable.size();//匿名对象的写法
                newnode->_next = newtable[hashi];
                newtable[hashi] = newnode;
                //迭代
                cur = cur->_next;
            }
        }
        _table.swap(newtable);
    }
    //插入元素
    Node* newnode = new Node(kv);
    size_t hashi = Hash()(newnode->_kv.first) % _table.size();//匿名对象
    newnode->_next = _table[hashi];
    _table[hashi] = newnode;
    ++_size;
}

(2)查找函数

用户传递key,find通过哈希函数映射到对应链表,在链表中寻找即可

//查找函数
Node* find(const K& key)
{
    //防止后续除零错误
    if (_size == 0)
        return false;
    Hash hf;
    size_t hashi = hf(key) % _table.size();
    Node* cur = _table[hashi];
    while (cur)
    {
        if (cur->_kv.first == key)//找到了返回该节点
        {
            return cur;
        }
        cur = cur->_next;//没找到接着往下找
    }
    return nullptr;
}

(3)删除函数

使用find的思想,在链表中查找时增加一个上一个节点的指针用于链接,而且要分为头删和非头删两种情况。

//删除函数
bool erase(const K& key)
{
    //防止后续除零错误
    if (_size == 0)
        return false;
    Hash hf;
    size_t hashi = hf(key) % _table.size();
    Node* cur = _table[hashi];
    Node* prev = nullptr;
    while (cur)
    {
        if (cur->_kv.first == key)//找到了该节点
        {
            if (prev)//非头删
            {
                prev->_next = cur->_next;
                delete cur;
            }
            else//头删
            {
                _table[hashi] = cur->_next;
                delete cur;
            }
            --_size;
            return true;
        }
        prev = cur;
        cur = cur->_next;//没找到接着往下找
    }
    return false;//走到空了都找不到就是没有
}

(4)构造函数析和构函数

构造函数主要确定我们初始vector可储存的链表个数。

闭散列的析构函数直接使用vector和各节点的析构函数即可,开散列的vector可以使用vector的析构,而vector中的每一个链表就需要我们手动释放了。

HashTable()
    :_size(0)
{
    _table.resize(10, nullptr);//起始vector容量为10

}

~HashTable()
{
    for (int i = 0; i < _table.size(); ++i)
    {
        Node* cur = _table[i];
        while (cur)
        {
            Node* next = cur->_next;
            cur->_next = nullptr;
            delete cur;
            cur = next;
        }
    _table[i] = nullptr;
    }
}

(5)两种扩容方式

在上面的insert函数中,我们vector每次扩容都是从10开始,每次扩大两倍。

而在STL中,unordered_map和unordered_set会使用一系列接近二倍的奇数作为容量的大小。

上面的这种方式需要一个函数:

//由于哈希表定值使用对容器容量取余的方式,所以选用一组间隔数据接近2倍的质数就可以有效减少哈希冲突
//STL中也是使用这一样的方式
inline unsigned long __stl_next_prime(unsigned long n)
{
    static const int __stl_num_primes = 28;
    static const unsigned long __stl_prime_list[__stl_num_primes] =
    {
        53, 97, 193, 389, 769,
        1543, 3079, 6151, 12289, 24593,
        49157, 98317, 196613, 393241, 786433,
        1572869, 3145739, 6291469, 12582917, 25165843,
        50331653, 100663319, 201326611, 402653189, 805306457,
        1610612741, 3221225473, 4294967291
    };

    for (int i = 0; i < __stl_num_primes; ++i)
    {
        if (__stl_prime_list[i] > n)
        {
            return __stl_prime_list[i];
        }
    }
    
    return __stl_prime_list[__stl_num_primes - 1];
}

然后同时修改插入和构造函数。

//构造函数
HashTable()
    :_size(0)
{
    _table.resize(__stl_next_prime(0), nullptr);//容量按质数列控制
}

//插入函数
bool insert(const std::pair<K, V>& kv)
{
    if (find(kv.first))
    {
        return false;
    }
    //当足够满时扩容
    if (_table.size() == _size)//负载因子为1
    {
        //异地扩容,开一个新的vector
        vector<Node*> newtable;
        
        newtable.resize(__stl_next_prime(_table.size()), nullptr);//这里不再是二倍扩容
        

        Hash hf;
        for (size_t i = 0; i < _table.size(); ++i)
        {
            Node* cur = _table[i];
            while (cur)
            {
                //构造新节点插入新表
                Node* newnode = new Node(cur->_kv);
                size_t hashi = hf(newnode->_kv.first) % newtable.size();
                //size_t hashi = Hash()(newnode->_kv.first) % newtable.size();//匿名对象的写法
                newnode->_next = newtable[hashi];
                newtable[hashi] = newnode;
                //迭代
                cur = cur->_next;
            }
        }
        _table.swap(newtable);
    }
    //插入元素
    Node* newnode = new Node(kv);
    size_t hashi = Hash()(newnode->_kv.first) % _table.size();//匿名对象
    newnode->_next = _table[hashi];
    _table[hashi] = newnode;
    ++_size;
}

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

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

相关文章

【力扣--622】设计循环队列

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 : 数据结构 &#x1f606;今日分享 : 丢脸其实并没有那么可怕&#xff0c;我们可以从另一个角度来想&#xff1a;别人能够记住我了&#xff0c;而且过了还有多少人能记得我呢&#xff1f;虽然这种出场不太优雅&#x1f606; 设…

python制作散点动图

目录 示例1&#xff1a;简单的散点图示例2&#xff1a;添加颜色和大小示例3&#xff1a;实时更新动图完整代码 本教程将介绍如何使用Python制作散点动图。我们将通过三个示例代码&#xff0c;从易到难&#xff0c;逐步说明如何使用Python绘制出散点动图。 示例1&#xff1a;简单…

图书馆客流人数统计分析系统方案

智慧客流人数统计分析系统可以帮助图书馆管理者更好地管理人群流量。系统能够自动统计区域内的人流量高峰期&#xff0c;并通过数据分析提供更加合理的管控&#xff0c;从而提区域内人群流动性&#xff0c;避免拥堵的情况。 AI客流视觉监控 客流量管控分析系统意义 讯鹏客流量管…

雪花算法生成ID

1.简介 SnowFlake 中文意思为雪花&#xff0c;故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。 雪花算法的原理就是生成一个的 64 位比特位的 long 类型的唯一 id。 最高 1 位固定值 0&#xff0c;因为生成的 id 是…

Python的HTTP库及示例

13.3 HTTP库 HTTP&#xff08;Hyper Text Transfer Protocol&#xff09;是一个客户端和服务器端请求和应答的标准。客户端是终端用户&#xff0c;服务器端是网站。客户端发起一个到服务器上指定端口的HTTP请求&#xff0c;服务器向客户端发回一个状态行和响应的消息。 可以…

ICV: 2025年全球QKD产业规模有望达到25亿美元

近日&#xff0c;专注于量子、智能驾驶等前沿科技领域的国际咨询机构ICV发布了《全球量子安全通信产业研究报告》&#xff0c;报告主要内容包括量子安全通信的概念、量子密钥分发 (QKD)发展历程、QKD的优势、产业链、应用领域、QKD产业规模及预测、全球主要参与者。 量子安全通…

ubuntu18.04中PCL点云库依赖的安装以及PCL点云库和Eigen的卸载与安装

一、PCL点云库依赖的安装、遇到问题的解决方法 sudo add-apt-repository ppa:v-launchpad-jochen-sprickerhof-de/pcl sudo apt-get update sudo apt-get install libpcl-all 以上是官方给出的&#xff0c;但是会出很多报错&#xff0c;这里建议自己配置。 配置步骤&#xff…

【Android学习专题】安卓样式学习(学习内容记录)

学习记录内容来自《Android编程权威指南&#xff08;第三版&#xff09;》 样式调整和添加 调整颜色资源&#xff08;res/values/colors.xml&#xff09; 格式&#xff1a; 添加样式&#xff08;res/values/styles.xml&#xff09;&#xff0c;&#xff08;创建BeatBox项目时…

开源狂潮让巨头惨败!谷歌内部文件曝光:我们和OpenAI都没有护城河

因为开源&#xff0c;AI军备竞赛&#xff0c;谷歌和OpenAI全是输家&#xff1f; 来源丨新智元 重磅&#xff01; 谷歌的一份名为《我们没有护城河&#xff0c;OpenAI也没有》的内部文件疑似被泄露&#xff0c; 今天早上&#xff0c;外媒SemiAnalysis公布了这份重磅炸弹泄露…

日撸 Java 三百行day45

文章目录 说明day45 冒泡排序1. 基本思路2.代码 说明 闵老师的文章链接&#xff1a; 日撸 Java 三百行&#xff08;总述&#xff09;_minfanphd的博客-CSDN博客 自己也把手敲的代码放在了github上维护&#xff1a;https://github.com/fulisha-ok/sampledata day45 冒泡排序 …

IP地址技术原理

IP地址用于唯一地标识一个网络设备&#xff08;如计算机、路由器等&#xff09;在互联网或局域网中的位置。IP地址由32位二进制数表示&#xff0c;通常分为4个8位二进制数&#xff0c;每个数用点号隔开&#xff0c;如106.110.92.215。IP地址可用于数据传输、网络管理和安全控制…

Springboot +Flowable,服务任务ServiceTask执行的三种方式(一)

一.简介 ServiceTask 从名字上看就是服务任务&#xff0c;它的图标是像下面这样&#xff0c;截图如下&#xff1a; ServiceTask 一般由系统自动完成&#xff0c;当流程走到这一步的时候&#xff0c;不会自动停下来&#xff0c;而是会去执行我们提前在 ServiceTask 中配置好的…

托福高频真词List06 // 附阅读真题 14:26~15:12 // 15:12~16:12

目录 生词 熟词 阅读真题 生词 inducecausecause the formation of increasestimulate&#xff08;导致&#xff09; v 导致 inevitableunavoidable certainnecessary&#xff08;不可避免地&#xff09; adj 不可避免的accommodateallowv 顺应 modestreasonablesmall modera…

【Git 教程】—git指令详细解析,上机操作,简单易懂

目录 一. 集中式和分布式的区别 1. 集中式版本控制工具 2. 分布式版本控制工具 3. 基本概念 4. 配置信息 二. 创建本地仓库&#xff08;git init) 三.基础指令 1. 获取git版本号 2.查看文件的状态 3. 查看提交记录 4. 版本回退 5. git reflog 6. .gitignore文件…

数据机房温湿度监控系统解决应用案例

机房温湿度监控系统之POE以太网温湿度传感器解决方案 物联网工业级高精度传感器 以太网智能RJ45温湿度传感器&#xff08;POE12VDC供电型&#xff09; 智 能 传 感 器 介 绍 目 录 一、关键词…………………………………………………………………………3 二、 产品概述…

51单片机 | DS18B20 温度传感器实验

系统设计 设计要求 (1) 主体电路包括晶振、复位、电源指示电路&#xff0c;预留程序下载接口&#xff1b; (2) 要求能够实现温度数值的自动显示&#xff0c;值根据实际温度变化自动变化。 (3) 并按照题目要求完成程序源码编写、调试及关键代码注释。 设计任务 使用给定硬件…

C++两个区间的比较?——STL之equal()算法和mismatch()算法

equal&#xff08;&#xff09;算法 检验相等性 bool equal(InputIterator1 beg,InputIterator1 end&#xff0c;InputIterator2 cmpBeg)bool equal(InputIterator1 beg,InputIterator1 end&#xff0c;InputIterator2 cmpBeg,BinaryPredicate op) 第一形式判断区间…

mongodb命令使用

查询副本集当前状况(需要将命令行切换到需要查询的副本集中) rs.status() 查询数据库当前情况 sh.status() 查询数据分片情况 db.集合名称.getShardDistribution() 查看集合当前情况 db.集合名称.stats() 查看集合是否开启分片 db.集合名称.stats().sharded 查看mong…

Python网络编程(一)——了解IP和端口的基础知识以及socket的简单实现

系列文章目录 Python网络编程&#xff08;一&#xff09;——了解IP和端口的基础知识以及socket的简单实现 了解IP和端口的基础知识以及socket的简单实现 系列文章目录前言获取本机设备名和IP获取远程设备的IP地址将IPv4地址转换成以十六进制形式表示二进制数据获取端口的服务…

数据结构 -- 共用体Union

在数据结构 -- 结构体Struct一文中详细介绍了结构体的定义以及内存对齐。在C语言中&#xff0c;还有另外一种和结构体非常类似的语法&#xff0c;叫做共用体&#xff08;Union&#xff09;&#xff0c;也称为联合体。它的定义格式为&#xff1a; union 共用体名{成员列表 };1. …