【C++】哈希表封装 unordered_map 和 unordered_set 的实现过程

news2024/11/8 10:27:27

在这里插入图片描述

C++语法相关知识点可以通过点击以下链接进行学习一起加油!
命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇
类和对象-下篇日期类C/C++内存管理模板初阶String使用
String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriority Queue与仿函数
模板进阶-模板特化面向对象三大特性-继承机制面向对象三大特性-多态机制STL 树形结构容器二叉搜索树
AVL树红黑树红黑树封装map/set哈希-开篇闭散列-模拟实现哈希
哈希桶-模拟实现哈希

本篇将讲述如何通过哈希表封装 unordered_mapunordered_set 容器。在开始本章内容之前,建议先阅读红黑树封装 mapset一文,以便更好地理解编译器如何区分 unordered_mapunordered_set 并调用底层哈希表。

请添加图片描述
Alt
🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 一、节点类型
  • 二、区分unordered_map/set容器
  • 三、相关文件修改后
    • 3.1 HashTable.h
    • 3.2 unoderded_set.h
    • 3.3 unoderded_map.h
  • 四、实现迭代器
    • 4.1 迭代器常见功能
    • 4.2 operator++实现
    • 4.3 编译器扫描顺序问题
      • 4.3.1 【第一个办法】:声明及其友元
      • 4.3.2 【第二办法】:内部类
    • 4.4 operator++内部逻辑
    • 4.5 获得begin和end迭代器位置
    • 4.6 迭代器调用流程图
  • 五、unoderded_map/set相关接口实现
    • 5.1 unoderded_map相关接口
    • 5.2 unoderded_set相关接口
    • 5.3 HashTable相关接口调整
  • HashTable.h

这里哈希表将采用哈希桶的方式解决哈希冲突且实现哈希表结构,那么我们需要对哈希表节点类型进行处理,同时设计编译器去识别unordered_map/set的逻辑。

一、节点类型

如果我们想使用哈希表封装unordered_map/set容器,就需要考虑泛型编程,将节点类型统一成T类型

template<class T>
    struct HashNode
    {
        T _data;
        HashNode<T>* _next;

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

二、区分unordered_map/set容器

编译器是无非确定你需要啥容器**,这里我们可以通过外部输入数据,使得编译器明确你需要啥容器,该容器都有一套属于返回K类型的仿函数**,只要unordered_set是否会多余,这里可以参考红黑树封装map/set文章,这里是为了跟map构成模板,陪太子读书。

在这里插入图片描述

既然我们希望可以通过手动输入数据,得到预计效果,那么关于HashFunc函数也是期望从外部调用,这里可以写到外面来。(图片写错了,这里看下面的)

template<class K,class Hash = HashFunc > and template<class K,class T,class Hash = HashFunc>

三、相关文件修改后

3.1 HashTable.h

#pragma once
#include <iostream>
using namespace std;
#include <string>
#include <vector>

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

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

namespace hash_bucket
{


    template<class T>
        struct HashNode
        {
            T _data;
            HashNode<T>* _next;

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

    template<class K, class T, class KeyOfT, class Hash>
        class HashTable
        {
            public:
            typedef HashNode<T> Node;
            HashTable()
            {
                _tables.resize(10, nullptr);

            }

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

                }
            }

            bool Insert(const T& data)
            {
                Hash hs;
                KeyOfT kot;

                if (Find(kot(data))
                    {
                        return false;
                    }


                    if (_n == _tables.size())
                    {
                        vector<Node*> NewTable(n * 10, nullptr);

                        for (size_t i = 0; i < _tables.size(); i++)
                        {
                            Node* cur = _tables[i];
                            while (cur)
                            {
                                Node* next = cur->_next;
                                // 头插新表的位置
                                size_t hashi = hs(kot(cur->_data)) % NewTable.size();
                                cur->_next = NewTable[hashi];
                                NewTable[hashi] = cur;

                                cur = cur->_next;
                            }
                            _tables[i] = nullptr;
                        }
                        _tables.swap(NewTable);
                    }


                    size_t hashi = hs(kot(data)) % _tables.size();
                    //进行头插操作
                    Node* newnode = new Node(data);
                    _tables[hashi]->_next = newnode;
                    _tables[hashi] = newnode;
                    ++_n;
                    return true;
                    }

                    Node* Find(const K& key)
                    {
                        Hash hs;
                        KeyOfT kot;
                        size_t hashi = hs(key) % _tables.size();
                        Node* cur = _tables[hashi];
                        while (cur)
                        {
                            if (kot(cur->_data) == key)
                            {
                                return &kot(cur->_data);
                            }
                            else
                            {
                                cur = cur->_next;
                            }
                        }
                        return nullptr;
                    }

                    bool Erase(const K& key)
                    {
                        Hash hs;
                        size_t 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;

                                return true;
                            }
                            else
                            {
                                prev = cur;
                                cur = cur->_next;
                            }
                        }
                        return false;
                    }
                    private:
                    vector<Node*> _tables;
                    size_t _n = 0;
                    };
                    }

