哈希表HashMap(基于vector和list)

C++数据结构与算法实现(目录)

1 什么是HashMap?

我们这里要实现的HashMap接口不会超过标准库的版本(是一个子集)。

HashMap是一种键值对容器(关联容器),又叫字典

和其他容易一样,它可以对存储的元素进行增删改查操作。

它之所以叫关联容器,是因为它的每个元素都是一对(键 key 和值 value)。

比如:

HashMap h;
h[123] = string("张三");//每个元素包括一个键(123)和值("张三")

这种容器可以快速的访问容器中的任何一个元素。

2 为何可以快速访问元素?桶

它之所以能快速的做到这一点,是因为可以快速的知道这个容器里有没有这个元素。

而快速知道容器里有没有这个元素的关键就在于拿到一个key,就知道这个元素会在哪个里面。

这里使用一个函数,也叫hash函数,计算key对应的桶:

auto index = hashFunction(key);

HashMap的结构如下图所示:

槽桶是一个vector数组,每个数组里是一个list链表。

槽桶是一个vector数组,每个数组里是一个list链表

3 冲突与解决

不同的 key 如果都映射到同一个index怎么让同一个桶存两个value呢?

hashFun(key1) == hashFun(key2)

可以使用一个单链表,将相同 hash 值的 key 放到同一个桶里。

hashFunction(54) == hashFunction(28) 的情况

4 元素的均匀分布

怎么样设计hashFun使得元素会均匀的落到每个链表里?

使得每个链表里都有大概n/N个元素。其中,n是元素总数,N是槽的个数。是我们需要重点考虑的。

为了解决这个问题,我们要求hashFunction 对每一个key,都能均匀的分散到各个桶里,最简单有效的办法就是将key看成(转换成)整数,对vector 桶的数量求余数,余数作为桶的索引(本文我们就使用这种方法)。

理论上让每个桶里都有total/N个元素

5 再哈希 rehash

如果随着元素数量越来越多,元素都堆积在某一个或几个链表里,其他链表都空着。这样HashMap又退化成链表了:

HashMap退化成了链表,因为元素散落不均匀导致

解决了元素的均匀分布之后,我们还会遇到下一个问题。

元素越来越多以后,桶bucket的数量应该要增加,不然每个链表都很长,效率还是会降低。

元素太多堆在list里,vector容量不够多,再次退化

这时候就要再hash。

判断什么时候应该rehash的机制就是看装载因子load_factor 的大小。

load_factor 来表示现在是不是元素数量已经很多了,如果这个值 大于0.8,就表示每个桶里堆积的元素都比较多了。就要使用更多的桶来存放元素了。

装载因子的计算方法,有个很简单的办法,就是让每个vector里的链表尽量的短。那就是容器元素接近vector元素的数量:

load_factor = n/N

其中,n 是元素总数,N是槽的个数。

比如, n/N > 0.8 ,我们就再 hash 一次。

6 成员函数设计要点

我们将使用vector 和 list 来实现这个容器,让大家感受一下用轮子造轮子到底有多爽。

感受软件复用的力量。

感受抽象的力量。

1) clear

这个函数负责清空元素,回到初始状态,也就是对象被构造出来的状态。

但是,由于我们的HashMap 在初始状态就开辟了8个空间为未来存放元素使用,所以clear会涉及到调用reset。

而 reset需要记得初始化一切数据,包括将容量设置为8.

清空的HashMap,vector并不为空

2) reset

reset是给默认构造函数调用的,其具体行为参考 clear部分的介绍。

同时,reset也会被clear调用。那直接用clear一个函数不就好了吗?实际上是为了代码更容易阅读理解。

reset为了初始化, clear是用户调用的,用户只知道清空容器,并不关心reset。

3) copy

copy函数负责拷贝另一个对象的数据给当前对象。

主要是拷贝构造函数和赋值操作符会调用。

首先,需要先清空当前对象的数据。然后再做深拷贝即可。

4 rehash 再哈希

这个函数是最复杂的,它主要分为如下几个部分:

(1)创建一个新的HashMap对象

(2)新对象的桶数量要翻倍(rehash的主要作用就是横向扩展,让每个list变短)

(3)把原来的元素装到新的HashMap对象里

(4)交换新旧对象的资源实现接管新场所,释放旧空间的目的

5 operator[]

