`unordered_map` 和 `unordered_set`

news2024/12/25 10:23:51

unordered —— 无序的,从表面上来看,与 map 和 set 不同之处就在于,unordered_map 和 unordered_set 无法保证插入数据是有序的;

尽管如此,由于这两种容器内部封装了“哈希桶”,可以实现快速查找数据 —— 这一优点与 map 和 set 相同。

其实,除了内部结构的不同外,其余与 map 和 set 没什么不同,一样的 insert、find、erase … … 在我们模拟过 map set 的基础上,再学习封装 无序map 和 无序set 实在简单。

因此,本文的重点在于迭代器的运行逻辑 —— operator++() ,和理解模板、仿函数等

最后,补充一个概念:

哈希 是一种思想,将传入的数据映射成一个或多个整型;哈希表 / 哈希桶 则是一种实现哈希思想的结构。

一、改造哈希桶

1.1 初步搭建 HashTable
	// 改造 HashNode
	template<class T> 
    struct HashNode
    {
        HashNode<T>* _next; 
        T _data;
        
        HashNode(const T& data)
            :_next(nullptr)
            ,_data(data)
        {}
    };

	// 改造 HashTable
    // 此处 HashFunc 与《开散列哈希桶》中提供的无异
	// KeyOfT 与 map set 中的无异,都是用于从 T 中取到键值 
	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> 
    class HashTable
    {
    public:
        typedef HashNode<T> Node;
    
    private:
        vector<Node*> _tables;
        size_t _n = 0;
    };
1.2 数据插入 数据查找
  • 数据插入 —— Insert()
	pair<iterator, bool> Insert(const T& data)
    {
        KeyOfT kot;
        iterator ret = Find(kot(data)); // 未实现的 Find,返回值为 iterator
        if (ret != end())// 找到了
        {
            return make_pair(ret, false);
        }
        
        // 扩容 
        // ... 
        
        // 插入
        Hash hs;
        size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值
        Node* newNode = new Node(data);
        newNode->_next = _tables[hashi];
        _tables[hashi] = newNode;
        _n++;
        
        return make_pair(new_iterator, true); // 此处为伪代码!
	}

与普通哈希桶不同的地方在于此: size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值 ,多套了一层仿函数。

PS:

  1. 扩容逻辑与哈希桶完全一致。

  2. return make_pair(new_iterator, true); 为伪代码,用 new_iterator 代表插入节点的迭代器 —— 后面介绍迭代器时,会将这里的坑填上!

  • 数据查找 —— Find()

很多新手不理解为什么在封装 map set 要这样构造 —— 好像传入了两个 Key :

map: RBTree<K, pair<const K, V>, ... > _t;

set: RBTree<K, const K, ...> _t;

我们通常是 t.find(key1); t,erase(key2); 这种方式使用 find() 和 erase() ,无论 t 是 map 还是 set

意思就是,第一个模板参数 K 是为了解决 map 的查找和删除等的问题

	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; // 这里同样是伪代码!
            }
            cur = cur->_next;
        }
        return iterator_nullptr;// 伪代码
    }

iterator_cur 代表 cur 位置的迭代器; iterator_nullptr 代表 空迭代器,这两个迭代器的空白会在后面填补。

二、迭代器封装 __Hash_Iterator

__Hash_Iterator 内部应该传入什么呢?节点的指针吗?哈希桶的指针吗?

我们希望可以通过迭代器遍历整个哈希桶,同时要能取到当前迭代器所在节点的数据,因此,迭代器内部应有节点的指针和哈希桶的指针。

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> 
    struct __Hash_Iterator
    {
        typedef HashNode<T> Node;
        typedef HashTable<K, T, KeyOfT, Hash> HashTable;
        typedef __Hash_Iterator<K, T, KeyOfT, Hash> Self;
        
        Node* _node;
        HashTable* _ht;
        
        __Hash_Iterator(Node* node, HashTable* ht)
        	:_node(node)
            ,_ht(ht)
        {}
    };
2.1 operator++()

operator++() 需要考虑两种情形:

  1. _node->_next 不为空,++ 后,_node = _node->_next
  2. _node->_next 为空,则往后遍历 HashTable,直到找到下一个不为空的位置,或者遍历完整个 HashTable 。
	Self& operator++()
    {
        if (_node->_next)
        {
            _node = _node->_next;
        }
        else 
        {
            Hash hs;
            KeyOfT kot;
            size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
            hashi++; // 从当前位置的后一个位置开始查找
            while (hashi < _ht->_tables.size())
            {
                if (_ht->_tables[hashi])
                {
                    // 找到下一个位置,跳出循环
                    _node = _ht->_tables[hashi]; 
                    break;
                }
                ++hashi;
            }
            if (hashi == _ht->_tables.size()) // 遍历结束,没有下一个节点
            {
                _node = nullptr;
            }
        }
        return *this;
    }
