【C++】stack、queue和priority_queue的模拟实现

在本篇博客中,作者将会讲解STL中的stackqueuepriority_queue的模拟实现,同时还会带大家了解一下deque这个容器。

一.什么是适配器

STL中一共有6大组件:容器,适配器,空间配置器,仿函数,迭代器,算法

其中像vector、list这种数据结构叫做容器,而像stack、queue这种数据结构叫做适配器

为什么呢?


因为stack和queue是通过deque这个容器转换过来的,也就是说,将deque容器的成员函数转换stackqueue成员函数

通过查看C++的手册,发现stack和queue类中有一个模板,其中第二个模板参数就是deque,即在stack和queue类中,是通过deque这个容器来实现的。


可能看到这里,还有同学不懂适配器到底是什么,那么在这里通过几张图来解释一下。 


在解释之前,我们来看看deque这个容器的具体的成员函数。

可以发现,deque这个容器的成员函数特别的多。 


接下来我们再看看stack和queue的成员函数。 

 

可以发现deque的成员函数特别的多,而stack和queue的成员函数特别的少,但是在stack和queue的实现中又用到了deque,所以要减少deque的成员函数来实现stack和queue。


如下图:

二.stack的模拟实现 

在C++库中,stack和queue都是通过使用deque容器来实现的,但是为了能够便于大家理解,在stack的模拟实现中,我们使用vector这个容器来实现,而在queue的模拟实现中,我们使用list这个容器来实现,实现完后,我们再来简单的了解一下deque。

stack的基本成员函数

首先我们来梳理一下stack的基本成员函数,看看这个数据结构需要用到那些功能。

empty:判断是否为空

size:求数据个数

top:取栈顶数据

push:入栈

pop:出栈

剩下的两个暂时不讲解。

stack模拟实现

梳理完stack的基本成员函数后,我们就可以来实现一下了。 

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

namespace My_Stack
{
	//使用模板来给一个vector参数
	template<class T,class Container=vector<T>>
	class stack
	{
	public:
		stack()
		{}

		//入栈
		void push(const T& val)
		{
			_s.push_back(val);
		}

		//出栈
		void pop()
		{
			_s.pop_back();
		}

		//判断是否为空
		bool empty() const
		{
			return _s.empty();
		}

		//获取数据个数
		size_t size() const
		{
			return _s.size();
		}

		//取栈顶数据
		T& top()
		{
			return _s.back();
		}

		//const类型取栈顶数据
		const T& top() const
		{
			return _s.back();
		}

	private:
		Container _s;
	};

	void Test1()
	{
		stack<int> s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);

		while (!s1.empty())
		{
			cout << s1.top() << " ";
			s1.pop();
		}
	}
}

三.queue的模拟实现

在模拟实现queue时,我们使用list这个容器来实现它,因为list提供的成员函数比较适合queue。 

queue的基本成员函数 

在实现queue之前,同样的,我们先来看看queue的基本成员函数。

empty:判断队列是否为空

size:获取数据个数

front:获取队头数据

back:获取队尾数据

push:入队列

pop:出队列

queue的模拟实现 

#pragma once
#include<iostream>
#include<list>

namespace My_queue
{
    //模板参数给一个list容器
	template<class T,class Container=list<T>>
	class queue
	{
	public:
		queue()
		{}

		//入队列
		void push(const T& val)
		{
			_l.push_back(val);
		}

		//出队列
		void pop()
		{
			_l.pop_front();
		}

		//取对头
		T& front()
		{
			return _l.front();
		}

		const T& front() const
		{
			return _l.front();
		}

		//取队尾
		T& back()
		{
			return _l.back();
		}

		const T& back() const
		{
			return _l.back();
		}

		//判断是否为空
		bool empty() const
		{
			return _l.empty();
		}

		//获取数据个数
		size_t size() const
		{
			return _l.size();
		}

	private:
		Container _l;
	};

	void Test1()
	{
		queue<int> q1;
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);

		cout << q1.size() << endl;

		while (!q1.empty())
		{
			cout << q1.front() << " ";
			q1.pop();
		}
	}
}

代码写到这里,stack和queue的模拟实现就基本完成了,但是在c++的STL中,实现stack和queue是通过使用deque这个容器来实现的

