STL容器之unordered_map类

文章目录

  • STL容器之unordered_map类
    • 1、unordered_map
      • 1.1、unordered_map介绍
      • 1.2、unordered_map的使用
        • 1.2.1、unordered_map的常见构造
        • 1.2.2、unordered_map的迭代器
        • 1.2.3、unordered_map的容量
        • 1.2.4、unordered_map的增删查
        • 1.2.5、unordered_map的桶操作
    • 2、unordered_multimap
    • 3、unordered_map的底层结构
    • 4、unordered_set的模拟实现
      • 4.1、改造哈希表(链表法)
      • 4.2、MyUnordered_Map类的构成

img

STL容器之unordered_map类

1、unordered_map

1.1、unordered_map介绍

unordered_map的文档介绍

  1. unordered_map是以不特定顺序存储独特元素的容器,并允许根据其值快速检索单个元素。

  2. 在unordered_map中,键值通常用于唯一标识元素,而映射值是具有与此键关联内容的对象(pair<K,V>)。键是不可变的。键和映射值的类型可能不同。

  3. 在内部,unordered_map中的元素没有按任何特定顺序排序,而是根据其哈希值组织成桶,以便通过其值直接快速访问单个元素(平均时间复杂度恒定)。

  4. unordered_map容器比map容器更快地通过其键访问单个元素,但是它通常在遍历元素子集的范围迭代方面效率较低。

  5. unordered_map实现了直接访问运算符(运算符[]),它允许使用其键值作为参数直接访问映射值。

  6. 容器中的迭代器至少是前向iterators。


1.2、unordered_map的使用

1.2.1、unordered_map的常见构造
(constructor )函数名称接口说明
unordered_map ( size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type() )构造空的unordered_map
unordered_map ( InputIterator first, InputIterator last, size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type() )用[first,last)迭代区间构造unordered_map
unordered_map ( const unordered_map& ump )unordered_map的拷贝构造
void test_um1() {
     unordered_map<int, int> um;
     int a[]{1, 2, 5, 7, 2, 3, 5, 8, 2, 11, 9};

     for (auto e: a) {
           um.insert(make_pair(e, e));
     }
     for (auto e: um) {
           cout << e.first << ":" << e.second << endl;
     }
     cout << "=======================" << endl;
 
    unordered_map<int, int> um1(um.begin(), um.end());
     for (auto e: um1) {
         cout << e.first << ":" << e.second << endl;
       }
     cout << "=======================" << endl;
 
     unordered_map<int, int> um2(um1);
    for (auto e: um2) {
         cout << e.first << ":" << e.second << endl;
     }
   
 }

1.2.2、unordered_map的迭代器
函数名称接口说明
begin()+end()获取第一个元素位置的iterator和获取最后一个元素的后一个位置的iterator
cbegin()+cend()获取第一个元素位置的const_iterator和获取最后一个元素的后一个位置的const_iterator
void test_um2() {
     unordered_map<int, int> um;
     int a[]{1, 2, 5, 7, 2, 3, 5, 8, 2, 11, 9};
     for (auto e: a) {
           um.insert(make_pair(e, e));
     }
     unordered_map<int, int>::iterator it = um.begin();
     while (it != um.end()) {
           cout << it->first << ":" << it->second << endl;
           ++it;
     }
 
    cout << "=======================" << endl;
 
    unordered_map<int, int>::const_iterator cit = um.cbegin();
 
     while (cit != um.cend()) {
           cout << cit->first << ":" << cit->second << endl;
           ++cit;
     }
 }

1.2.3、unordered_map的容量
函数名称接口说明
empty判断当前unordered_map是否为空
size获取unordered_map的元素个数
void test_um3() {
   unordered_map<int, int> um;
   int a[]{1, 2, 5, 7, 2, 3, 5, 8, 2, 11, 9};
   for (auto e: a) {
          um.insert(make_pair(e, e));
   }
   cout << um.empty() << endl;
   cout << um.size() << endl;

   um.clear();
   cout << um.empty() << endl;
   cout << um.size() << endl;
}