2.2 operator!=() operator*() operator->()
	bool operator!=(const Self& s)
    {
        return _node != s._node;
    }

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

	T* operator->()
    {
        return &_node->_data;
	}
2.3 完善 HashTable

针对第一部分中迭代器遗留问题,在这里将其完善。

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> 
    class HashTable
    {
    public:
        typedef HashNode<T> Node;
        typedef __Hash_Iterator<K, T, KeyOfT, Hash> iterator;
        
        pair<iterator, bool> Insert(const T& data)
        {
            KeyOfT kot;
            Hash hs;
            iterator ret = Find(kot(data)); // 未实现的 Find,返回值为 iterator
            if (ret != end())// 找到了
            {
                return make_pair(ret, false);
            }

            // 扩容 
            if (_n == _tables.size())
			{
				vector<Node*> newTables(_tables.size() * 2, 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)) % newTables.size();
						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}

					_tables[i] = nullptr;
					
				}
				_tables.swap(newTables);
			}

            // 插入
            
            size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值
            Node* newNode = new Node(data);
            newNode->_next = _tables[hashi];
            _tables[hashi] = newNode;
            _n++;

            return make_pair(iterator(newNode, this), true); // 
        }
        
        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); 
                }
                cur = cur->_next;
            }
            return iterator(nullptr, this);
        }
        
        bool Erase(const K& key)
        {
            Hash hs;
            KeyOfT kot;
            size_t hashi = hs(key) % _tables.size();
            Node* cur = _tables[hashi];
            Node* prev = nullptr;

            while (cur)
            {
                if (kot(cur->_data) == key)
                {
                    if (prev == nullptr) // cur == _tables[hashi]
                    {
                        _tables[hashi]->_next = cur->_next;
                    }
                    else
                    {
                        prev->_next = cur->_next;
                    }
                    delete cur;
                    --_n;
                    return true;
                }
                prev = cur;
                cur = cur->_next;
            }
            return false;
        }
    
    private:
        vector<Node*> _tables;
        size_t _n = 0;
    };
2.4 begin() end()
	iterator begin()
    {
        for (size_t i = 0; i < _tables.size(); i++)
        {
            if (_tables[i])
            {
                return iterator(_tables[i], this);
			}
        }
        return iterator(nullptr, this);
    }

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

三、unordered_map unordered_set 封装

unordered_map

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

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

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

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

		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;
		}

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

unordered_set

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

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

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

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

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

	private:
		HashTable<K, const K, SetKeyOfT, Hash> _ht;
	};
注意:

如果直接将以上代码在 VS 中运行,会出现以下几个错误:

该问题的原因是,__Hash_Iterator 之前并未声明 HashTable

	template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值
	class HashTable; // 声明

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
	struct __Hash_Iterator
	{
        // ...
    };

该问题在于,__Hash_Iterator 无法访问 HashTableprivate 成员变量,解决办法是将 __Hash_Iterator 写成 HashTable 的友元类

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
	class HashTable
	{
	public:
		template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值
		friend struct __Hash_Iterator; // 友元

        // ...
    };

四、完整代码

My_Unordered_Map.h
#include "HashTable.h"

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

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

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

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

		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;
		}

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

	void test_map1()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", "排序"));
		dict.insert(make_pair("left", "左边"));
		dict.insert(make_pair("right", "右边"));

		for (auto& kv : dict)
		{
			//kv.first += 'x';
			kv.second += 'y';

			cout << kv.first << ":" << kv.second << endl;
		}
	}

	void test_map2()
	{
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
	"苹果", "香蕉", "苹果", "西瓜", "香蕉", "草莓" };
		unordered_map<string, int> countMap;
		for (auto& e : arr)
		{
			/*if (e == "ݮ")
			{
				int i = 0;
			}*/

			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
}
My_Unordered_Set.h
#include "HashTable.h"


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

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

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

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

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

	private:
		HashTable<K, const K, SetKeyOfT, Hash> _ht;
	};


	void test_set1()
	{
		unordered_set<int> us;
		us.insert(3);
		us.insert(1);
		us.insert(5);
		us.insert(15);
		us.insert(45);
		us.insert(7);

		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			//*it += 100;
			cout << *it << " ";
			++it;
		}
		cout << endl;

		 for (auto e : us)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}
HashTable.h
#include <vector>

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

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

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

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

template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值
class HashTable; // 声明

template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
struct __Hash_Iterator
{
    typedef HashNode<T> Node;
    typedef HashTable<K, T, KeyOfT, Hash> HashTable;
    typedef __Hash_Iterator<K, T, KeyOfT, Hash> Self;

    Node* _node;
    HashTable* _ht;

    __Hash_Iterator(Node* node, HashTable* ht)
        :_node(node)
        , _ht(ht)
    {}