这个函数会添加元素(如果元素不存在的话),而如果此时容器已经比较拥挤了,就是扩容的时机。

4) 其他函数

更多函数设计的实现细节思路,参考下面代码中的注释部分。

C++实现

下面我们将实现一个 key 类型为 intvalue 类型为 std::string 的HashMap。

HashMap.h 头文件

#pragma once
#include<string>
#include<list>
#include<memory>
#include<vector>
#include<utility>
using namespace std;
class HashMap
{
public:HashMap(void);~HashMap(void);HashMap(const HashMap& from);HashMap& operator=(const HashMap& from);
public:bool empty(void) const { return m_size == 0; }size_t size(void) const;bool contains(const int& key) const;std::string& operator[](const int& key);void erase(const int& key);
public:using value_type = std::pair<int, std::string>;
public:class iterator{friend class HashMap;public:iterator& operator++(void);//++itrbool operator==(const iterator& itr);bool operator!=(const iterator& itr);value_type& operator*(void);private:iterator(HashMap* hashmap, size_t bucket_index, std::list<value_type>::iterator itr);private:std::list<value_type>::iterator m_itr;size_t m_bucket_index;//-1 for end()HashMap* m_hashmap;};iterator begin(void);iterator end(void);void clear(void);
private:size_t hash(const int& key) const;void copy(const HashMap& from);//装载因子double load_factor(void) const { return (double)m_size / m_bucket_array_length; };void re_hash(void);//扩大容量void reset(void);
private:std::vector<std::list<value_type>> m_bucket_array;size_t m_size = 0;size_t m_bucket_array_length = 8;
};

实现特点:增删改查、迭代器、再哈希、复制控制、基于std::vector、std::list (复用、模块化)

HashMap.cpp 源文件

#include "HashMap.h"
#include <algorithm>
#include <iostream>
#include <cassert>
using namespace std;HashMap::HashMap(void)
{//(1) your code 直接调用reset完成默认开辟8个元素空间cout << "HashMap()" << endl;
}HashMap::~HashMap(void)
{// 析构函数什么也不用做,因为存放数据的容器 vector list 会自动释放其拥有的元素cout << "~HashMap()" << endl;
}HashMap::HashMap(const HashMap& from)
{//(2) your code 直接调用 copy 即可完成拷贝cout << "HashMap(const HashMap &)" << endl;
}HashMap& HashMap::operator=(const HashMap& from)
{//(2) your code 直接调用 copy 即可完成拷贝cout << "HashMap::operator=(const HashMap & from)" << endl;return *this;
}size_t HashMap::size(void) const
{return m_size;
}bool HashMap::contains(const int& key) const
{//(3) your code 通过hash(key) 得到数据如果存在应该在哪个桶里//(4) your code 再到桶里 list 查找有没有这个元素 ,在链表中 线性查找return false;//这里需要改成真正的查找结果
}std::string& HashMap::operator[](const int& key)
{//(5) your code 如果装载因子 大于了 0.8 就 re_hash 扩大容量//通过hash(key) 得到数据如果存在应该在哪个桶里auto index = hash(key);assert(m_bucket_array.size() > index);auto& bucket = m_bucket_array[index];//再到桶里 list 查找有没有这个元素 ,在链表中 线性查找。返回 list<T>::iteratorauto itr = std::find_if(bucket.begin(), bucket.end(), [key](const value_type& value) {return value.first == key;});if (itr == bucket.end()){//(6) your code. key not exist, insert empty std::string as default value//(7) your code. increase the size of current hash map.//(8) your code. return elementstatic string s_bad;//请删除return s_bad;//请重写}else{//(9) your code. return elementstatic string s_bad;//请删除return s_bad;//请重写}
}void HashMap::erase(const int& key)
{auto index = hash(key);auto& bucket = m_bucket_array[index];auto itr = std::find_if(bucket.begin(), bucket.end(), [key](const value_type& value) {return value.first == key;});if (itr == bucket.end()){throw std::runtime_error("erasing not exist key!");}else{--m_size;bucket.erase(itr);}
}HashMap::iterator HashMap::begin(void)
{for (size_t i = 0; i < m_bucket_array_length; i++){if (!m_bucket_array[i].empty()){return HashMap::iterator(this, i, m_bucket_array[i].begin());}}return end();
}HashMap::iterator HashMap::end(void)
{return HashMap::iterator(this, -1, std::list<value_type>::iterator());
}size_t HashMap::hash(const int& key) const
{// 使用key 得到元素在哪个桶里。使用求余数来得到。// 这种算法可以认为是足够简单而且元素会均匀分布在各个桶里的int index = key % m_bucket_array_length;return index;
}void HashMap::clear(void)
{reset();
}void HashMap::copy(const HashMap& from)
{clear();m_bucket_array.resize(from.m_bucket_array_length);for (size_t i = 0; i < m_bucket_array_length; i++){//10 your code. 使用链表的赋值操作符直接拷贝链表}m_size = from.m_size;
}void HashMap::re_hash(void)
{//另起炉灶,新创建一个HashMapHashMap re_hashmap;//将新的炉灶扩大容量re_hashmap.m_bucket_array_length = this->m_bucket_array_length * 2 + 1;//11 your code. 将新的炉灶实际的桶开辟出来//使用迭代器,遍历原来的(this)所有元素,将所有元素拷贝到新的炉灶里for (auto itr = begin(); itr != end(); ++itr){//12 your code. 先根据key获得桶,再把value追加到桶里list的末尾}//交换新旧两个容器的内容,接管新炉灶std::swap(re_hashmap.m_bucket_array, m_bucket_array);//其他成员变量更新this->m_bucket_array_length = re_hashmap.m_bucket_array_length;re_hashmap.m_size = this->m_size;
}void HashMap::reset(void)
{m_size = 0;m_bucket_array.clear();m_bucket_array_length = 8;m_bucket_array.resize(m_bucket_array_length);
}HashMap::iterator& HashMap::iterator::operator++(void)
{//valid itr can always do ++itrauto index = m_hashmap->hash(m_itr->first);auto& bucket = m_hashmap->m_bucket_array[index];++m_itr;//find next list or the end() occorif (m_itr == bucket.end()){for (size_t i = m_bucket_index + 1; i < m_hashmap->m_bucket_array_length; i++){if (!m_hashmap->m_bucket_array[i].empty()){m_bucket_index = i;m_itr = m_hashmap->m_bucket_array[i].begin();return *this;}}m_bucket_index = -1;//end()}return *this;
}bool HashMap::iterator::operator==(const iterator& itr)
{if (itr.m_bucket_index != this->m_bucket_index){return false;}if (itr.m_bucket_index == -1 && this->m_bucket_index == -1){return true;//both end()}else{bool equal = &*(m_itr) == &*(itr.m_itr);return equal;//pointed to the same value address}
}bool HashMap::iterator::operator!=(const iterator& itr)
{bool equal = (*this == itr);return  !equal;
}HashMap::value_type& HashMap::iterator::operator*(void)
{return *m_itr;
}HashMap::iterator::iterator(HashMap* hashmap, size_t bucket_index, std::list<value_type>::iterator itr)
{m_hashmap = hashmap;m_itr = itr;m_bucket_index = bucket_index;
}

main.cpp

#include <iostream>
#include "HashMap.h"
#include <cassert>
#include <unordered_map>
#include <set>
using namespace std;//------下面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------
#include <algorithm>
#include <cstdlib>
#include <iostream> 
#include <vector>
#include <utility>
using namespace std;
struct Record { Record(void* ptr1, size_t count1, const char* location1, int line1, bool is) :ptr(ptr1), count(count1), line(line1), is_array(is) { int i = 0; while ((location[i] = location1[i]) && i < 100) { ++i; } }void* ptr; size_t count; char location[100] = { 0 }; int line; bool is_array = false; bool not_use_right_delete = false; }; bool operator==(const Record& lhs, const Record& rhs) { return lhs.ptr == rhs.ptr; }std::vector<Record> myAllocStatistic; void* newFunctionImpl(std::size_t sz, char const* file, int line, bool is) { void* ptr = std::malloc(sz); myAllocStatistic.push_back({ ptr,sz, file, line , is }); return ptr; }void* operator new(std::size_t sz, char const* file, int line) { return newFunctionImpl(sz, file, line, false); }void* operator new [](std::size_t sz, char const* file, int line)
{return newFunctionImpl(sz, file, line, true);
}void operator delete(void* ptr) noexcept { Record item{ ptr, 0, "", 0, false }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); } }void operator delete[](void* ptr) noexcept { Record item{ ptr, 0, "", 0, true }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (!itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); } }
#define new new(__FILE__, __LINE__)
struct MyStruct { void ReportMemoryLeak() { std::cout << "Memory leak report: " << std::endl; bool leak = false; for (auto& i : myAllocStatistic) { if (i.count != 0) { leak = true; std::cout << "leak count " << i.count << " Byte" << ", file " << i.location << ", line " << i.line; if (i.not_use_right_delete) { cout << ", not use right delete. "; }	cout << std::endl; } }if (!leak) { cout << "No memory leak." << endl; } }~MyStruct() { ReportMemoryLeak(); } }; static MyStruct my; void check_do(bool b, int line = __LINE__) { if (b) { cout << "line:" << line << " Pass" << endl; } else { cout << "line:" << line << " Ohh! not passed!!!!!!!!!!!!!!!!!!!!!!!!!!!" << " " << endl; exit(0); } }
#define check(msg)  check_do(msg, __LINE__);
//------上面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------int main()
{//create insert find{HashMap students;check(students.empty());check(students.size() == 0);int id = 123;check(students.contains(id) == false);std::string name("zhangsan");students[id] = name;check(!students.empty());check(students.size() == 1);check(students.contains(id));check(students[id] == name);}//modify value{HashMap students;int id = 123;std::string name("zhangsan");students[id] = name;std::string name2("lisi");students[id] = name2;check(students[id] == name2);check(students.size() == 1);}//erase{HashMap students;int id = 123;std::string name("zhangsan");students[id] = name;students.erase(id);check(!students.contains(id));check(students.size() == 0);}//clear value{HashMap students;int id = 123;std::string name("zhangsan");students[id] = name;std::string name2("lisi");students[id] = name2;check(students[id] == name2);check(students.size() == 1);students.clear();check(students.size() == 0);students.clear();}//copy{HashMap students;int id = 123;std::string name("zhangsan");students[id] = name;HashMap students2(students);//copy constructorcheck(students.contains(id));check(students.size() == 1);students[456] = "lisi";check(students.contains(456));check(!students2.contains(456));students2[789] = "wanger";check(!students.contains(789));check(students2.contains(789));check(students.size() == 2);check(students2.size() == 2);}//assignment{HashMap students;int id = 123;std::string name("zhangsan");students[id] = name;students[456] = "lisi";HashMap students2;students2 = students;check(students2.contains(id));check(students2.contains(456));check(students.size() == 2);}//iteratorconst int total = 50;{int id_creator = 1;HashMap students;std::string name("zhangsan");for (int i = 1; i <= total; ++i){students[id_creator++] = name + std::to_string(i);}check(students.size() == total);std::multiset<int> all_keys;for (auto& item : students){all_keys.insert(item.first);cout << item.first << " " << item.second << endl;}int i = 1;for (auto item : all_keys){assert(item == i++);}check(i == total + 1);students.clear();for (int i = 1; i <= total; ++i){students[i] = std::to_string(i);}check(students.contains(1));check(students.contains(total));check(students[1] == "1");check(students[total] == to_string(total));check(students.size() == total);}
}

