Hash表(C++)

news2024/9/20 16:47:39

        本篇将会开始介绍有关于 unordered_map 和 unordered_set 的底层原理,其中底层实现其实就是我们的 Hash 表,本篇将会讲解两种 Hash 表,其中一种为开放定址法,另一种为 hash 桶,在unordered_map 和 unordered_set 的底层实现中主要是使用的是 hash 桶。

        本篇分别介绍了 hash 表的两种实现方法,接着讲解 hash 冲突、扩容与 hash 函数的设计原则。最后使用代码实现 hash 表的数据结构。

目录

hash概念

1. 开放定址法

2. hash 桶

hash冲突与hash函数

1. hash冲突

2. hash 函数

3. hash 表的扩容

代码实现

1. 开放定址法

2. hash 桶

hash概念

        我们在顺序结构或者平衡树中,想要查找一个数必须经过关键码的多次比较,顺序查找的时间复杂度为O(n),平衡树的查找时间复杂度为O(logn)。但是是否存在一种搜索方法,我们可以不经过任何比较,直接从表中找到我们想要的元素

        我们构建的 hash 表就可以达到这样的效果:通过某种函数(hashfunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么我们在查找时通过该函数可以很快的找到该元素

        构造出相应的结构:

        插入元素:根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此方法进行存放。

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

        本篇一共构造两种结构:开放定址法、hash 桶。如下:

1. 开放定址法

        关于开放定址法,其中的主要思想为:构建一个存放数据的 hash 表,然后通过 hashfunc 计算出元素对应的关键码,然后将元素对应的放置在相应的 hash 表中的位置,如下:

        假设我们存放元素集合:{1,7,6,4,5,9}。

        当我们想要进行搜索元素的时候,我们就可以直接使用对应的 hash 函数搜索对应的元素。

2. hash 桶

        关于 hash 桶,其主要思想为:我们首先先建立一个 hash 表,在 hash 表中的每一个位置,我们都直接将使用哈希函数计算出元素对应的关键码存放到对应位置,如下:

        假设我们存放的元素集合为:{1,7,6,4,5,9,44}。

        当其中同一个位置存在多个元素的时候,我们只需要将多的元素链接到最后元素的后面即可。

hash冲突与hash函数

1. hash冲突

        在实现 hash 表,计算元素对应的关键码的时候,难免会遇到关键码相同的情况,也就是同一个位置将会存放两个元素,也就是 hash 冲突。对于开放定址法而言就会存在 hash 冲突,而 hash 桶则不用担心会出现 hash 冲突

        关于 hash 冲突的解决方法:

        线性探测:从发生冲突的位置开始,依次向后探测、直到寻找到下一个空位置为止。如下:

        插入:首先通过哈希函数获取插入元素在哈希表中的位置,若该位置没有元素则直接插入新的元素,若该位置中有元素发生哈希冲突,我们则使用线性探测找到下一个空位置,然后在插入元素,如下:

        删除:关于使用线性探测法删除元素,我们不能直接删除元素,直接删除元素将会影响其他元素的搜索。比如删除元素4,如果直接将其删掉,44的查找将会受到影响,所以线性探测采用标记的伪删除法来删除一个元素

enum State {EMPTY, EXIST, DELETE};
// EMPTY此位置为空
// EXIST此位置存在元素
// DELETE此位置元素已经删除

        二次探测:线性探测的缺陷是产生冲突的数据会堆积在一块,这与查找下一个空位置有关系,因为找空位置的方法就是挨着往后逐个去找,因此二次探索为了避免该问题,找到下一个空位置的方法为:Hi = (H0 + i ^ 2) % m 或者 Hi = (H0 - i ^ 2) % m,其中 H0 为通过散列函数计算出的关键码对应的位置,m 是表的大小。

2. hash 函数

        关于引起 hash 冲突的一个原因为:hash 函数的设计不合理。

        关于 hash 函数的设计原则有

        1. hash 函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有 m 个地址的时候,其值域必须在 0 ~ m - 1之间。

        2. hash 函数计算出来的地址能均匀分布在整个空间中。

        3. hash 函数的设计应该比较简单。

        常见的 hash 函数:直接定址法除留余数法、平方取中法、折叠法、随机数法……

        直接定址法:取关键字的某个线性函数为散列地址:hash(key) = A * key + B。直接定址法的优点为:简单且均匀;缺点为:需要事先知道关键字的分布情况。适合查找比较小且连续的情况。

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

        对于其他的 hash 函数不是很常用,便不在介绍。

3. hash 表的扩容

        虽然在使用 hash 表存储元素的时候,我们可以将元素不断的映射到 hash 表中来,但是我们的 hash 表也会存在存满的情况(使用开放定址法下的 hash 表),那么也就会存在一个 hash 表扩容的问题。

        hash 表的载荷因子定义为:α = 填入表中的元素个数 / hash 表的长度。

        关于 α 的取值,对于开放定址法而言,一般取值为0.7 ~ 0.8 以下。当表中的载荷因子大于 α 的时候,我们就需要对我们的 hash 表进行扩容。

代码实现

1. 开放定址法

        该代码的实现原理为开放定址法,如下:

#pragma once
#include <vector>
#include <string>

using namespace std;

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 e : key)
			hash = hash * 131 + e;
		return hash;
	}
};