四.deque的简单介绍

在上面提到,stack和queue其实是通过使用deque这个容器来实现的,但是我们实现的时候为了便于大家理解,所以使用了vector和list来实现,那么现在,我们先来简单的了解一下的deque这个容器。

deque的基本介绍

首先,deque是一个顺序的存储结构,和vectorlist一样,都是顺着顺序来存储数据的,那么它们有什么区别吗,又或者说,为什么会出现deque这种数据结构?

在讲解之前,我们先来看看vector和list的缺点

vector和list的缺点

vector:头插头删效率低(因为要挪动数据)。

list:不能支持随机访问(在访问某个结点前,要先去遍历list去找到它)。

deque的出现

所以为了解决这两个容器的缺点,人们发明出了deque这种数据结构,也叫双端队列,这种队列头插头删尾插尾删时间效率为O(1),同时还能支持随机访问(但是它并不是真正意义上的随机访问)。

deque的结构

那么deque是如何实现的呢?deque通过数组指针指针数组来实现的。

通过这样的结构来实现,使deque的头插头删尾插尾删的效率非常的高,但是deque不适合遍历,因为deque是分开的连续空间,导致在其遍历时非常麻烦,具体的细节在这里不做讲解。 

deque实现stack和queue

所以为什么stack和queue要使用deque这个容器来实现,因为stack只用到了尾插尾删queue只用到了尾插和头删,正好都利用到了deque的优点,而deque的缺点没有涉及到,所以stack和queue的实现用到了deque这个容器。 

以下是使用deque来实现stack和queue。

namespace deque_stack
{
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		stack(){}
		//入栈
		void push(const T& val){_s.push_back(val);}
		//出栈
		void pop(){_s.pop_back();}
		//判断是否为空
		bool empty() const{return _s.empty();}
		//获取数据个数
		size_t size() const{return _s.size();}
		//取栈顶数据
		T& top(){return _s.back();}
		//const类型取栈顶数据
		const T& top() const{return _s.back();}
	private:
		Container _s;
	};

	void Test1()
	{
		stack<int> s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);
		while (!s1.empty())
		{
			cout << s1.top() << " ";
			s1.pop();
		}
	}
}
namespace deque_queue
{
	template<class T,class Container = deque<T>>
	class queue
	{
	public:
		queue(){}
		//入队列
		void push(const T& val){_l.push_back(val);}
		//出队列
		void pop(){_l.pop_front();}
		//取对头
		T& front(){return _l.front();}
		const T& front() const{return _l.front();}
		//取队尾
		T& back(){return _l.back();}
		const T& back() const{return _l.back();}
		//判断是否为空
		bool empty() const{return _l.empty();}
		//获取数据个数
		size_t size() const{return _l.size();}
	private:
		Container _l;
	};

	void Test1()
	{
		queue<int> q1;
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);
		while (!q1.empty())
		{
			cout << q1.front() << " ";
			q1.pop();
		}
	}
}

五.priority_queue的模拟实现

接下来,我们进入到priority_queue的模拟实现。

如果你查过queue的手册,会发现queue下面还有一个priority_queue的东西。

这个叫优先队列,简单的说也就是

对于堆的结构和各种操作,可以参考下面这篇博客

【C语言】堆的实现(建堆、堆的基本操作、堆排序、TOK问题)详解_堆 编程-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/EWIAW_ilove/article/details/135045451?spm=1001.2014.3001.5501

这篇博客详细的讲解了堆的实现,以及各种讲解,在这里,我们直接给出priority_queue的模拟实现并做简单的解释。

默认生成大堆

在实现之前,我们来看看priority_queue的成员函数。


默认情况下,我们生成的堆都是大堆,但是有时候也会用到小堆,大堆和小堆的区别就是,在代码中的比较反过来就行了,但是具体怎么实现呢?

这个时候要用到STL中的仿函数来实现。