这里关于HashFunc()需要放在哈希命名空间外部,因为在其他头文件需要使用到该HashFunc()

3.2 unoderded_set.h

namespace unoderded_set
{
    template<class K,class Hash = HashFunc<K>>
        class unoderded_set
        {
            struct SetKeyOfT
            {
                const K& operator()(const K& key)
                {
                    return key;
                }
            };
            private:
            hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
        };
}

3.3 unoderded_map.h

namespace unoderded_map
{
    template<class K,class V,class Hash = HashFunc<K>>
        class unoderded_map
        {
            struct SetKeyOfT
            {
                const K& operator()(const pair<K,V>& key)
                {
                    return key.first;
                }
            };
            public:

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

四、实现迭代器

实现哈希表中迭代器跟之前其他容器实现迭代器需要考虑的差不多,主要是对于operator++和operator–需要考虑不同容器的结构进行行为重定义。同时需要设计为模板template<class T,class Ptr, class Ref>去匹配const类型和非const类型调用同一个迭代器。

typedef __HTIterator<T*, T&> iterator;
typedef __HTIterator<const T*, const T&> const_iterator;

这里得到当前_node的位置,是浅拷贝。这不希望改变实参,而是希望通过该实参位置进行操作,得到预期数据,在下一次进行操作时还是之前位置。

4.1 迭代器常见功能

template<class T,class Ptr, class Ref>
    struct _HTIterator
    {
        //得到一个节点
        typedef HashNode<T>	Node;
        typedef _HTIterator Self;

        Node* _node;

        //这里希望是浅拷贝
        _HTIterator(const Node* node)
            :_node(node)
            {}

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

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

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

4.2 operator++实现

大体思路】:在第一个不为空位置,向下遍历++直到_node-> _next ==nullptr说明该位置迭代器达到悬挂着哈希桶最后一个不为空的桶,那么需要通过当前节点数值,进行除留余数法得到当前表位置下标,以该下标为基准重新在表中找到下一个不为空的位置,直到i == tables.size()说明遍历完成。

4.3 编译器扫描顺序问题

目前关于迭代器实现某方面功能需要借助HashTable类的类型及其成员,但是问题在于HashTable需要将迭代器设为"武器",将迭代器类功能具体实现放在HashTabl类上方,编译器扫描是上到下扫描,这就导致了这两个类总有一方无法得到另外一份的东西。在这里插入图片描述

4.3.1 【第一个办法】:声明及其友元

我们可以在迭代器类中声明HashTable类作为类型定义一个哈希表指针,同时在HashTable类型设置迭代器友元,使之迭代器可以通过定义的哈希表指针访问到哈希表。

template<class K,class T,class Ptr, class Ref,class KeyOfT, class Hash>
    struct _HTIterator
    {	//前置声明
        template<class K, class T, class KeyOfT, class Hash>
            class HashTable;	
    }


template<class K, class T, class KeyOfT, class Hash>
    class HashTable
    {
        public:
        template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
            friend struct _HTIterator;
    }

前置声明

前置声明仅仅告诉编译器有一个 HashTable 类,但由于没有给出该类的完整定义和实现,编译器并不知道如何去使用它。也就是说,你不能在代码中使用 HashTable 类的功能,比如创建对象、调用其成员函数、访问数据成员等,除非你提供了类的完整定义。

4.3.2 【第二办法】:内部类

template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		typedef HashNode<T> Node;
	public:
		// 友元声明
		/*template<class K, class T, class KeyOfT, class Hash>
		friend struct __HTIterator;*/