// 开放定值法
namespace Open_Address {
	enum status { 
		EMPTY, DELETE, EXIST 
	};

	template <class K, class V>
	struct HashData {
		pair<K, V> _kv;
		status _status;

		HashData() = default;

		HashData(const pair<K,V>& data)
			: _kv(data)
		{}
	};

	template <class K, class V, class Hash = HashFunc<K>>
	class HashTable {
		typedef HashData<K, V> Data;
	public:
		// 构造函数
		HashTable() 
			: _n(0)
		{
			// 先开十个空间
			_tables.resize(10);
		}

		bool insert(const pair<K, V>& data) {
			if (find(data.first))
				return false;
			Hash hf;
			if (10 * _n / _tables.size() >= 7) {
				// 扩容
				HashTable newTable;
				newTable._tables.resize(2 * _tables.size());
				for (size_t i = 0; i < _tables.size(); i++) {
					if (_tables[i]._status == EXIST)
						newTable.insert(_tables[i]._kv);
				}
				_tables.swap(newTable._tables);
			}
			size_t cur = hf(data.first);
			cur %= _tables.size();

			while (_tables[cur]._status == EXIST) {
				cur++;
				cur %= _tables.size();
			}
			_n++;
			_tables[cur] = Data(data);
			_tables[cur]._status = EXIST;
			return true;
		}

		HashData<K, V>* find(const K& key) {
			Hash hf;
			size_t cur = hf(key);
			cur %= _tables.size();
			while (_tables[cur]._status != EMPTY) {
				if (_tables[cur]._status != DELETE && _tables[cur]._kv.first == key)
					return &_tables[cur];
				cur++;
				cur %= _tables.size();
			}
			return nullptr;
		}

		//bool erase(const K& key) {
		//	Hash hf;
		//	size_t cur = hf(key);
		//	cur %= _tables.size();
		//	if (find(key) == false)
		//		return false;
		//	while (_tables[cur]._kv.first != key) {
		//		cur++;
		//		cur %= _tables.size();
		//	}
		//	_tables[cur] = Data();
		//	_tables[cur]._status = DELETE;
		//	_n--;
		//	return true;
		//}

		bool erase(const K& key) {
			Data* Fd = find(key);
			if (Fd) return false;
			*Fd = Data();
			Fd->_status = DELETE;
			_n--;
			return true;
		}
	private:
		vector<Data> _tables;
		size_t _n;
	};
}

        关于此代码的实现,我们首先先实现计算元素关键码的类,其中主要包括两种元素,一种为数字类元素,另一类为字符串元素(计算字符串元素的关键码存在多种方式,本篇实现的只是其中的一种)。我们只需要使用一个类模板就可以实现计算关键码的类,其中字符串元素类的计算为特化的类,如下:

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 e : key)
			hash = hash * 131 + e;
		return hash;
	}
};

        对于 hash 表的建立,我们还需要建立它的数据类型和状态类型(用来表征当前位置的状态),如下:

enum status { 
	EMPTY, DELETE, EXIST 
};

template <class K, class V>
struct HashData {
	pair<K, V> _kv;
	status _status;

	HashData() = default;

	HashData(const pair<K,V>& data)
		: _kv(data)
	{}
};

        接着是插入函数,其中插入函数主要的实现为判断当前是否需要扩容,以及计算出元素的关键码,然后将元素放入到对应的位置。如下:

bool insert(const pair<K, V>& data) {
	if (find(data.first))
		return false;
	Hash hf;
	// 判断是否需要扩容
	if (10 * _n / _tables.size() >= 7) {
		// 扩容
		HashTable newTable;
		newTable._tables.resize(2 * _tables.size());
		for (size_t i = 0; i < _tables.size(); i++) {
			if (_tables[i]._status == EXIST)
				newTable.insert(_tables[i]._kv);
		}
		_tables.swap(newTable._tables);
	}
	// 计算关键码
	size_t cur = hf(data.first);
	cur %= _tables.size();

	// 若当前位置存在元素,则放在下一个位置
	while (_tables[cur]._status == EXIST) {
		cur++;
		cur %= _tables.size();
	}
	_n++;
	_tables[cur] = Data(data);
	_tables[cur]._status = EXIST;
	return true;
}

        对于查找函数,我们需要计算出搜索元素的关键码,然后使用在表中不断的查找,若找到的位置为 EMPTY,则查找失败,若找到位置为 EXIST,则说明我们找到相应的元素了,如下:

HashData<K, V>* find(const K& key) {
	Hash hf;
	size_t cur = hf(key);
	cur %= _tables.size();
	while (_tables[cur]._status != EMPTY) {
		if (_tables[cur]._status != DELETE && _tables[cur]._kv.first == key)
			return &_tables[cur];
		cur++;
		cur %= _tables.size();
	}
	return nullptr;
}

        最后则是我们的删除元素,我们首先需要使用查找函数,查看是否能找到对应的元素,若找不到则直接返回 false(表示删除失败),若找到,直接将对应位置的状态设置为 DELETE,如下:

bool erase(const K& key) {
	Data* Fd = find(key);
	if (Fd) return false;
	*Fd = Data();
	Fd->_status = DELETE;
	_n--;
	return true;
}

2. hash 桶

        接下来的代码为实现 hash 桶的代码,如下:

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 e : key)
			hash = hash * 131 + e;
		return hash;
	}
};

// hash桶
namespace Hash_Bucket {
	template<class K, class V>
	struct HashNode {
		pair<K, V> _kv;
		HashNode* next = nullptr;

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

	template <class K, class V, class Hash = HashFunc<K>>
	class HashTable {
		typedef HashNode<K, V> Node;
	public:
		HashTable() {
			_n = 0;
			_tables.resize(10, nullptr);
		}

		// 拷贝构造函数
		HashTable(const HashTable<K, V>& ht) {
			_tables.resize(ht._tables.size());
			size_t size = ht._tables.size();
			for (size_t i = 0; i < size; i++) {
				Node* cur = ht._tables[i];
				while (cur) {
					Node* next = cur->next;
					// 插入元素
					insert(cur->_kv);
					cur = next;
				}
			}
		}


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