输出如下:

HashMap()
line:30 Pass
line:31 Pass
line:33 Pass
line:36 Pass
line:37 Pass
line:38 Pass
line:39 Pass
~HashMap()
HashMap()
line:49 Pass
line:50 Pass
~HashMap()
HashMap()
line:59 Pass
line:60 Pass
~HashMap()
HashMap()
line:70 Pass
line:71 Pass
line:73 Pass
~HashMap()
HashMap()
HashMap(const HashMap &)
line:83 Pass
line:84 Pass
line:86 Pass
line:87 Pass
line:89 Pass
line:90 Pass
line:91 Pass
line:92 Pass
~HashMap()
~HashMap()
HashMap()
HashMap()
HashMap::operator=(const HashMap & from)
line:103 Pass
line:104 Pass
line:105 Pass
~HashMap()
~HashMap()
HashMap()
HashMap()
~HashMap()
HashMap()
~HashMap()
HashMap()
~HashMap()
line:117 Pass
1 zhangsan1
2 zhangsan2
3 zhangsan3
4 zhangsan4
5 zhangsan5
6 zhangsan6
7 zhangsan7
8 zhangsan8
9 zhangsan9
10 zhangsan10
11 zhangsan11
12 zhangsan12
13 zhangsan13
14 zhangsan14
15 zhangsan15
16 zhangsan16
17 zhangsan17
18 zhangsan18
19 zhangsan19
20 zhangsan20
21 zhangsan21
22 zhangsan22
23 zhangsan23
24 zhangsan24
25 zhangsan25
26 zhangsan26
27 zhangsan27
28 zhangsan28
29 zhangsan29
30 zhangsan30
31 zhangsan31
32 zhangsan32
33 zhangsan33
34 zhangsan34
35 zhangsan35
36 zhangsan36
37 zhangsan37
38 zhangsan38
39 zhangsan39
40 zhangsan40
41 zhangsan41
42 zhangsan42
43 zhangsan43
44 zhangsan44
45 zhangsan45
46 zhangsan46
47 zhangsan47
48 zhangsan48
49 zhangsan49
50 zhangsan50
line:129 Pass
HashMap()
~HashMap()
HashMap()
~HashMap()
HashMap()
~HashMap()
line:135 Pass
line:136 Pass
line:137 Pass
line:138 Pass
line:139 Pass
~HashMap()
Memory leak report:
No memory leak.