		// 内部类
		template<class Ptr, class Ref>
		struct __HTIterator
		{
			typedef HashNode<T> Node;
			typedef __HTIterator Self;
        }
    }

这里就采用第一种方法(选择哪个方法都是差不多的)

4.4 operator++内部逻辑

Self& operator++()
{
    KeyOfT kot;
    Hash hs;

    if( _node->_next )
    {
        _node = _node->_next;
    }
    else
    {
        //访问该成员
        size_t i = hs(kot(_node->_data)) % _pht->_tables.size();
        i++;
        for (; i < _pht->_tables.size(); i++)
        {
            if (_pht->_tables[i])
            {
                break;
            }
        }

        if (i == _pht->tables.size())
        {
            _node = nullptr;
        }
        else
        {
            _node = _pht->_tables[i];
        }
    }
}

由于在库中unoderded_map和unoderded_set只有单向迭代器,就没有operator–。如果有兴趣可以将单链表设计为双向链表,然后根据operator++逻辑,设计出一个operator–接口

4.5 获得begin和end迭代器位置

在这里插入图片描述

iterator begin()
{
    for (size_t i = 0; i < _tables.size(); i++)
    {
        Node* cur = _tables[i];
        if (cur)
        {
            return iterator(cur, this);
        }

    }

}

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

const_iterator begin() const
{

    for (size_t i = 0; i < _tables.size(); i++)
    {
        Node* cur = _tables[i];
        if (cur)
        {
            return iterator(cur, this);
        }

    }
}

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

这里就是通过哈希表begin和end接口,构造相关需求的迭代器。这里很好体现了每个不同对象的迭代器指向表是自己对象的哈希表。

4.6 迭代器调用流程图

在这里插入图片描述

unoderded_map及其unoderded_set迭代器配置,由于unoderded_map支持修改元素,那么没有必要写const系列。

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

五、unoderded_map/set相关接口实现

5.1 unoderded_map相关接口

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

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


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

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

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


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

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

}

unoderded_map增添接口】:V& operator[](const K& key)pair<iterator, bool> insert(const pair<K, V>& kv)

5.2 unoderded_set相关接口

#pragma once
#include "HashTable.h"

namespace unoderded_set
{
    template<class K, class Hash = HashFunc<K>>
        class unoderded_set
        {
            struct SetKeyOfT
            {
                const K& operator()(const K& key)
                {
                    return key;
                }
            };
            typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT,Hash>::iterator iterator;
            typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::const_iterator  const_iterator;


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

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

            const_iterator begin() const
            {
                return _ht.begin();
            }

            const_iterator end() const
            {
                return _ht.end();
            }
            pair<iterator, bool> insert(const K& key)
            {
                return _ht.Insert(key);
            }

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

            bool erase(const K& key)
            {
                return _ht.Erase(key);
            }
            private:
            hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
        };
}

unoderded_set增添接口 pair<iterator, bool> insert(const K& key) iterator find(const K& key)及其bool erase(const K& key)

5.3 HashTable相关接口调整

iterator Find(const K& key)
{
    Hash hs;
    KeyOfT kot;
    size_t hashi = hs(key) % _tables.size();
    Node* cur = _tables[hashi];
    while (cur)
    {
        if (kot(cur->_data) == key)
        {
            return iterator(cur, this);
        }
        else
        {
            cur = cur->_next;
        }
    }
    return end();
}

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*> NewTable(_n * 10, nullptr);