    Self& operator++()
    {
        if (_node->_next)
        {
            _node = _node->_next;
        }
        else
        {
            Hash hs;
            KeyOfT kot;
            size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
            hashi++; // 从当前位置的后一个位置开始查找
            while (hashi < _ht->_tables.size())
            {
                if (_ht->_tables[hashi])
                {
                    // 找到下一个位置,跳出循环
                    _node = _ht->_tables[hashi];
                    break;
                }
                ++hashi;
            }
            if (hashi == _ht->_tables.size()) // 遍历结束,没有下一个节点
            {
                _node = nullptr;
            }
        }
        return *this;
    }

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

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

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

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

    template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值
    friend struct __Hash_Iterator; // 友元



    HashTable()
    {
        _tables.resize(10);
    }

    pair<iterator, bool> Insert(const T& data)
    {
        KeyOfT kot;
        Hash hs;
        iterator ret = Find(kot(data)); // 未实现的 Find,返回值为 iterator
        if (ret != end())// 找到了
        {
            return make_pair(ret, false);
        }

        // 扩容 
        if (_n == _tables.size())
        {
            vector<Node*> newTables(_tables.size() * 2, 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)) % newTables.size();
                    cur->_next = newTables[hashi];
                    newTables[hashi] = cur;

                    cur = next;
                }

                _tables[i] = nullptr;

            }
            _tables.swap(newTables);
        }

        // 插入
        
        size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值
        Node* newNode = new Node(data);
        newNode->_next = _tables[hashi];
        _tables[hashi] = newNode;
        _n++;

        return make_pair(iterator(newNode, this), true); // 
    }

    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);
            }
            cur = cur->_next;
        }
        return iterator(nullptr, this);
    }

    bool Erase(const K& key)
    {
        Hash hs;
        KeyOfT kot;
        size_t hashi = hs(key) % _tables.size();
        Node* cur = _tables[hashi];
        Node* prev = nullptr;

        while (cur)
        {
            if (kot(cur->_data) == key)
            {
                if (prev == nullptr) // cur == _tables[hashi]
                {
                    _tables[hashi]->_next = cur->_next;
                }
                else
                {
                    prev->_next = cur->_next;
                }
                delete cur;
                --_n;
                return true;
            }
            prev = cur;
            cur = cur->_next;
        }
        return false;
    }

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

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

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

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

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

相关文章

白话机器学习5:卷积神经网络(CNN)原理

1.神经元 激活函数f(z)的种类&#xff1a; 2.卷积方法种类 https://mp.weixin.qq.com/s/FXzTbMG64jr93Re31Db2EA 标准卷积&#xff08;Standard Convolution&#xff09;: 特点&#xff1a;每个卷积核在输入数据的整个深度上滑动&#xff0c;计算输出特征图的一个元素。应用场…

STM32有什么高速接口吗?

STM32 有一些高速接口&#xff0c;比如 USART、SPI、I2C 等&#xff0c;这些接口可以用于与外部设备进行高速数据传输。我这里有一套stm32入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习stm32&#xff0c;不妨点个关注&#xff0c;给个评论…

JavaSE——集合框架一(2/7)-Collection集合的遍历方式-迭代器、增强for循环、Lambda、案例

目录 Collection的遍历方式 迭代器 增强for循环&#xff08;foreach&#xff09; Lambda表达式遍历集合 案例 需求与分析 代码部分 运行结果 Collection的遍历方式 迭代器 选代器是用来遍历集合的专用方式&#xff08;数组没有选代器&#xff09;&#xff0c;在Java中…

【爬虫之scrapy框架——尚硅谷(学习笔记one)--基本步骤和原理+爬取当当网(基本步骤)】

爬虫之scrapy框架——基本原理和步骤爬取当当网&#xff08;基本步骤&#xff09; 下载scrapy框架创建项目&#xff08;项目文件夹不能使用数字开头&#xff0c;不能包含汉字&#xff09;创建爬虫文件&#xff08;1&#xff09;第一步&#xff1a;先进入到spiders文件中&#x…

LabVIEW开发RS422通信

LabVIEW开发RS422通信 项目围绕LabVIEW软件开发的程序在RS422通信技术检测方面的应用进行展开&#xff0c;通过软件编程将上位计算机虚拟化为检测设备&#xff0c;控制其通信端口与被测产品进行RS422通信&#xff0c;以此检验产品的性能优劣。该虚拟检测仪器在实际测试中表现出…

C++17新特性 结构化绑定

一、Python中的相似功能 熟悉python的应该对下面的代码很熟悉 def return_multiple_values():return 11, 7x, y return_multiple_values()函数返回一个元组&#xff0c;元组自动分配给了x和y。 二、C11中的元组 c11中就存在类似python元组的概念了&#xff1a; std::tupl…

【简单介绍下Milvus】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

CV每日论文--2024.5.10