		bool insert(const pair<K, V>& kv) {
			if (find(kv.first))
				return false;
			// 扩容
			Hash hf;
			//if (_n == _tables.size()) {
			//	// 扩容的时候,只需要转换一整只即可
			//	HashTable newTable;
			//	newTable._tables.resize(2 * _tables.size());
			//	for (size_t i = 0; i < _tables.size(); i++) {
			//		if (_tables[i] != nullptr) {
			//			//Node* cur = _tables[i];
			//			//while (cur) {
			//			//	newTable.insert(cur->_kv);
			//			//	cur = cur->next;
			//			//}
			//			
			//			// 将所有节点取下来连接上去
			//			Node* cur = _tables[i];
			//			while (cur) {
			//				Node* next = cur->next;
			//				// 将节点取下来重新加到新表上
			//				size_t index = hf(cur->_kv.first) % newTable._tables.size();
			//				cur->next = newTable._tables[index];
			//				newTable._tables[index] = cur;
			//				cur = next;
			//			}
			//		}
			//	}
			//	_tables.swap(newTable._tables);
			//}

			if (_n == _tables.size()) {
				vector<Node*> newTables;
				newTables.resize(_tables.size() * 2, nullptr);
				size_t size = _tables.size();
				for (int i = 0; i < size; i++) {
					Node* cur = _tables[i];
					while (cur) {
						Node* next = cur->next;
						// 将节点取下来放在新表上
						size_t index = hf(cur->_kv.first) % newTables.size();
						cur->next = newTables[index];
						newTables[index] = cur;

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

			size_t index = hf(kv.first);
			index %= _tables.size();
			Node* newNode = new Node(kv);
			newNode->next = _tables[index];
			_tables[index] = newNode;
			_n++;
			return true;
		}

		Node* find(const K& key) {
			Hash hf;
			size_t index = hf(key);
			index %= _tables.size();
			Node* cur = _tables[index];
			if (cur == nullptr)
				return nullptr;
			while (cur) {
				if (cur->_kv.first == key)
					return cur;
				cur = cur->next;
			}
			return nullptr;
		}

		bool erase(const K& key) {
			if (find(key) == nullptr)
				return false;
			Hash hf;
			size_t index = hf(key);
			index %= _tables.size();
			Node* cur = _tables[index];
			Node* prev = _tables[index];
			if (cur->_kv.first == key) {
				_n--;
				_tables[index] = cur->next;
				delete prev;
				return true;
			}
			while (cur->_kv.first != key) {
				if (cur->_kv.first == key) {
					prev->next = cur->next;
					delete cur;
					_n--;
					return true;
				}
				prev = cur;
				cur = cur->next;
			}
			return false;
		}

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

        对于计算关键码的类,我们仍然沿用开放定址法的计算方法。

        但是对于 hash 表中的元素,我们则是采用指针的方法存放对应的元素,并将其初始化为 nullptr,表示当前位置没有元素。所以对于插入元素的设计,我们则将其设计为结点,当插入元素时,只需要连接到对应位置即可。

template<class K, class V>
struct HashNode {
	pair<K, V> _kv;
	HashNode* next = nullptr;

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

        接下来是插入元素,对于需要插入的元素,我们仍然是需要先判断该 hash 表是否需要扩容(这里的扩容和开放定址法的扩容不一样,元素个数和容量一致的时候我们才扩容)。然后通过计算关键码找到需要插入的位置,如下:

bool insert(const pair<K, V>& kv) {
	if (find(kv.first))
		return false;
	// 扩容
	Hash hf;

	// 扩容
	if (_n == _tables.size()) {
		vector<Node*> newTables;
		newTables.resize(_tables.size() * 2, nullptr);
		size_t size = _tables.size();
		for (int i = 0; i < size; i++) {
			Node* cur = _tables[i];
			while (cur) {
				Node* next = cur->next;
				// 将节点取下来放在新表上
				size_t index = hf(cur->_kv.first) % newTables.size();
				cur->next = newTables[index];
				newTables[index] = cur;

				cur = next;
			}
			// 将原 hash 表对应位置设置为null
			_tables[i] = nullptr;
		}
		_tables.swap(newTables);
	}
	size_t index = hf(kv.first);
	index %= _tables.size();
	Node* newNode = new Node(kv);
	newNode->next = _tables[index];
	_tables[index] = newNode;
	_n++;
	return true;
}

        接下来是查找函数,此查找函数就较为简单,因为直接就可以通过计算关键码查询是否能找到该元素。如下:

Node* find(const K& key) {
	Hash hf;
	size_t index = hf(key);
	index %= _tables.size();
	Node* cur = _tables[index];
	if (cur == nullptr)
		return nullptr;
	while (cur) {
		if (cur->_kv.first == key)
			return cur;
		cur = cur->next;
	}
	return nullptr;
}

        最后是删除元素函数,仍然是先查找元素是否在 hash 表中,查找到之后在进行删除,其中会分为删除元素的位置在当前 hash 表的最后链接位置和中间位置进行分别讨论,如下:

bool erase(const K& key) {
	if (find(key) == nullptr)
		return false;
	Hash hf;
	size_t index = hf(key);
	index %= _tables.size();
	Node* cur = _tables[index];
	Node* prev = _tables[index];
	if (cur->_kv.first == key) {
		_n--;
		_tables[index] = cur->next;
		delete prev;
		return true;
	}
	while (cur->_kv.first != key) {
		if (cur->_kv.first == key) {
			prev->next = cur->next;
			delete cur;
			_n--;
			return true;
		}
		prev = cur;
		cur = cur->next;
	}
	return false;
}

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

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

相关文章

【Git从入门到精通】——Git常用命令总结

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

火星全球彩色影像图介绍(中分辨率相机)

一、数据基本信息 该数据是利用天问一号轨道器中分辨率相机获取的影像经光度校正、几何校正、全球制图等制作而成的全火星地图数据DOM&#xff0c;每个数据包含一个tif数据文件。该影像图分辨率为76米。 任务型号&#xff1a;天问一号 搭载平台&#xff1a;环绕器 数据获…

批量提取网页表格内容至excel文件

问题背景 将网页的表格内容&#xff08;5237个股票信息&#xff09;复制粘贴到excel文件中 网址&#xff1a;A股上市公司名单-A股上市公司名录-A股上市公司大全-商业计划书-可研报告-中商产业研究院数据库-中商情报网 实现代码 # 导入包 import pandas as pd import time# 创…

[安洵杯 2019]easy_web1

知识点&#xff1a; 1.base64加解密 2.md5加解密 3.md5碰撞绕过强类型比较 4.Linux命令绕过 进入页面发现url地址中存在 img参数和一个cmd参数&#xff0c;img参数看上去像是base64编码&#xff0c;可以去尝试一下解码. 进行了两次base64解密得到3535352e706e67看着像16进制那么…

SSM整合--笔记总结

1.概述 ssm(springmvc spring mybatis)这三个框架的整合。 spring和springmvc他们隶属于一家公司&#xff0c;他们无需整合。 spring和mybatis框架的整合。 spring把mybatis中的配置内容放到自己的配置文件中。因为我们可以让tomcat加载spring配置文件。 思考:mybatis配置文件…

SD card知识学习

一、基础知识 1、简介 SD Card 全称(Secure Digital Memory Card)&#xff0c;日本电子公司松下&#xff08;Panasonic&#xff09;、瑞典公司爱立信&#xff08;Ericsson&#xff09;、德国公司西门子&#xff08;Siemens&#xff09;共同开发的&#xff0c;于1999年发布根…

Java中如何发送短信?(荣耀典藏版)

大家好&#xff0c;我是月夜枫~~ 本来是没计划写这方面的文章&#xff0c;奈何粉丝经常私信要求整理一篇发短信的文章&#xff0c;今天他来了。 很多业务场景里&#xff0c;我们都需要发送短信&#xff0c;比如登陆验证码、告警、营销通知、节日祝福等等。 这篇文章&#xf…

JavaSE——集合框架二(4/6)-Map集合的遍历方式(键找值,键值对,Lambda)、Map集合案例(需求与分析,问题解决)

目录 Map集合的遍历方式 键找值 键值对 Lambda Map集合案例 需求与分析 问题解决 Map集合的遍历方式 键找值 先获取Map集合全部的键&#xff0c;再通过遍历键来找值。 键值对 把“键值对”看成一个整体进行遍历&#xff08;较为复杂&#xff09; Lambda JDK 1.8 开…

C嘎嘎:函数模版和类模版

目录 泛型编程 函数模版 函数模版概念 函数模版的格式 函数模版的原理 函数模版的实例化 函数参数的匹配原则 类模版 类模版的定义格式 类模版的实例化 泛型编程 如何实现一个通用的交换函数呢 void Swap(int& left, int& right) {int temp left;left rig…

【每日一练】python之sum()求和函数实例讲解

在Python中&#xff0c; sum()是一个内置函数&#xff0c;用于计算可迭代对象&#xff08;如列表、元组等&#xff09;中所有元素的总和。如下实例&#xff1a; """ 收入支出统计小程序 知识点:用户输入获取列表元素添加sum()函数&#xff0c;统计作用 "&…

快捷:通过胶水语言实现工作中测试流程并行、加速

通过胶水语言实现工作中测试流程并行、加速 通过胶水语言实现工作中测试流程并行、加速工作场景&#xff08;背景&#xff09;问题抽象&#xff08;挑战&#xff09;如何做&#xff08;行动&#xff09;获得了什么&#xff08;结果&#xff09;后记相关资源 通过胶水语言实现工…

Oracle 性能诊断包收费依据

Which Data Dictionary or Dynamic Performance Views Require Purchase of the Diagnostics and / or Tuning Pack? (Doc ID 2082355.1)​编辑To Bottom In this Document Goal Solution References APPLIES TO: Oracle Database - Enterprise Edition - Version 10.2.0.5 …

AI口语练习APP主要功能

AI口语练习APP主要功能可以分为以下几个方面&#xff0c;AI口语练习APP可以帮助用户克服练习口语的场地、时间、语言环境等限制&#xff0c;更方便、高效地练习口语&#xff0c;提高英语口语水平。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎…

Profibus协议转Profinet协议网关模块连接智能电表通讯案例

一、背景 在工业自动化领域&#xff0c;Profibus协议和Profinet协议是两种常见的工业通讯协议&#xff0c;而连接智能电表需要用到这两种协议之间的网关模块。本文将通过一个实际案例&#xff0c;详细介绍如何使用Profibus转Profinet模块&#xff08;XD-PNPBM20&#xff09;实…

电脑案件冲突问题

一.故障展示 有一天我打开了电脑,发现3这个数字按键一直在输入,拔了外界的键盘,他这个按键还是会冲突 ,就如同上面的图一样 ,可能是电脑内部的键位进了灰卡住了什么东西导致的,于是我果断就电脑上的按键给扣下来了,扣的时候不知道里面的结构非常的谨慎,所以没导致里面的结构被损…

Amazon EC2 部署Ollama + webUI

最近和同事闲聊&#xff0c;我们能不能内网自己部署一个LLM&#xff0c;于是便有了Ollama webUI的尝试 对于Linux&#xff0c;使用一行命令即可 curl -fsSL https://ollama.com/install.sh | shollama --help Large language model runnerUsage:ollam…

C语言 ——— const关键字

目录 const修饰变量 const修饰指针变量 const放在指针类型之前 const放在指针类型之后 小结 const修饰变量 当 const 修饰 int类型 的 变量a 后&#xff0c;此时的 变量a 就具有长属性&#xff0c;就不能被赋值为其他的值 将 变量a的地址 存储到 指针变量pa 中&#xff…

【JavaScript】解决 JavaScript 语言报错:Uncaught TypeError: XYZ is not iterable

文章目录 一、背景介绍常见场景 二、报错信息解析三、常见原因分析1. 对非数组类型使用 for...of 循环2. 对非可迭代对象使用扩展运算符3. 在 Promise.all 中传递非可迭代对象4. 使用解构赋值时&#xff0c;右侧值非可迭代 四、解决方案与预防措施1. 确保使用可迭代对象2. 使用…

开源项目的浪潮:机遇、挑战与未来展望

&#x1f308;所属专栏&#xff1a;【其它】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的点…

二分查找和斐波那契查找

这里写自定义目录标题 二分查找斐波那契查找二分查找改进B二分查找改进C 二分查找 int binSearch(int* arr, int lo, int hi,int target) {while (lo < hi){int mid lo ((hi - lo) >> 1);if (arr[mid] > target) hi mid;else if (arr[mid] < target) lo mi…