        for (size_t i = 0; i < _tables.size(); i++)
        {
            Node* cur = _tables[i];
            while (cur)
            {
                Node* next = cur->_next;
                // 头插新表的位置
                size_t hashi = hs(kot(cur->_data)) % NewTable.size();
                cur->_next = NewTable[hashi];
                NewTable[hashi] = cur;

                cur = cur->_next;
            }
            _tables[i] = nullptr;
        }
        _tables.swap(NewTable);
    }


    size_t hashi = hs(kot(data)) % _tables.size();
    //进行头插操作
    Node* newnode = new Node(data);
    _tables[hashi]->_next = newnode;
    _tables[hashi] = newnode;
    ++_n;
    return make_pair(iterator(newnode, this), true);
}

unodered_map当中operator[]可以看作insert和find融合版本,不需要单独设计Find(),但是内部还是有调用Find()逻辑

HashTable.h

#pragma once
#include <iostream>
using namespace std;
#include <string>
#include <vector>

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

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




namespace hash_bucket
{

    template<class T>
        struct HashNode
        {
            T _data;
            HashNode<T>* _next;

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

    template<class K,class T,class Ptr, class Ref,class KeyOfT, class Hash>
        struct _HTIterator
        {
            //前置声明
            template<class K, class T, class KeyOfT, class Hash>
                class HashTable;

            //得到一个节点
            typedef HashNode<T>	Node;
            typedef _HTIterator Self;

            Node* _node;
            const HashTable* _pht;

            //这里希望是浅拷贝
            //这里对象-->指向一个哈希表-->cur及其this。
            _HTIterator(const Node* node,const HashTable* pht)
                :_node(node)
                    ,_pht(pht)
                {}

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

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


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

            Self& operator++()
            {
                KeyOfT kot;
                Hash hs;

                if( _node->_next )
                {
                    _node = _node->_next;
                }
                else
                {
                    //访问该成员
                    size_t i = hs(kot(_node->_data)) % _pht->_tables.size();
                    i++;
                    for (; i < _pht->_tables.size(); i++)
                    {
                        if (_pht->_tables[i])
                        {
                            break;
                        }
                    }

                    if (i == _pht->tables.size())
                    {
                        _node = nullptr;
                    }
                    else
                    {
                        _node = _pht->_tables[i];
                    }
                }
            }
        };

    template<class K, class T, class KeyOfT, class Hash>
        class HashTable
        {
            public:

            template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
                friend struct _HTIterator;


            typedef _HTIterator< K, T, T*,  T&, KeyOfT, Hash> iterator;
            typedef _HTIterator<K,  T,const T*, const T&, KeyOfT, Hash> const_iterator;

            typedef HashNode<T> Node;
            HashTable()
            {
                _tables.resize(10, nullptr);
            }

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

                }
            }

            iterator Find(const K& key)
            {
                Hash hs;
                KeyOfT kot;
                size_t hashi = hs(key) % _tables.size();
                Node* cur = _tables[hashi];
                while (cur)
                {
                    if (kot(cur->_data) == key)
                    {
                        return iterator(cur, this);
                    }
                    else
                    {
                        cur = cur->_next;
                    }
                }
                return end();
            }

            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*> NewTable(_n * 10, nullptr);