项目下载:start file

链接:百度网盘 请输入提取码

提取码:1234

加油吧!

期待你的pass

答案在此

哈希表HashMap(基于vector和list)(答案)_C++开发者的博客-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue笔记

第一章&#xff1a;Vue环境搭建 1.搭建Vue环境 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!-- 1.引入Vue.js--><script src"1.vue.js"></scr…

阿晨的运维笔记 | CentOS部署Docker

使用yum安装 # step 1: 安装必要的一些系统工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Step 3: 更新并安装 …

LeetCode——无重复的最长子串(中等)

题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为…

利用随机数生成猜数字游戏【C语言】

猜数字游戏实现 游戏要求生成随机数rand 函数srand 函数time 函数 确定生成随机数的范围猜数字游戏实现 游戏要求 自动生成随机数的范围&#xff1a;0-99玩家猜数字&#xff0c;猜数字的过程中&#xff0c;根据猜测数据的大小给出大了或小了的反馈&#xff0c;直到猜对&#x…

面经:微服务

文章目录 参考资料一. 微服务概述1. CAP理论2. BASE理论3. SpringBoot 与 SpringCloud对比 二. 服务注册&#xff1a;Zookeeper,Eureka,Nacos,Consul1. Nacos两种健康检查方式&#xff1f;2. nacos中负责负载均衡底层是如何实现的3. Nacos原理4. 临时实例和持久化(非临时)实例 …