在具体讲解建小堆前,我们先来看看大堆的实现。

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
namespace My_priority
{
	template<class T,class Container = vector<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}
		//从尾部插入数据
		void push(const T& val)
		{
			_pq.push_back(val);//先在vector的尾部插入新数据

			//后进行一个向上调整
			AdjustUp();
		}
		void AdjustUp()//向上调整算法
		{
			int child = size() - 1;
			int parent = (child - 1) / 2;

			while (parent >= 0 && _pq[child] > _pq[parent])
			{
				swap(_pq[child], _pq[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
		}
		//删除堆顶数据
		void pop()
		{
			assert(size());
			swap(_pq[0], _pq[size() - 1]);//交换堆顶和尾部数据
			_pq.pop_back();//删除尾部数据

			//向下调整
			AdjustDown();
		}
		void AdjustDown()//向下调整算法
		{
			int parent = 0;
			int child = parent * 2 + 1;//默认child给左孩子

			while (child < size())
			{
				if ((child + 1) < size() && (_pq[child] < _pq[child + 1]))//如果右孩子存在并且大于左孩子,则child给右孩子
				{
					child += 1;
				}

				if (_pq[child] > _pq[parent])
				{
					swap(_pq[child], _pq[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		//判断是否为空
		bool empty() const
		{
			return _pq.empty();
		}
		//求数据个数
		size_t size() const
		{
			return _pq.size();
		}
		//取堆顶数据
		const T& top() const
		{
			assert(size());
			return _pq[0];
		}
	private:
		Container _pq;
	};
}

仿函数 

在上面的实现中,默认情况下能生成只大堆,那么如果我们想要生成小堆,该怎么办呢?

在解释之前,我们先来看看仿函数这个东西。


仿函数如字面意思,是一个模仿的函数,即仿函数不是真正意义上的函数,其实它是一个由一个类通过重载()实现的。如下我实现了一个用于比较小于的仿函数。

 同样的,大于的比较我们也可以通过这种方式实现。

如下:        

 

 模拟实现大小堆

有了这两个仿函数,我们就可以通过模板的方式来控制生成的是大堆还是小堆了。 

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>

using namespace std;

namespace My_priority
{
	//用于比较小于的仿函数
	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}	 
	};

	//用于比较大于的仿函数
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	//模板参数有三个:类型参数、堆底层的容器、仿函数,仿函数默认给less
	template<class T,class Container = vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}

		//从尾部插入数据
		void push(const T& val)
		{
			_pq.push_back(val);//先在vector的尾部插入新数据

			//后进行一个向上调整
			AdjustUp();
		}

		void AdjustUp()//向上调整算法
		{
			Compare com;//创建仿函数对象
			int child = size() - 1;
			int parent = (child - 1) / 2;

			while (parent >= 0 && com(_pq[parent],_pq[child]))
			{
				swap(_pq[child], _pq[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
		}

		//删除堆顶数据
		void pop()
		{
			assert(size());
			swap(_pq[0], _pq[size() - 1]);//交换堆顶和尾部数据
			_pq.pop_back();//删除尾部数据

			//向下调整
			AdjustDown();
		}

		void AdjustDown()//向下调整算法
		{
			Compare com;//创建仿函数对象
			int parent = 0;
			int child = parent * 2 + 1;//默认child给左孩子

			while (child < size())
			{
				if ((child + 1) < size() && com(_pq[child] , _pq[child + 1]))//如果右孩子存在并且大于左孩子,则child给右孩子
				{
					child += 1;
				}

				if (com(_pq[parent] , _pq[child]))
				{
					swap(_pq[child], _pq[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		//判断是否为空
		bool empty() const
		{
			return _pq.empty();
		}

		//求数据个数
		int size() const
		{
			return _pq.size();
		}

		//取堆顶数据
		const T& top() const
		{
			assert(size());
			return _pq[0];
		}

	private:
		Container _pq;
	};
}

 六.所以源代码

stack.h

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

namespace My_Stack
{
	//使用模板来给一个vector参数
	template<class T,class Container=vector<T>>
	class stack
	{
	public:
		stack()
		{}

		//入栈
		void push(const T& val)
		{
			_s.push_back(val);
		}

		//出栈
		void pop()
		{
			_s.pop_back();
		}

		//判断是否为空
		bool empty() const
		{
			return _s.empty();
		}

		//获取数据个数
		size_t size() const
		{
			return _s.size();
		}

		//取栈顶数据
		T& top()
		{
			return _s.back();
		}

		//const类型取栈顶数据
		const T& top() const
		{
			return _s.back();
		}

	private:
		Container _s;
	};

	void Test1()
	{
		stack<int> s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);

		while (!s1.empty())
		{
			cout << s1.top() << " ";
			s1.pop();
		}
		cout << endl;
	}

	void Test2()
	{
		stack<int> s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);

		int a = s1.top();
		s1.pop();
		s1.push(10);
	
		cout << a << endl;
	}
}

namespace deque_stack
{
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		stack(){}
		//入栈
		void push(const T& val){_s.push_back(val);}
		//出栈
		void pop(){_s.pop_back();}
		//判断是否为空
		bool empty() const{return _s.empty();}
		//获取数据个数
		size_t size() const{return _s.size();}
		//取栈顶数据
		T& top(){return _s.back();}
		//const类型取栈顶数据
		const T& top() const{return _s.back();}
	private:
		Container _s;
	};

	void Test1()
	{
		stack<int> s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);
		while (!s1.empty())
		{
			cout << s1.top() << " ";
			s1.pop();
		}
		cout << endl;
	}
}

queue.h

#pragma once
#include<iostream>
#include<list>
#include<deque>
using namespace std;

namespace My_queue
{
	template<class T,class Container=list<T>>
	class queue
	{
	public:
		queue()
		{}

		//入队列
		void push(const T& val)
		{
			_l.push_back(val);
		}

		//出队列
		void pop()
		{
			_l.pop_front();
		}

		//取对头
		T& front()
		{
			return _l.front();
		}

		const T& front() const
		{
			return _l.front();
		}

		//取队尾
		T& back()
		{
			return _l.back();
		}

		const T& back() const
		{
			return _l.back();
		}

		//判断是否为空
		bool empty() const
		{
			return _l.empty();
		}

		//获取数据个数
		size_t size() const
		{
			return _l.size();
		}

	private:
		Container _l;
	};

	void Test1()
	{
		queue<int> q1;
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);

		cout << q1.size() << endl;

		while (!q1.empty())
		{
			cout << q1.front() << " ";
			q1.pop();
		}
		cout << endl;
	}
}

namespace deque_queue
{
	template<class T,class Container = deque<T>>
	class queue
	{
	public:
		queue(){}
		//入队列
		void push(const T& val){_l.push_back(val);}
		//出队列
		void pop(){_l.pop_front();}
		//取对头
		T& front(){return _l.front();}
		const T& front() const{return _l.front();}
		//取队尾
		T& back(){return _l.back();}
		const T& back() const{return _l.back();}
		//判断是否为空
		bool empty() const{return _l.empty();}
		//获取数据个数
		size_t size() const{return _l.size();}
	private:
		Container _l;
	};

	void Test1()
	{
		queue<int> q1;
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);
		while (!q1.empty())
		{
			cout << q1.front() << " ";
			q1.pop();
		}
		cout << endl;
	}
}

priority.h

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>

using namespace std;

namespace My_priority
{
	//用于比较小于的仿函数
	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}	 
	};

	//用于比较大于的仿函数
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	//模板参数有三个:类型参数、堆底层的容器、仿函数
	template<class T,class Container = vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}

		//从尾部插入数据
		void push(const T& val)
		{
			_pq.push_back(val);//先在vector的尾部插入新数据

			//后进行一个向上调整
			AdjustUp();
		}

		void AdjustUp()//向上调整算法
		{
			Compare com;
			int child = size() - 1;
			int parent = (child - 1) / 2;

			while (parent >= 0 && com(_pq[parent],_pq[child]))
			{
				swap(_pq[child], _pq[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
		}

		//删除堆顶数据
		void pop()
		{
			assert(size());
			swap(_pq[0], _pq[size() - 1]);//交换堆顶和尾部数据
			_pq.pop_back();//删除尾部数据

			//向下调整
			AdjustDown();
		}

		void AdjustDown()//向下调整算法
		{
			Compare com;
			int parent = 0;
			int child = parent * 2 + 1;//默认child给左孩子

			while (child < size())
			{
				if ((child + 1) < size() && com(_pq[child] , _pq[child + 1]))//如果右孩子存在并且大于左孩子,则child给右孩子
				{
					child += 1;
				}

				if (com(_pq[parent] , _pq[child]))
				{
					swap(_pq[child], _pq[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		//判断是否为空
		bool empty() const
		{
			return _pq.empty();
		}

		//求数据个数
		int size() const
		{
			return _pq.size();
		}

		//取堆顶数据
		const T& top() const
		{
			assert(size());
			return _pq[0];
		}

	private:
		Container _pq;
	};

	void Test1()
	{
		//priority_queue<int> pq;
		priority_queue<int,vector<int>,greater<int>> pq;
		pq.push(15);
		pq.push(10);
		pq.push(8);
		pq.push(20);

		while (!pq.empty())
		{
			cout << pq.top() << " ";
			pq.pop();
		}
		cout << endl;
	}
}

test.cpp

#include"stack.h"
#include"queue.h"
#include"priority.h"
int main()
{
	My_Stack::Test1();
	My_Stack::Test2();

	My_queue::Test1();

	deque_stack::Test1();
	deque_queue::Test1();

	My_priority::Test1();

	return 0;
}

 

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

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

相关文章

控制台调试 hover 后才出现的元素

调试 hover后才出现的元素 打开开发者工具&#xff0c;鼠标放在hover时才出现的元素上&#xff0c;然后点击右键&#xff1b; 不要选中任何选项&#xff0c;将鼠标移动到开发者工具的调试面板中&#xff1b; 按下N键&#xff0c;此时悬浮的元素不会消失&#xff0c;定位成功。…

Java注解介绍

注解&#xff08;Annotation&#xff09;是Java提供的一种元数据形式&#xff0c;它可以被添加到Java代码的各种元素上&#xff0c;如类、方法、变量、参数等。注解的作用主要包括&#xff1a; 1. 代码文档&#xff1a;注解可以用于生成文档&#xff0c;提高代码的可读性。 2.…

前端之深拷贝

前提&#xff1a; 就是在实际开发中&#xff0c;我有一个编辑的弹窗&#xff0c;可以查看和编辑&#xff0c;因为弹窗里面是一个步骤条&#xff0c;点击下一步就要向对应的接口发送请求&#xff0c;考虑到就比如我点击下一步&#xff0c;此次表箱信息其实不需要修改&#xff0…

大模型_DISC-MedLLM基于Baichuan-13B-Base医疗健康对话

文章目录 DISC-MedLLM介绍概述数据集部署推理流程 DISC-MedLLM 介绍 DISC-MedLLM 是一个专门针对医疗健康对话式场景而设计的医疗领域大模型&#xff0c;由复旦大学数据智能与社会计算实验室 (Fudan-DISC) 开发并开源。 该项目包含下列开源资源: DISC-Med-SFT 数据集 (不包…

智慧园区综合物业管理平台解决方案PPT(130页精品)

我们对智慧园区的理解 智慧园区&#xff0c;是通过信息技术和各类资源的整合&#xff0c;充分降低企业运营成本&#xff0c;提高工作效率&#xff0c;加强各类园区创新、服务和管理能力&#xff0c;为园区铸就一套超强的软实力。智慧园区的实现是多技术融合、多系统融合、多领域…

初识C语言——第十三天

关键字2&#xff1a; static 修饰局部变量&#xff0c;改变了局部变量的生命周期&#xff08;本质上是改变了变量的存储类型&#xff09; static修饰全局变量&#xff0c;使得这个全局变量只能在自己所在的源文件&#xff08;.c)内部可以使用&#xff0c;其他源文件不能使用 …

全方位了解 Meta Llama 3

本文将为您提供 Llama 3 的全面概览&#xff0c;从其架构、性能到未来的发展方向&#xff0c;让您一文了解这一革命性大语言模型的所有要点。 Meta Llama 发展历程 Llama 1 Llama 是由 Meta(FaceBook) AI 发布的一个开源项目&#xff0c;允许商用&#xff0c;影响力巨大。Lla…

基于springboot+vue+Mysql的在线动漫信息平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Qt | QLCDNumber 类(LCD 数字),LCD 表示液晶显示屏

01、上节回顾 Qt 基础教程合集02、QLCDNumber 1、QLCDNumber 类用于显示类似于 LCD 显示屏上的字符(见右图) ​ 2、QLCDNumber 类是 QFrame 类的直接子类,因此 QLCDNumber 以使用从 QFrame 类继承而来的边框效果 3、QLCDNumber 可显示的符号有:0,1,2,3,4,5,6,7,8,…

ue引擎游戏开发笔记(33)——武器与角色的匹配,将新武器装备到角色身上

1.需求分析&#xff1a; 武器能出现在世界中&#xff0c;完成了第一步&#xff0c;下一步需要角色和武器适配&#xff0c;即不论角色跑动&#xff0c;射击等&#xff0c;武器和角色都相匹配&#xff0c;将武器装备到角色身上。 2.操作实现&#xff1a; 1.首先先把角色原有的武…

【数据结构】--- 深入剖析二叉树(中篇)--- 认识堆堆排序Topk

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 数据结构之旅 文章目录 &#x1f3e0; 初识堆 &#x1f4d2; 堆的概念 &#x1f4d2; 堆的性质 &#x1f3e0; 向上调整算法 && 向下调整算…

vector的oj题

1.只出现1次的数字 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 方法&#xff1a;…

【Stable Diffusion】三句话,让Ai帮你画18万张图

本文介绍Stable Diffusion的快速上手&#xff0c;本地部署&#xff0c;以及更多有趣的玩法展示。 在 DALL-E 2 和 Imagen 之后&#xff0c;AI绘图领域又一个热乎的深度学习模型出炉——Stable Diffusion 。8月份发布的 Stable Diffusion 更加高效且轻量&#xff0c;可以在消费…

第六节课《Lagent AgentLego 智能体应用搭建》

PDF链接&#xff1a;https://pan.baidu.com/s/1JFtvBWgEGFWJq8pHafvIUg?pwd6666 提取码&#xff1a;6666 Lagent & AgentLego 智能体应用搭建_哔哩哔哩_bilibili https://github.com/InternLM/Tutorial/blob/camp2/agent/README.md InternStudio 一、为什么需要agent…

网页html版面分析-- BeauifulSoup(python 文档解析提取)

介绍 BeauifulSoup 是一个可以从HTML或XML 文件中提取数据的python库&#xff1b;它能通过转换器实现惯用的文档导航、查找、修改文档的方式。 BeauifulSoup是一个基于re开发的解析库&#xff0c;可以提供一些强大的解析功能&#xff1b;使用BeauifulSoup 能够提高提取数据的效…

R语言Rstudio突然无法启动?如何解决

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

由于找不到msvcp120.dll,无法继续执行代码的5种解决方法

在操作计算机的过程中&#xff0c;您或许会遇到这样一种情形&#xff1a;当试图启动某个软件应用程序时&#xff0c;系统突然弹出一个错误提示框&#xff0c;明确指出“找不到msvcp120.dll”&#xff0c;它会导致程序无法正常启动或运行。为了解决这个问题&#xff0c;我总结了…

作为全栈工程师,如何知道package.json中需要的依赖分别需要什么版本去哪里查询?

作为前端工程师&#xff0c;当你需要确定package.json中依赖的具体版本时&#xff0c;可以通过以下方法来查询&#xff1a; NPM 官网查询&#xff1a; 访问 npm 官网&#xff0c;在搜索框中输入你想查询的包名。在包的页面上&#xff0c;你可以看到所有发布过的版本号&#xff…

为什么很多人不推荐你用JWT?

为什么很多人不推荐你用JWT? 如果你经常看一些网上的带你做项目的教程&#xff0c;你就会发现 有很多的项目都用到了JWT。那么他到底安全吗&#xff1f;为什么那么多人不推荐你去使用。这个文章将会从全方面的带你了解JWT 以及他的优缺点。 什么是JWT? 这个是他的官网JSON…

解密Kol发文推广10个提升转化率的实用技巧-华媒舍

Key Opinion Leader&#xff08;Kol&#xff0c;关键意见领袖&#xff09;的发文推广成为了提升产品和服务转化率的重要手段。如何有效地利用Kol进行发文推广&#xff0c;并将潜在的观众转化为忠实的消费者&#xff0c;成为了营销从业者普遍关注的话题。本文将为您介绍10个实用…