1.2.4、unordered_map的增删查
函数名称接口说明
insert在unordered_map中插入pair<key,value>键值对,如果插入成功,返回<key插入位置,true>,插入失败说明unordered_map中已经有key,返回<key在unordered_map的位置,false>
erase删除unordered_map中pos位置的元素,或者删除值为val的元素
swap交换两个unordered_map的元素
clear将unordered_map的元素置空
find返回unordered_map中值为x的元素位置
count返回unordered_map中值为x的元素个数
operator[]访问key元素的映射值value,如果key不存在,则插入pair<key,V()>并返回V();如果key存在,则返回对应的value
void test_um4() {
    unordered_map<int, int> um;
    int a[]{1, 2, 5, 7, 2, 3, 5, 8, 2, 11, 9};
    for (auto e: a) {
          um.insert(make_pair(e, e));
    }
    for (auto e: um) {
          cout << e.first << ":" << e.second << endl;
    }
    cout << "========================" << endl;
    um.erase(1);
    um.erase(2);

    auto pos = um.find(11);
    um.erase(pos);
    for (auto e: um) {
        cout << e.first << ":" << e.second << endl;
      }
    cout << "========================" << endl;

    unordered_map<int, int> um1;
    um1.insert(make_pair(100, 100));
    um1.swap(um);
    cout << "um:";
    for (auto e: um) {
        cout << e.first << ":" << e.second << endl;
    }
  
    cout << "um1:";
    for (auto e: um1) {
        cout << e.first << ":" << e.second << endl;
    }

      cout << "========================" << endl;

    cout << um1.count(5) << endl;
    cout << um1.count(1) << endl;
    cout << "========================" << endl;

    um1.clear();
    cout << "um:";
    for (auto e: um) {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "um1:";
    for (auto e: um1) {
          cout << e.first << ":" << e.second << endl;
    }

}

1.2.5、unordered_map的桶操作
函数名称接口说明
bucket_count返回unordered_map中的桶的个数
bucket_size返回第n个桶中元素的个数
bucket返回元素k在哪个桶
void test_um5() {
    unordered_map<int, int> um;
    int a[]{1, 2, 5, 7, 2, 3, 5, 8, 2, 11, 9};
    for (auto e: a) {
        um.insert(make_pair(e, e));
    }

    cout << um.bucket_count() << endl;
    cout << um.bucket_size(1) << endl;
    cout << um.bucket(11) << endl;
}


2、unordered_multimap

这个其实就和map和multimap一样,就是比unordered_map多了一个可以存重复值的特性。

演示一下:

void test_umm1() {
       unordered_multimap<int, int> ummp;
       int a[]{1, 2, 5, 7, 2, 3, 5, 8, 2, 11, 9};
       for (auto e: a) {
           ummp.insert(make_pair(e, e));
       }

       for (auto e: ummp) {
           cout << e.first << ":" << e.second << endl;
       }
       cout << endl;
}

3、unordered_map的底层结构

unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构。

unordered_map和unordered_set一样,底层都是用的哈希表,都是用的链表法。

想了解底层结构可以看STL容器之unordered_set类这篇文章


4、unordered_set的模拟实现

以下我们自己模拟实现的时候,unordered_set的模拟实现我们使用MyUnordered_Set类用于区分,unordered_map的模拟实现我们使用MyUnordered_Map类用于区分

4.1、改造哈希表(链表法)

结点的结构体需要修改一下,之前我们存的是pair<K,V>,但MyUnordered_Set是存K的,因此我们模版参数设置为一个参数T,MyUnordered_Set调用就是K,MyUnordered_Map调用就是pair<K,V>

template<class T>
struct HashNode {
 typedef HashNode<T> Node;

 Node *_next;
 T _data;

 HashNode(const T &data) : _data(data), _next(nullptr) {}

};

哈希表需要增加一个模版参数:取Key的仿函数,因为MyUnordered_Set插入的是K,但是MyUnordered_Map插入的是pair<K,V>,我们需要从pair<K,V>中取出K。因此插入也要改成bool Insert(const T &data){}

这个仿函数分别放在MyUnordered_SetMyUnordered_Map的类中,用来传给哈希表。

MyUnordered_Set类中:

struct MapKeyOfT {
     K operator()(const pair<K, V> &kv) {
         return kv.first;
     }
};

MyUnordered_Map类中:

struct MapKeyOfT {
     K operator()(const pair<K, V> &kv) {
         return kv.first;
     }
};

因此当前改造后的哈希表为:

template<class T>
struct HashNode {
 typedef HashNode<T> Node;

 Node *_next;
 T _data;

 HashNode(const T &data) : _data(data), _next(nullptr) {}

};

template<class K>
struct HashFunc {
 size_t operator()(const K &key) {
     return (size_t) key;
 }
};

template<>
struct HashFunc<string> {
 size_t operator()(const string &str) {
     size_t hash = 0;
     for (auto e: str) {
         hash += e;
         e *= 131;
     }
     return hash;
 }
};

template<class K, class T, class KeyOfT, class Hash>
class HashTable {
 typedef HashNode<T> Node;
public:

 ~HashTable() {
     for (int i = 0; i < _tables.size(); ++i) {
         _tables[i] = nullptr;
     }
 }

 HashTable(size_t size = 10) {
     _tables.resize(size, nullptr);// 各结点初始化为空
 }

 Node* Find(const K &key) {
     Hash hs;
     KeyOfT kot;
     // 先算出映射到哪里
     int hashi = hs(key) % _tables.size();
     Node *cur = _tables[hashi];
     while (cur) {
         if (kot(cur->_data) == key) {
             return cur;
         }
         cur = cur->_next;
     }
     return nullptr; // 没找到
 }

 bool Erase(const K &key) {
     Hash hs;
     KeyOfT kot;
     iterator ret = Find(key);
     if (ret != end()) {
         int hashi = hs(key) % _tables.size();
         Node *cur = _tables[hashi];
         Node *prev = nullptr;
         while (cur) {
             if (kot(cur->_data) == key) {
                 if (prev == nullptr)
                     _tables[hashi] = cur->_next; // 要删除的就是表中的结点,那么将它的下一个结点放到表中
                 else
                     prev->_next = cur->_next; // 要删除的是表中的结点的后继中的某个

                 delete cur; // 删除该结点并退出循环
                 break;
             }
             prev = cur;
             cur = cur->_next;
         }
         --_n;
         return true;
     } else {
         return false;
     }
 }

 bool Insert(const T &data) {

     Hash hs;
     KeyOfT kot;
     if (Find(kot(data)))
         return false;// 找到了
     if (_n == _tables.size()) {
         // 扩容
         vector<Node *> newTables;
         newTables.resize(2 * _tables.size(), nullptr);
         for (int i = 0; i < _tables.size(); ++i) {
             Node *cur = _tables[i];
             while (cur) {
                 Node *next = cur->_next; //记录下一个结点位置,防止找不到下一个结点
                 // 映射到新表位置
                 int hashi = hs(kot(cur->_data)) % newTables.size();
                 // 插入到新表位置
                 Node *newcur = newTables[hashi];
                 if (newcur) {
                     // 当前表中的结点不为空,则头插并链接
                     cur->_next = newcur;
                     newTables[hashi] = cur;
                 } else {
                     // 当前表中的结点为空,则新结点直接放到这个表中
                     newTables[hashi] = cur;
                     cur->_next = nullptr;// 新插入的cur的_next不一定是空
                 }
                 cur = next;// 继续往下找
             }
         }
         _tables.swap(newTables);
     }

     // 先算出映射到哪里
     int hashi = hs(kot(data)) % _tables.size();
     Node *cur = _tables[hashi];
     Node *newnode = new Node(data);
     if (cur) {
         // 当前表中的结点不为空,则头插并链接
         newnode->_next = cur;
         _tables[hashi] = newnode;
     } else {
         // 当前表中的结点为空,则新结点直接放到这个表中
         _tables[hashi] = newnode;
     }
     ++_n;
     return true;
 }

private:
 vector<Node *> _tables;
 size_t _n = 0;
};

还需要增加迭代器,用来遍历哈希表。

// 前置声明
template<class K, class T, class KeyOfT, class Hash>
class HashTable;

template<class K, class T, class KeyOfT, class Hash>
struct __HTIterator {

 typedef HashNode<T> Node;
 typedef HashTable<K, T, KeyOfT, Hash> HT;
 typedef __HTIterator<K, T, KeyOfT, Hash> Self;

 Node *_node;
 HT *_ht;

 __HTIterator(Node *node, HT *ht) : _node(node), _ht(ht) {}


 T &operator*() {
     return _node->_data;
 }

 T *operator->() {
     return &_node->_data;
 }

 Self &operator++() {
     if (_node->_next) {
         // 当前结点还有后继结点
         _node = _node->_next;
     } else {
         // 当前桶走完了
         KeyOfT kot;
         Hash hs;

         // 先算出当前在哪个哈希桶
         int hashi = hs(kot(_node->_data)) % _ht->_tables.size();
         ++hashi;// 走到下一个桶

         while (hashi < _ht->_tables.size()) {
             if (_ht->_tables[hashi]) {
                 //  这个桶有结点
                 _node = _ht->_tables[hashi];
                 break;
             } else
                 ++hashi;// 这个桶没有有结点
         }

         if (hashi == _ht->_tables.size()) {
             // 走到最后了,没找到下一个位置
             _node = nullptr;
         }

     }
     return *this;
 }

 bool operator!=(const Self &s) {
     return s._node != _node;
 }
};

改造后的哈希表(终极版):这里的插入的返回值由bool变为了pair<iterator,bool>,和官方保持一致。

template<class T>
struct HashNode {
 typedef HashNode<T> Node;

 Node *_next;
 T _data;

 HashNode(const T &data) : _data(data), _next(nullptr) {}

};

template<class K>
struct HashFunc {
 size_t operator()(const K &key) {
     return (size_t) key;
 }
};

template<>
struct HashFunc<string> {
 size_t operator()(const string &str) {
     size_t hash = 0;
     for (auto e: str) {
         hash += e;
         e *= 131;
     }
     return hash;
 }
};


// 前置声明
template<class K, class T, class KeyOfT, class Hash>
class HashTable;

template<class K, class T, class KeyOfT, class Hash>
struct __HTIterator {

 typedef HashNode<T> Node;
 typedef HashTable<K, T, KeyOfT, Hash> HT;
 typedef __HTIterator<K, T, KeyOfT, Hash> Self;

 Node *_node;
 HT *_ht;

 __HTIterator(Node *node, HT *ht) : _node(node), _ht(ht) {}


 T &operator*() {
     return _node->_data;
 }

 T *operator->() {
     return &_node->_data;
 }

 Self &operator++() {
     if (_node->_next) {
         // 当前结点还有后继结点
         _node = _node->_next;
     } else {
         // 当前桶走完了
         KeyOfT kot;
         Hash hs;

         // 先算出当前在哪个哈希桶
         int hashi = hs(kot(_node->_data)) % _ht->_tables.size();
         ++hashi;// 走到下一个桶

         while (hashi < _ht->_tables.size()) {
             if (_ht->_tables[hashi]) {
                 //  这个桶有结点
                 _node = _ht->_tables[hashi];
                 break;
             } else
                 ++hashi;// 这个桶没有有结点
         }

         if (hashi == _ht->_tables.size()) {
             // 走到最后了,没找到下一个位置
             _node = nullptr;
         }

     }
     return *this;
 }

 bool operator!=(const Self &s) {
     return s._node != _node;
 }
};

template<class K, class T, class KeyOfT, class Hash>
class HashTable {
 friend __HTIterator<K, T, KeyOfT, Hash>;// 为了能访问_tables
 typedef HashNode<T> Node;
public:

 typedef __HTIterator<K, T, KeyOfT, Hash> iterator;

 iterator begin() {
     for (int i = 0; i < _tables.size(); ++i) {
         if (_tables[i])
             return iterator(_tables[i], this);// 从左找到第一个哈希桶的第一个结点
     }
     return end();
 }

 iterator end() {
     return iterator(nullptr, this);
 }

 ~HashTable() {
     for (int i = 0; i < _tables.size(); ++i) {
         _tables[i] = nullptr;
     }
 }

 HashTable(size_t size = 10) {
     _tables.resize(size, nullptr);// 各结点初始化为空
 }

 iterator Find(const K &key) {
     Hash hs;
     KeyOfT kot;
     // 先算出映射到哪里
     int hashi = hs(key) % _tables.size();
     Node *cur = _tables[hashi];
     while (cur) {
         if (kot(cur->_data) == key) {
             return iterator(cur, this);
         }
         cur = cur->_next;
     }
     return iterator(nullptr, this); // 没找到
 }

 bool Erase(const K &key) {
     Hash hs;
     KeyOfT kot;
     iterator ret = Find(key);
     if (ret != end()) {
         int hashi = hs(key) % _tables.size();
         Node *cur = _tables[hashi];
         Node *prev = nullptr;
         while (cur) {
             if (kot(cur->_data) == key) {
                 if (prev == nullptr)
                     _tables[hashi] = cur->_next; // 要删除的就是表中的结点,那么将它的下一个结点放到表中
                 else
                     prev->_next = cur->_next; // 要删除的是表中的结点的后继中的某个

                 delete cur; // 删除该结点并退出循环
                 break;
             }
             prev = cur;
             cur = cur->_next;
         }
         --_n;
         return true;
     } else {
         return false;
     }
 }

 pair<iterator, bool> Insert(const T &data) {

     Hash hs;
     KeyOfT kot;
     iterator it = Find(kot(data));
     if (it != end())
         return make_pair(it, false);// 找到了
     if (_n == _tables.size()) {
         // 扩容
         vector<Node *> newTables;
         newTables.resize(2 * _tables.size(), nullptr);
         for (int i = 0; i < _tables.size(); ++i) {
             Node *cur = _tables[i];
             while (cur) {
                 Node *next = cur->_next; //记录下一个结点位置,防止找不到下一个结点
                 // 映射到新表位置
                 int hashi = hs(kot(cur->_data)) % newTables.size();
                 // 插入到新表位置
                 Node *newcur = newTables[hashi];
                 if (newcur) {
                     // 当前表中的结点不为空,则头插并链接
                     cur->_next = newcur;
                     newTables[hashi] = cur;
                 } else {
                     // 当前表中的结点为空,则新结点直接放到这个表中
                     newTables[hashi] = cur;
                     cur->_next = nullptr;// 新插入的cur的_next不一定是空
                 }
                 cur = next;// 继续往下找
             }
         }
         _tables.swap(newTables);
     }

     // 先算出映射到哪里
     int hashi = hs(kot(data)) % _tables.size();
     Node *cur = _tables[hashi];
     Node *newnode = new Node(data);
     if (cur) {
         // 当前表中的结点不为空,则头插并链接
         newnode->_next = cur;
         _tables[hashi] = newnode;
     } else {
         // 当前表中的结点为空,则新结点直接放到这个表中
         _tables[hashi] = newnode;
     }
     ++_n;
     return make_pair(iterator(_tables[hashi], this), true);
 }

private:
 vector<Node *> _tables;
 size_t _n = 0;
};

4.2、MyUnordered_Map类的构成

其实就是套了一层哈希表,多了一个MapKeyOfT用来传给哈希表的模版参数。

#include "HashTable_Bucket.h"

template<class K, class V, class Hash = HashFunc<K>>
class MyUnordered_Map {
    struct MapKeyOfT {
        K operator()(const pair<K, V> &kv) {
            return kv.first;
        }
    };

public:

    typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;

    iterator begin() {
        return _hs.begin();
    }

    iterator end() {
        return _hs.end();
    }

    pair<iterator, bool> insert(const pair<K, V> &kv) {
        return _hs.Insert(kv);
    }

    bool erase(const K &key) {
        return _hs.Erase(key);
    }

    iterator find(const K &key) {
        return _hs.Find(key);
    }

    V &operator[](const K &key) {
        pair<iterator, bool> ret = insert(make_pair(key, V()));
        return ret.first->second;
    }

private:
    HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _hs;
};


OKOK,C++ STL容器之unordered_map类就到这里。如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

白盒测试-条件覆盖

​ 条件覆盖是指运行代码进行测试时&#xff0c;程序中所有判断语句中的条件取值为真值为假的情况都被覆盖到&#xff0c;即每个判断语句的所有条件取真值和假值的情况都至少被经历过一次。 ​ 条件覆盖率的计算方法为&#xff1a;测试时覆盖到的条件语句真、假情况的总数 / 程…

redis开源协议变更了?我们还能用吗?

Redis是一款广泛使用的开源键值存储数据库&#xff0c;其开源协议的变更引起了社区和行业的广泛关注。根据搜索结果&#xff0c;Redis Labs宣布Redis将采用双重源代码可用许可证&#xff08;RSALv2&#xff09;和服务器端公共许可证&#xff08;SSPLv1&#xff09;&#xff0c;…

AI自我推理和规划,OpenAI和Meta今年要打开“潘多拉盒子”了 油价100美元几乎已经成了华尔街的共识

OpenAI和Meta今年要打开“潘多拉盒子”了 OpenAI首席运营官Brad Lightcap表示&#xff0c;“我们将开始看到AI能够以更复杂的方式执行更复杂的任务。” OpenAI和Meta正准备发布新的AI模型&#xff0c;他们称这些模型将能够进行自我推理和规划&#xff0c;而这是实现机器“超…

Jmeter —— 自动录制脚本

1、Jmeter配置 1.1新增一个线程组 1.2Jmeter中添加HTTP代理 1.3配置HTTP代理服务器 修改端口 修改Target Cintroller(目标控制器) 修改Grouping(分组) 编辑录制中的包含和排除 在“URL Patterns to include包含模式”中填入.*(123456).*用以过滤请求地址中不包含123456的请求…

设计模式-接口隔离原则

基本介绍 客户端不应该依赖它不需要的接口&#xff0c;即一个类对另一个类的依赖应该建立在最小的接口上先看一张图: 类A通过接口Interface1 依赖类B&#xff0c;类C通过接口Interface1 依赖类D&#xff0c;如果接口Interface1对于类A和类C来说不是最小接口&#xff0c;那么类…

LangChain入门:17.使用 ConversationChain实现对话记忆功能

在默认情况下&#xff0c;无论是 LLM 还是代理都是无状态的&#xff0c;每次模型的调用都是独立于其他交互的。也就是说&#xff0c;我们每次通过 API 开始和大语言模型展开一次新的对话&#xff0c;它都不知道你其实昨天或者前天曾经和它聊过天了。 你肯定会说&#xff0c;不可…

3.2.k8s搭建-kubeadm

目录 一、虚拟机准备 二、所有节点环境准备 1.所有节点做hosts解析 2.所有节点重新命名 3.所有节点安装docker 4.所有节点为docker做linux内核转发 5.所有节点配置docker 6.所有节点关闭swap分区 7.所有节点验证网卡硬件编号是否冲突 8.所有节点配置允许iptables桥接…

【HTML】简单制作一个分形动画

目录 前言 开始 HTML部分 效果图 ​编辑​编辑​编辑​编辑总结 前言 无需多言&#xff0c;本文将详细介绍一段代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建一个文本文档&#xff0c;其中HTML的文件名改为[index.html]&a…

【原创教程】Smart200通过Modbus RTU实现V90位置控制的方法

1 绪论 1.1 本文的目的 S7-200Smart 可通过标准的 Modbus 功能块发送伺服驱动器的控制指令及读写驱动器的参数。本文详细叙述了 S7-200 SMART PLC 通过 Modbus RTU 通信连接 SINAMICS V90 实现内部位置的 MDI 功能。(MDI(Manual Data Input)称为设定值直接给定运行方式。…

岛屿个数c++

参考文章 岛屿个数1岛屿个数2 题目 输入样例&#xff1a; 2 5 5 01111 11001 10101 10001 11111 5 6 111111 100001 010101 100001 111111输出样例&#xff1a; 1 3样例解释 对于第一组数据&#xff0c;包含两个岛屿&#xff0c;下面用不同的数字进行了区分&#xff1a; 0…

计算机网络-TCP基础、三次挥手、四次握手过程

TCP基础 定义&#xff1a;TCP是面向连接的、可靠的、基于字节流的传输层通信协议。这意味着在发送数据之前&#xff0c;TCP需要建立连接&#xff0c;并且它能确保数据的可靠传输。此外&#xff0c;TCP将数据视为无结构的连续字节流。面向连接&#xff1a;TCP只能一对一进行连接…

Harmony与Android项目结构对比

主要文件对应 Android文件HarmonyOS文件清单文件AndroidManifest.xmlmodule.json5Activity/Fragmententryability下的ts文件XML布局pages下的ets文件resresourcesModule下的build.gradleModule下的build-profile.json5gradlehvigor根目录下的build.gradle根目录下的build-profi…

动态内存管理详解

一.为什么要存在动态内存分配&#xff1a; 下图是不同类型数据在内存中的分配&#xff1a; 上述的开辟空间的⽅式有两个特点&#xff1a; • 空间开辟⼤⼩是固定的。 • 数组在申明的时候&#xff0c;必须指定数组的⻓度&#xff0c;数组空间⼀旦确定了⼤⼩不能调整 但是对…

DeepStream做对象模糊的几种方法

有时候&#xff0c;我们需要对视频的敏感信息做模糊处理&#xff0c;比如模糊人脸&#xff0c;车牌。 有时候&#xff0c;也需要对整帧做模糊&#xff0c;或者遮挡。比如这个例子。 下面介绍几种模糊的办法。 1. 通过nvosd deepstream-test1是DeepStream最简单的一个例子&…

基于SpringBoot的“垃圾分类网站”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“垃圾分类网站”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 系统功能界面图 用户登录、用户注…

基于java+springboot+vue实现的人事管理系统(文末源码+Lw)23-242

摘 要 使用旧方法对人事管理系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在人事管理系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发的人事管理…

时序预测 | Matlab实现SSA-ESN基于麻雀搜索算法(SSA)优化回声状态网络(ESN)的时间序列预测

时序预测 | Matlab实现SSA-ESN基于麻雀搜索算法(SSA)优化回声状态网络(ESN)的时间序列预测 目录 时序预测 | Matlab实现SSA-ESN基于麻雀搜索算法(SSA)优化回声状态网络(ESN)的时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现SSA-ESN基于麻雀搜索…

SQL 注入之 Windows/Docker 环境 SQLi-labs 靶场搭建!

在安全测试领域&#xff0c;SQL注入是一种常见的攻击方式&#xff0c;通过应用程序的输入执行恶意SQL查询&#xff0c;从而绕过认证和授权&#xff0c;可以窃取、篡改或破坏数据库中的数据。作为安全测试学习者&#xff0c;如果你要练习SQL注入&#xff0c;在未授权情况下直接去…

(2022级)成都工业学院数据库原理及应用实验一:CASE工具概念数据模型建模

写在前面 1、基于2022级软件工程/计算机科学与技术实验指导书 2、代码仅提供参考 3、如果代码不满足你的要求&#xff0c;请寻求其他的途径 运行环境 window11家庭版 PowerDesigner 16.1 实验要求 某医院一个门诊部排班管理子系统涉及如下信息&#xff1a; 若干科室&a…

传输大咖22|如何利用ProtoBuf实现高效的数据传输?

在今日信息技术日新月异的时代&#xff0c;数据传输的速度与安全性无疑成为了软件开发中的重中之重。无论是微服务架构下的服务间交流&#xff0c;还是客户端与服务器间的数据互动&#xff0c;寻求一种既高效又稳妥的数据传输方式已成为共识。尽管传统的数据格式&#xff0c;如…