数据结构1

数据结构是计算机科学中存储和组织数据的一种方式&#xff0c;它定义了数据的表示方式和对数据进行操作的方法&#xff0c;常见的数据结构包括数组、栈、链表、队列、树、图等。 目录 一、常见的数据结构 1.数组 2.栈 3.队列 4.链表 5.树 6.图 一、常见的数据结构 1.数…

uniapp 项目实践总结(二)从零开始搭建一个项目

导语:本篇文章主要是项目方面的技术开发总结,新建一个项目可以选择使用可视化界面,也可以使用命令行搭建。 目录 可视化界面命令行搭建安卓开发环境苹果开发环境可视化界面 安装软件 使用官方推荐的 HbuilderX 软件,开发方式比较简单,内置相关环境以及终端,无需配置 no…

windows自带远程桌面连接的正确使用姿势

摘要 目前远程办公场景日趋广泛&#xff0c;对远程控制的需求也更加多样化&#xff0c;windows系统自带了远程桌面控制&#xff0c;在局域网内可以实现流程的远程桌面访问及控制。互联网使用远程桌面则通常需要使用arp等内网穿透软件&#xff0c;市场上teamviewer、Todesk、向…

进程管理死死的学

进程管理 文件属性 chattr【扩展】 chattr chattr i 文件名 # 添加权限 a 可追加&#xff0c;不可修改 i 只可查看 A 不修改访问时间 charrt -i 文件名 # 取消权限 -R 递归处理&#xff0c;将指令目录下的所有文件及子目录一并处理&#xff1b;lsattr 查看文件属性 lsattr …

《论文阅读21》Equivariant Multi-View Networks