1、Attention-Driven Training-Free Efficiency Enhancement of Diffusion Models 中文标题&#xff1a;扩散模型的注意力驱动的训练免费效率增强 简介&#xff1a;扩散模型在生成高质量和多样化图像方面取得了出色的表现,但其卓越性能是以昂贵的架构设计为代价的,特别是广泛使…

激光SLAM总结——Fast LIO / Fast LIO2 / Faster LIO

激光SLAM总结——Fast LIO / Fast LIO2 / Faster LIO 在之前的工作中有接触过LOAM&#xff0c;最近在工作中又接触到Faster LIO相关的工作&#xff0c;于是想着对Fast LIO / Fast LIO2 / Faster LIO这一系列工作进行一个简单的总结&#xff0c;以加深自己对激光SLAM算法的理解…

网络网络层之(5)IPv6协议

网络网络层之(5)IPv6协议 Author: Once Day Date: 2024年5月12日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day…

未授权访问:Docker未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、通过crontab反弹宿主机shell 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好…

【机器学习】机器学习与人工智能融合新篇章:自适应智能代理在多元化复杂环境中的创新应用与演进趋势

&#x1f512;文章目录&#xff1a; &#x1f4a5;1.引言 &#x1f68b;1.1 机器学习与人工智能的发展背景 &#x1f68c;1.2 自适应智能代理的概念与重要性 &#x1f690;1.3 研究目的与意义 ☔2.自适应智能代理的关键技术 &#x1f6e3;️2.1 环境感知与信息处理技术 …

whisper报错:hp, ht, pid, tid = _winapi.CreateProcess [WinError 2] 系统找不到指定的文件。

in _execute_child hp&#xff0c; ht&#xff0c; pid&#xff0c; tid _winapi.CreateProcess&#xff08;executable&#xff0c; args&#xff0c; FileNotFoundError&#xff1a; [WinError 2] 系统找不到指定的文件。 原因&#xff1a; 没装ffmpeg 或者 ffmpeg没添加到…

Nios实验使用串口输出“Hello Nios-II”字符到笔记本电脑

目录 实验过程 创建工程 修改程序 编译工程 运行项目 效果实现 总结 参考 实验过程 硬件设计见博主上篇博客 软件部分设计 下面使用 Nios II Software Build Tools for Eclipse 来完成当前项目的软件开发。 启动 Nios II SBT 按照下图所示点击 Nios II Software Build…

Backend - 数据分析 Pandas

目录 一、作用 二、基础环境 &#xff08;一&#xff09;执行虚拟环境的终端命令 &#xff08;二&#xff09;代码中导包 三、应用&#xff1a;一维数组 &#xff08;一&#xff09;Series对象 1. 含义 2. 常用属性和方法 &#xff08;1&#xff09;属性 &#xff08;…

springboot学习整理

视频&#xff1a;基础篇-01_springboot概述_哔哩哔哩_bilibili 介绍 spring boot 是spring提供的一个子项目&#xff0c;用于快速构建spring应用程序 spring构建&#xff1a; 1 导入依赖繁琐 &#xff1b; 2 项目配置繁琐 spring Framework: 核心 spring Boot :快速构建spring…

【LangChain学习之旅】—(21)聊天客服机器人的开发(上)

【LangChain学习之旅】—(21)聊天客服机器人的开发(上) “聊天机器人”说明项目的技术实现细节技术实现步骤简述第二步:增加记忆机制第三步:增加检索机制总结“聊天机器人”说明 聊天机器人(Chatbot)是 LLM 和 LangChain 的核心用例之一,很多人学习大语言模型,学习 …

Jmeter(三十九) - 从入门到精通进阶篇 - Jmeter配置文件的刨根问底 - 上篇(详解教程)

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 为什么宏哥要对Jmeter的配置文件进行一下讲解了&#xff0c;因为有的童鞋或者小伙伴在测试中遇到一些需要修改配置文件的问题不是很清楚也不是很懂&#xff0c;就算修改了也是…

今天开发了一款软件,我竟然只用敲了一个字母(文末揭晓)

软件课题&#xff1a;Python实现打印100内数学试题软件及开发过程 一、需求管理&#xff1a; 1.实现语言&#xff1a;Python 2.打印纸张&#xff1a;A4 3.铺满整张纸 4.打包成exe 先看效果&#xff1a; 1. 2.电脑打印预览 3.打印到A4纸效果&#xff08;晚上拍的&#x…

R语言手把手教你进行支持向量机分析

1995年VAPINK 等人在统计学习理论的基础上提出了一种模式识别的新方法—支持向量机 。它根据有限的样本信息在模型的复杂性和学习能力之间寻求一种最佳折衷。 以期获得最好的泛化能力.支持向量机的理论基础决定了它最终求得的是全局最优值而不是局部极小值,从而也保证了它对未知…