                    for (size_t i = 0; i < _tables.size(); i++)
                    {
                        Node* cur = _tables[i];
                        while (cur)
                        {
                            Node* next = cur->_next;
                            // 头插新表的位置
                            size_t hashi = hs(kot(cur->_data)) % NewTable.size();
                            cur->_next = NewTable[hashi];
                            NewTable[hashi] = cur;

                            cur = cur->_next;
                        }
                        _tables[i] = nullptr;
                    }
                    _tables.swap(NewTable);
                }


                size_t hashi = hs(kot(data)) % _tables.size();
                //进行头插操作
                Node* newnode = new Node(data);
                _tables[hashi]->_next = newnode;
                _tables[hashi] = newnode;
                ++_n;

                return make_pair(iterator(newnode, this), true);
            }

            iterator begin()
            {
                for (size_t i = 0; i < _tables.size(); i++)
                {
                    Node* cur = _tables[i];
                    if (cur)
                    {
                        return iterator(cur, this);
                    }

                }

            }

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

            const_iterator begin() const
            {

                for (size_t i = 0; i < _tables.size(); i++)
                {
                    Node* cur = _tables[i];
                    if (cur)
                    {
                        return iterator(cur, this);
                    }

                }
            }

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

            bool Erase(const K& key)
            {
                Hash hs;
                size_t 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;

                        return true;
                    }
                    else
                    {
                        prev = cur;
                        cur = cur->_next;
                    }
                }
                return false;
            }
            private:
            vector<Node*> _tables;
            size_t _n = 0;
        };

};

在这里插入图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!

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

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

相关文章

攀拓(PAT)- 程序设计(乙级)2024年春季考试

题目来源&#xff1a;https://pintia.cn/market/item/1767454903977603072 B-1 题目要求 2024 这个数字&#xff0c;可以由 n n n个互不相同的正偶数和 m m m个互不相同的正奇数组合出来吗&#xff1f;本题就请你回答这个问题。 输入格式&#xff1a; 输入在一行中给出一个…

STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55

STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55 前面我们讲述了几种BootLoader中的命令&#xff0c;包括获取软件版本号、获取帮助、获取芯片ID、读取Flash保护Level。 下面我们来介绍一下BootLoader中最重要的功能之一—跳转&#xff01;就像BootLoader词汇中的Boot…

VTK知识学习(2)-环境搭建

1、c方案 1.1下载源码编译 官网获取源码。 利用Cmake进行项目构建。 里面要根据实际使用的情况配置相关的模块哟&#xff0c;这个得你自行研究下了。 CMAKEINSTALLPREFIX--这个选项的值表示VTK的安装路径&#xff0c;默认的路径是C:/Program Files/VTK。该选项的值可不作更…

Chrome(谷歌浏览器中文版)下载安装(Windows 11)

目录 Chrome_10_30工具下载安装 Chrome_10_30 工具 系统&#xff1a;Windows 11 下载 官网&#xff1a;https://chrome.google-zh.com/&#xff0c;点击立即下载 下载完成&#xff08;已经下过一遍所以点了取消&#xff09; 安装 解压&#xff0c;打开安装包 点击下一步…

如何在算家云搭建Aatrox-Bert-VITS2(音频生成)

一、模型介绍 ‌ Aatrox - Bert -VITS2 模型是一种基于深度学习的语音合成系统&#xff0c;结合了 BERT 的预训练能力和 VITS2 的微调技术&#xff0c;旨在实现高质量的个性化语音合成。 二、模型搭建流程 1. 创建容器实例 进入算家云的“应用社区”&#xff0c;点击搜索找到…

232转485模块测试

概述 常用的PLC一般会有两个左右的232口&#xff0c;以及两个左右的485口&#xff0c;CAN口等&#xff0c;但是PLC一般控制的设备可能会有很多&#xff0c;会超出通讯口的数量&#xff0c;此时我们一般会采用一个口接多个设备&#xff0c;这种情况下要注意干扰等因素&#xff0…

静态数组类型无法用“=“给整个静态数组赋值

基础知识&#xff1a; 什么是静态数组类型&#xff1f; 在 C 中&#xff0c;静态数组是一种在编译时大小固定的数组。这意味着一旦声明&#xff0c;静态数组的大小就不能改变。 ------ 你可以声明一个静态数组并选择性地初始化它&#xff1a; int arr[10]; // 声明一个包…

使用 Python 调用云 API 实现批量共享自定义镜像