一、论文 研究领域&#xff1a;计算机视觉 | 多视角数据处理中实现等变性论文&#xff1a;Equivariant Multi-View Networks ICCV 2019 论文链接视频链接 二、论文简述 在计算机视觉中&#xff0c;模型在不同视角下对数据&#xff08;例如&#xff0c;点云、图像等&#xff0…

Linux 操作系统实战视频课 - GPIO 基础介绍

文章目录 一、GPIO 概念说明二、视频讲解沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将讲解 GPIO 。 一、GPIO 概念说明 ARM 平台中的 GPIO(通用输入/输出)是用于与外部设备进行数字输入和输出通信的重要硬件接口。ARM 平台的 GPIO 特性可以根据具体的芯…

Spring-TX 事务

目录 一、事务的种类 二、Spring事务管理器 三、事务注解使用 四、事务注解属性 一、事务的种类 1.编程式事务 所谓编程式事务就是用代码手写事务&#xff0c;包含了事务的开始&#xff0c;具体事务&#xff0c;事务的提交和事务的回滚。在这期间就会产生一些冗余问题&am…

Vulnhub系列靶机---JIS-CTF-VulnUpload-CTF01靶机

文章目录 1、网卡配置2、信息收集主机发现端口扫描目录扫描 3、漏洞探测4、漏洞利用一句话木马蚁剑 GetShellSSH连接提权 JIS文档说明 JIS-CTF-VulnUpload-CTF01靶机下载地址 该靶机有5个flag 1、网卡配置 开启靶机&#xff0c;看见加载的进度条按shift&#xff0c;看到如下界…

Astro 3.0 闪亮登场,让你轻松构建更快速、更流畅的前端应用

网站前端开发的领域不断演进&#xff0c;随着Astro 3.0的发布&#xff0c;它正在迈出巨大的一步。Astro 3.0引入了突破性的功能和增强功能&#xff0c;承诺改变我们构建和体验网络应用程序的方式。在本文中&#xff0c;我们将探讨Astro 3.0的主要亮点以及如何赋予开发人员创建更…

直播平台源码弹性云托管技术:稳定直播与降低成本的利器

在当今的互联网时代&#xff0c;直播平台源码层出不穷&#xff0c;直播平台源码不仅可以让人们获取最新的资讯、查找资料等信息获取&#xff0c;还能让人们在其中观看短视频、直播、与其他人聊天等互动放松&#xff0c;直播平台源码的受欢迎与平台人数的增加使得人们在选择直播…

【juc】读写锁ReentrantReadWriteLock

目录 一、说明二、读读不互斥2.1 代码示例2.2 截图示例 三、读写互斥3.1 代码示例3.2 截图示例 四、写写互斥4.1 代码示例4.2 截图示例 五、注意事项5.2.1 代码示例5.2.2 截图示例 一、说明 1.当读操作远远高于写操作时&#xff0c;使用读写锁让读读可以并发&#xff0c;来提高…

关于linux openssl的自签证书认证与nginx配置

自签文档链接 重点注意这块&#xff0c;不能写一样的&#xff0c;要是一样的话登录界面锁会报不安全 域名这块跟最后发布的一致 nginx配置的话 server {listen 443 ssl; //ssl 说明为https 默认端口为443server_name www.skyys.com; //跟openssl设置的域名保持一致s…

ffmpeg把RTSP流分段录制成MP4,如果能把ffmpeg.exe改成ffmpeg.dll用,那音视频开发的难度直接就降一个维度啊

比如&#xff0c;原来我们要用ffmpeg录一段RTSP视频流转成MP4&#xff0c;我们有两种方案&#xff1a; 方案一&#xff1a;可以使用以下命令将rtsp流分段存储为mp4文件 ffmpeg -i rtsp://example.com/stream -vcodec copy -acodec aac -f segment -segment_time 3600 -reset_t…

CSS中如何隐藏元素但保留其占位空间(display:none vs visibility:hidden)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ display: none;⭐ visibility: hidden;⭐ 如何选择⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为…

QT DAY 2

window.cpp #include "window.h" #include<QDebug> #include<QIcon> Window::Window(QWidget *parent) //构造函数的定义: QWidget(parent) //显性调用父类的构造函数 {//this->resize(430,330);this->resize(QSize(800,600));// this…