本文介绍如何通过 Python SDK 调用 API 接口&#xff0c;通过子用户批量共享云服务器自定义镜像。若您具备类似需求&#xff0c;或想了解如何使用 SDK&#xff0c;可参考本文进行操作。 前提条件 已创建子用户&#xff0c;并已具备云服务器及云 API 所有权限。 创建子用户请…

【旷视科技-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件

在现代地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;有许多大家耳熟能详的GIS软件。它们各自具有独特的优势&#xff0c;适用于不同的行业需求和使用场景。在众多企业和开发者面前&#xff0c;如何选择合适的 GIS 软件成为了一个值得深入思考的问题。今天&#xff…

【Spring】Spring Web MVC基础入门~(含大量例子)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;什么是Spring Web MVC 1&#xff1a;Servlet 2&#xff1a;总结 二&#xff1a;MVC …

数组类算法【leetcode】

704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 二分查找 用于有序数组中&#xff0c;没有重复的数组。…

24/11/7 算法笔记 PCA主成分分析

假如我们的数据集是n维的&#xff0c;共有m个数据(x,x,...,x)。我们希望将这m个数据的维度从n维降到k维&#xff0c;希望这m个k维的数据集尽可能的代表原始数据集。我们知道数据从n维降到k维肯定会有损失&#xff0c;但是我们希望损失尽可能的小。那么如何让这k维的数据尽可能表…

wxWidgets布局管理及XRC界面使用解惑

wxWidgets布局&#xff0c;通常就是wxBoxSizer&#xff0c;当然还有别的Sizer&#xff0c;就像Qt中的Layout一样&#xff0c;各种布局管理类。只是如今的wxWidgets尚未发展起来&#xff0c;一般其他的你用的比较少&#xff0c;因为这个最简单&#xff0c;最容易布局&#xff0c…

【开源免费】基于SpringBoot+Vue.JS医院管理系统(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 062 &#xff0c;文末自助获取源码 \color{red}{T062&#xff0c;文末自助获取源码} T062&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

【循环引用及格式化输出】

垃圾回收机制 当一个值在内存中直接引用跟间接引用的量为0时&#xff0c;&#xff08;即这个值没有任何入口可以找到它&#xff09;那么这个值就会被清空回收♻️&#xff0c;释放内存空间&#xff1b; 列表在内存中的存储方式 1&#xff09;引用计数的两种方式 x "ea…

31.7K+ Star!AgentGPT:一个在浏览器中运行的Agent

AgentGPT 简介 AgentGPT[1] 是一个可以让你在浏览器中组装、配置和部署自主AI代理的项目。你可以为你的自定义AI命名,并让它去实现任何你想象中目标。它将尝试通过思考要执行的任务、执行它们并从结果中学习来达成目标。 项目特点 主要特点 自主AI代理:用户可以自定义AI并赋…

【大模型】通过Crew AI 公司的崛起之路学习 AI Agents 的用法

AI 技术的迅猛发展正以前所未有的速度重塑商业格局&#xff0c;而 AI Agents&#xff0c;作为新一代的智能自动化工具&#xff0c;正逐步成为创新型公司的核心力量。在本文中&#xff0c;我们将探讨如何利用 AI Agents 构建一家 AI 驱动的公司&#xff0c;并详细了解 Crew AI 创…

【Uniapp】Uniapp Android原生插件开发指北

前言 在uniapp开发中当HBuilderX中提供的能力无法满足App功能需求&#xff0c;需要通过使用Andorid/iOS原生开发实现时&#xff0c;或者是第三方公司提供的是Android的库&#xff0c;这时候可使用App离线SDK开发原生插件来扩展原生能力。 插件类型有两种&#xff0c;Module模…

网页版五子棋——用户模块(服务器开发)

前一篇文章&#xff1a;网页版五子棋—— WebSocket 协议-CSDN博客 目录 前言 一、编写数据库代码 1.数据库设计 2.配置 MyBatis 3.创建实体类 4.创建 UserMapper 二、前后端交互接口 1.登录接口 2.注册接口 3.获取用户信息 三、服务器开发 1.代码编写 2.测试后端…