c++中的链表list的模拟实现

news2024/12/24 20:23:05

拖更了半个月,我终于来填c++的坑啦。上次我们说的vetcor不知道小伙伴还记得多少呢?今天我们要讲list的模拟实现。

目录

  • 架构
    • 结点
    • list表的结构
  • 构造函数
  • 尾插push_back()
  • 尾删pop_back()
  • 计算个数:size()
  • 判断空empty()
  • ※迭代器问题
    • 普通迭代器
      • 迭代器主体
      • begin()和end()
      • operator*()
      • 前置++ operator++()
      • 后置++ operator++(int)
      • 前置-- operator--()
      • 后置-- operator--(int)
      • 重载不等于 operator!=(const Self& rhs)
      • 重载箭头 operator->()
    • const迭代器问题
      • 解决方法一:创建新的类。
      • 解决方法二:模板
  • 在某个位置前插入 insert()
  • 删除某位置的值 erase()
  • 清空链表 clear()
  • 拷贝构造
  • 析构函数
  • 问题
  • 实现整体代码

架构

我们之前也写过c语言版本的链表,当时很麻烦,还要传二级指针。但是现在不用了引用就能解决。我们先把大体的架构搭出来。

结点

在这里插入图片描述

list是一个个的结点指针链接,而且我们看手册的时候,会发现ist是一个双向带头的链表。所以我们先写节点的代码加粗样式

template<class T>
struct ListNode
{
//为什么要用struct?其实用class也ok,但是要整个public,因为节点我希望你可以随时申请,所以不能弄成私有。
	ListNode(const T& val=T())
		:_prev(nullptr)
		,_next(nullptr)
		,data(val)
	{

	}
	ListNode<T>* _prev;
	//为什么是ListNode<T>*的指针呢?因为我们将来要指向
	//ListNode<T>类型的节点。
	ListNode<T>* _next;
	T data;
};

list表的结构

我们也说了是双向带头链表,所以list一定要有头结点。

template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
	private:
		Node* _head;//头结点是一个节点指针类型。
		size_t _size;//因为库中有size这个接口,这里存一个会方便一点。
	};

构造函数

在我们之前实现过得带头双向链表,在刚开始的时候,就是对头结点进行处理,让头结点的前后指针指向自己。

	list()
		{
			_head = new Node;//给头一个空间
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}

尾插push_back()

尾插,我们很熟悉了。就是先new出一个节点的大小,然后把这个节点连接到链表的尾部。如图:
在这里插入图片描述

void push_back(const T& val)
{
	Node* node = new Node(val);//申请节点
	Node* tail = _head->_prev;//如何找到4?是_head->prev!保存起来方便我们使用。
	node->_prev = tail;//链接
	node->_next = _head;
	tail->_next = node;
	_head->_prev = node;
	_size++;//修改size
}

尾删pop_back()

尾删也是老朋友了。话不多说如图:
在这里插入图片描述
简单叙述过程:就是先找到尾巴,然后保存一起来(方便我们删除)。然后将指针链接到尾删的前一个值。

void pop_back()
{
	Node* tail = _head->_prev;//保存

	_head->_prev = tail->_prev;//更改指向
	tail->_prev->_next = _head;
	delete tail;//删除节点
	_size--;//别忘了修改个数
}

计算个数:size()

这个就很简单了,因为我们内置了个数,所以直接返回就好了。

size_t size()
{
	return _size;
}

判断空empty()

bool empty()
{
	return _size == 0;
}

※迭代器问题

链表的迭代器很麻烦,不像vector那样用下标访问就可以的我们先把迭代器的主体写出来,分析一下,我们都需要什么吧。

bit::list<int>::const it = lt2.begin();
while (it != lt2.end())
{
	std::cout << *it << " ";
	it++;
}

我们平时访问数据的时候就这些,例如:迭代器本身、*星号、begin()、end()、自增++。那怎么写呢?

普通迭代器

问题:迭代器本身怎么写,还用T* 吗?写在list中码?(一定要思考这个问题!)


答案:不不不,我问你的自增,你在增谁?增加的list还是迭代器?我们要写operator++(),你一定要明白是谁在++。如果你把迭代器写到list中,你operator++()自增的是谁?一定要把这个问题想清楚。

自增的迭代器本身。那么我们的自增就不能写到list中,也就表示迭代器不能写在list中。那么我们就需要另外写一个类封装了。OK!开写!

迭代器主体

我们要遍历这个list链表的时候,我们一定要有这个地方的节点指针,我们才能访问这里的数据对吧,所以里面的成员变量是Node* _node;

template<class T>
class List_Iterator
{
public:
	typedef ListNode<T> Node;
	Node* _node;

	List_Iterator(Node* node)
		:_node(node)
	{}
};

begin()和end()

begin()和end()多好写,begin()是返回数据的开始和end()返回头结点(有效数据的下一个)。

问题:你写在哪?list类?还是iterator类?
答:思考一下,你的数据在哪?你的数据在list 里面对吧,我要访问遍历数据,我去哪里拿数据?list中,那么你的begin()和end()一定不能写在iterator。begin()和end()应该写在list中。

template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
		iterator begin()
		{
			//这里存在单参数构造函数的隐式类型转换
			//按道理 return iterator(_head->next);
			//应该是这样的,匿名对象,但是单参数的构造函数可以进行隐式类型转换。
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
	private:
		Node* _head;//头结点是一个节点指针类型。
		size_t _size;//因为库中有size这个接口,这里存一个会方便一点。
	};

operator*()

因为我们遍历一般都是要打印出它的值,所星号也是必不可少的。星号表示解压用。那么我们只需要返回当前节点的值就可以了。

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

前置++ operator++()

自增很简单,我们只需要下一个节点的指针就好,那么我们怎么找到下一个节点呢?是不是当前节点的_next?那就更简单了。

List_Iterator<T>& operator++()
{
	//this->_node= this->_node->_next;
	_node= _node->_next;
	return *this;
}

后置++ operator++(int)

后置++,就是我们创建一个临时变量,然后返回临时变量,但是让实际已经指向下一个节点了。注:不能返回引用,因为是临时变量。

List_Iterator<T> operator++(int)
{
	List_Iterator<T> tmp(*this);
	_node = _node->_next;
	return tmp;
}

前置-- operator–()

为什么要重载自减呢?是因为有些地方我们还是有需要的,所以就直写了。

List_Iterator<T> & operator--()
{
	_node= _node->_prev;
	return *this;
}

后置-- operator–(int)

List_Iterator<T> operator--(int)
{
	Self tmp(*this);
	_node = _node->_prev;
	return tmp;
}

重载不等于 operator!=(const Self& rhs)

问题:在比较的时候,我们比的是节点指针还是数值呢?
答:节点指针!数值可能重复。

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

重载箭头 operator->()

为什么要重载operator->?我们一直用int内置类型来作为样例,这没有问题,但是我想问你,你的list只存int吗?没有自定义类型吗?如果我希望能存一个student类呢?可能你说(*it).成员变量也可以啊,但是你觉得方便吗?所以我们要重载->。

箭头的运算符重载:
在这里插入图片描述
从图上我们能发现,他隐藏了一个箭头,他应该是it.operator->()->_a1;但是事实上只有意见箭头,就表明了,他隐藏了一个箭头。

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

const迭代器问题

以上,我们用的都是普通的迭代器,那么const迭代器怎么写呢?你会说,我知道了!只要让他不改变不就好了?OK!说干就干。显示就是,依然改了,并没有用。
在这里插入图片描述
在这里插入图片描述

解决方法一:创建新的类。

问题就出在了你迭代器本身,你的返回值依然是普通的,我依然可以改。那你突然灵光一现,我在写一个const_ListIterator类就好了!好说干就干。你用了一下,真好用。但是你有没有发现代码很冗余?唯独只有operator*()和operator->()的返回值改变了,因为在拿取值的时候,我们要返回const类型的。

template<class T>
class List_ConstIterator
{
public:
	typedef ListNode<T> Node;
	typedef List_ConstIterator<T> Self;
	Node* _node;

	List_ConstIterator(Node* node)
		:_node(node)
	{}
	//前置++
	Self& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	//后置++
	Self operator++(int)
	{
		Self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	//前置--
	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	//后置--
	Self operator--(int)
	{
		Self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const Self& rhs)  
	{
		return rhs._node != _node;
	}
	bool operator==(const Self& rhs)
	{
		return _node == rhs._node;
	}

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

解决方法二:模板

我们上面也说只是两个成员函数的返回类型不同,那么我们在处理返回类型不同的时候怎么做的呢?对!就是模板!模板是不是可以传不同类型呀?那么我们就只需要改一下。
在这里插入图片描述
list类中:
在这里插入图片描述

template<class T ,class Ref ,class Ptr>
class List_Iterator
{
public:
	typedef ListNode<T> Node;
	typedef List_Iterator< T, Ref, Ptr> Self;
	Node* _node;

	List_Iterator(Node* node)
		:_node(node)
	{}
	//前置++
	Self& operator++()
	{
		_node= _node->_next;
		return *this;
	}
	//后置++
	Self operator++(int)
	{
		Self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	//前置--
	Self& operator--()
	{
		_node= _node->_prev;
		return *this;
	}
	//后置--
	Self operator--(int)
	{
		Self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const Self& rhs )
	{
		return rhs._node != _node;
	}
	bool operator==(const Self& rhs)
	{
		return _node == rhs._node;
	}
	Ptr operator->()
	{
		return &_node->data;
	}
	Ref operator* ()
	{
		return _node->data;
	}
};

在某个位置前插入 insert()

库中给我们了三种,我们只实现第一种。需要迭代器类型的pos和 T val。刚好迭代器问题我们刚刚解决。

在这里插入图片描述
连接方式,如图:
在这里插入图片描述

iterator insert(iterator pos, const T& val)
{
	//我写的不美观
	Node* node = new Node(val);
	Node* tmp=pos._node->_prev;
	tmp->_next = node;
	node->_prev = tmp;
	node->_next = pos._node;
	pos._node->_prev = node;
	_size++;
	return _head;
			// Node* node = new Node(val);
			// Node* prev=pos->prev;
			// node->prev=prev;
			// node->next=pos;
			// pos->prev=node;
			// prev->next=node;
			// _size++;
			// return _head
}

删除某位置的值 erase()

这里我们要注意,库中要求返回被删除值的下一个的迭代器。
在这里插入图片描述
在这里插入图片描述

		iterator erase(iterator pos)
		{
			//不美观版本
			assert(pos != end());//这里我担心有人有人恶意传迭代器,所以就判断了一下。
			Node* tmp = pos._node->_prev;
			tmp->_next = pos._node->_next;
			pos._node->_next->_prev = tmp;
			delete pos._node;
			_size--;
			return tmp->_next;

			/*美观版本
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return next;*/

		}

清空链表 clear()

注意,我们清空的链表,不需要清头指针,所以直接用迭代器,pop_back()就可以了。

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
	_size = 0;
}

拷贝构造

拷贝构造这里有几个问题。

问题1:直接push_back()可以吗?
答:不可以。你的头指针没有初始化,都是空,插入直接空指针访问。所以你需要初始化。
问题2:用我注释部分的迭代器可以吗?
答:不行,如果你传进来是一个const类型的迭代器呢?普通迭代器类型不匹配,除非你用auto。

//lt1(lt2)
list(const list<T>& lt)
{
	empty_init();//初始化头结点。
	for (auto& e : lt)
	{
		push_back(e);
	}
	iterator it = lt.begin();
	类型不明确,用范围for更好
	//auto it = lt.begin();//这样也可以
	//while (it != lt.end())
	//{
	//	push_back(*it);
	//	it++;
	//}
}

析构函数

释放空间,并且还有头结点!!!有些小伙伴以为只用释放各个数据的节点就可以了,但是头结点也是你开出来的空间啊,所以也要记得释放。

~list()
{
	clear();//复用
	delete _head;
}

问题

如果我什么都写了,只有拷贝构造没有写,下面图代码对吗?
在这里插入图片描述

答:不对,因为传值传参回去调用拷贝构造,没有写拷贝构造就是浅拷贝,当函数结束 临时对象会被析构。但是浅拷贝导致本对象被析构,当整体结束的时候会导致二次析构。

实现整体代码

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

namespace bit {
	template<class T>
	struct ListNode
	{
		ListNode(const T& val=T())
			:_prev(nullptr)
			,_next(nullptr)
			,data(val)
		{

		}
		ListNode<T>* _prev;//为什么是ListNode<T>*的指针呢?因为我们将来要指向
		//ListNode<T>类型的节点。
		ListNode<T>* _next;
		T data;
	};
	template<class T ,class Ref ,class Ptr>
	class List_Iterator
	{
	public:
		typedef ListNode<T> Node;
		typedef List_Iterator< T, Ref, Ptr> Self;
		Node* _node;

		List_Iterator(Node* node)
			:_node(node)
		{}
		//前置++
		Self& operator++()
		{
			_node= _node->_next;
			return *this;
		}
		//后置++
		Self operator++(int)
		{
			Self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		//前置--
		Self& operator--()
		{
			_node= _node->_prev;
			return *this;
		}
		//后置--
		Self operator--(int)
		{
			Self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const Self& rhs )
		{
			return rhs._node != _node;
		}
		Ref operator* ()
		{
			return _node->data;
		}
		bool operator==(const Self& rhs)
		{
			return _node == rhs._node;
		}
		Ptr operator->()
		{
			return &_node->data;
		}
	};
	//template<class T>
	//class List_ConstIterator
	//{
	//public:
	//	typedef ListNode<T> Node;
	//	typedef List_ConstIterator<T> Self;
	//	Node* _node;

	//	List_ConstIterator(Node* node)
	//		:_node(node)
	//	{}
	//	//前置++
	//	Self& operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	//后置++
	//	Self operator++(int)
	//	{
	//		Self tmp(*this);
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	//前置--
	//	Self& operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	//后置--
	//	Self operator--(int)
	//	{
	//		Self tmp(*this);
	//		_node = _node->_prev;
	//		return tmp;
	//	}
	//	bool operator!=(const Self& rhs)  
	//	{
	//		return rhs._node != _node;
	//	}
	//	const T& operator* ()
	//	{
	//		return _node->data;
	//	}
	//	bool operator==(const Self& rhs)
	//	{
	//		return _node == rhs._node;
	//	}
	//	const T* operator->()
	//	{
	//		return &_node->data;
	//	}
	//};
	template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
		typedef List_Iterator<T, T&, T*> iterator;
		typedef List_Iterator<T, const T&, const T*> const_iterator;
		//typedef List_Iterator<T> iterator;
		//typedef List_ConstIterator<T> const_iterator;
		void empty_init()
		{
			_head = new Node;//给头一个空间
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}
		list()
		{
			empty_init();
			//刚创建一个链表的时候,没有插入任何数据,我们需要让他指向自己
		}
		~list()
		{
			clear();
			delete _head;
		}
		//lt1(lt2)
		list(const list<T>& lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
			iterator it = lt.begin();
			类型不明确,用范围for更好
			//auto it = lt.begin();//这样也可以
			//while (it != lt.end())
			//{
			//	push_back(*it);
			//	it++;
			//}
		}
		void push_back(const T& val)
		{
			/*Node* node = new Node(val);
			Node* tail = _head->_prev;
			node->_prev = tail;
			node->_next = _head;
			tail->_next = node;
			_head->_prev = node;*/
			insert(end(), val);
		}
		void pop_back()
		{
			/*Node* tail = _head->_prev;

			_head->_prev = tail->_prev;
			tail->_prev->_next = _head;
			delete tail;*/
			erase(--end());
		}
		//当我们想使用insert的时候,发现需要迭代器那么我们先实现迭代器,怎么做呢?
		iterator begin()
		{
			//这里存在单参数构造函数的隐式类型转换
			//
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
		const_iterator begin()const
		{
			//这里存在单参数构造函数的隐式类型转换
			return _head->_next;
		}
		const_iterator end()const
		{
			return _head;
		}
		//目前测试的迭代器没有大碍,那么就写insert
		iterator insert(iterator pos, const T& val)
		{
			//我写的不美观
			Node* node = new Node(val);
			Node* tmp=pos._node->_prev;
			tmp->_next = node;
			node->_prev = tmp;
			node->_next = pos._node;
			pos._node->_prev = node;
			_size++;
			return _head;
			// Node* node = new Node(val);
			// Node* prev=pos->prev;
			// node->prev=prev;
			// node->next=pos;
			// pos->prev=node;
			// prev->next=node;
			// _size++;
			// return _head
		}
		iterator erase(iterator pos)
		{
			//我写的不美观
			assert(pos != end());
			Node* tmp = pos._node->_prev;
			tmp->_next = pos._node->_next;
			pos._node->_next->_prev = tmp;
			delete pos._node;
			_size--;
			return tmp->_next;

			//老师写的
			/*Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return next;*/

		}
		size_t size()
		{
			return _size;
		}
		bool empty()
		{
			return _size == 0;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
			_size = 0;
		}
	private:
		Node* _head;
		size_t _size;
	};
}

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

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

相关文章

16. Spring 事务和事务传播机制

源码位置&#xff1a;transaction 1. 事务回顾 在数据库阶段&#xff0c;想必大家都已经学习过事务了。当多个操作要么一起成功&#xff0c;要么一起失败的时候就需要将多个操作放在同一个事务中。 举个例子&#xff1a;比如用户A给用户B转账100元的业务&#xff0c;需要把用…

英文文档阅读学习atoi

文档链接&#xff1a;atoi - C Reference (cplusplus.com) 如果可以看的懂英文的可以直接看这个图&#xff0c;看不明白也没关系&#xff0c;可以看一下下面的翻译&#xff1a; 这是一些c语言的相关单词意思&#xff1a; C-string——使用空字符 0 结尾的一维字符数组 as in i…

(学习日记)2024.04.28:UCOSIII第五十二节:User文件夹函数概览(uC-LIB文件夹)第二部分

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

重学java 24.面向对象 多态

下雨了&#xff0c;跑不跑衣服都会被淋湿&#xff0c;还不如慢慢地走&#xff0c;结局已定 —— 24.4.25 多态 1.面向对象三大特性&#xff1a;封装 继承 多态 2.怎么学&#xff1a; a、不要从字面意思上理解多态这两个字&#xff0c;要从使用形式上掌握 b、要知…

【Redis 开发】Redis哨兵

哨兵 作用和原理服务状态监控选举新的master 搭建哨兵集群RedisTemplate的哨兵模式 作用和原理 Redis提供了哨兵机制来实现主从集群中的自动故障恢复&#xff1a; 哨兵也是一个集群 监控&#xff1a;会不断检查master和slave是否按预期工作自动故障恢复&#xff1a;如果mast…

开发工具-pycharm的代码自动部署服务器以及服务端python配置、项目开发环境一键启动服务

文章目录 一、pycharm的ssh配置1.本地生成ssh密钥2.密钥配置到远端服务器(1-1) 有权限ssh访问到服务器(1-2) 无权限ssh访问到服务器(1-3) 没有办法通过以上形式上传到服务器(2) 配置到authorized_keys自动访问 3.pycharm中配置ssh(1) 选择File中的settings(1) 选择Tools中的SSH…

Github创建远程仓库(项目)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

StarBright染料--星光高亮抗体 助力多色方案

星光染料是一种独特的荧光纳米粒子&#xff0c;将它与 Bio-Rad 高度验证的抗体结合&#xff0c;专为流式细胞术开发&#xff0c;为您提供卓越染色效果。星光染料使稀有群体和低密度抗原易于分辨&#xff0c;同时保持适用于任何多色实验的灵活性。 本篇将帮助你了解高亮星光染料…

springboot权限验证学习-下

上篇讲了rbac对于菜单的权限&#xff0c;下面准备完成按钮权限以及行数据和列数据权限 权限控制(按钮权限) 权限控制 操作权限就是将操作视为资源&#xff0c;比如删除操作&#xff0c;有些人可以有些人不行。于后端来说&#xff0c;操作就是一个接口。于前端来说&#xff0…

vue2使用change事件监听不了回车事件的问题

在 vue2 项目中使用 el-input 的 change 监听&#xff0c;数据不发生变化时&#xff0c;回车事件和失去焦点不生效 输入框会一直显示 只有数据发生变化时才生效 <el-input v-model"editedText" change"endEditing" ref"input"></el-inp…

校园广播系统:智能化管理提升校园安全与效率

在现代教育环境中&#xff0c;校园广播系统不再仅仅是一个播放音乐和通知的工具&#xff0c;它已经成为学校基础设施中不可或缺的一部分。根据《义务教育阶段学校信息化设备配备标准》的第8条&#xff0c;校园广播系统在学校范围内的日常运用极为广泛&#xff0c;涵盖了升旗仪式…

基于 SpringCloud 的在线交易平台乐优商城的设计与实现(四)

第 4 章 数据库设计 4.1 数据库设计原则 4.2.数据库概念结构设计 4.3 数据库表设计 4.4.本章小结 前面内容请移步 基于 SpringCloud 的在线交易平台乐优商城的设计与实现&#xff08;三&#xff09; 相关免费源码资源 乐优商城 第 4 章 数据库设计 4.1 数据库设计原…

怎样把音频压缩?3种简单的音频压缩方法分享

怎样把音频压缩&#xff1f;在数字化时代&#xff0c;音频文件占据了大量的存储空间&#xff0c;因此音频压缩成为了许多人的需求。通过音频压缩&#xff0c;我们不仅可以减小文件大小&#xff0c;方便存储和传输&#xff0c;还可以节省设备空间&#xff0c;提升处理效率。因此…

人工智能|推荐系统——推荐大模型最新进展

近年来,大语言模型的兴起为推荐系统的发展带来了新的机遇。这些模型以其强大的自然语言处理能力和丰富的知识表示,为理解和生成复杂的用户-物品交互提供了新的视角。本篇文章介绍了当前利用大型语言模型进行推荐系统研究的几个关键方向,包括嵌入空间的解释性、个性化推荐的知…

电脑黑屏问题的4种解决方法,两分钟轻松掌握

电脑黑屏是一种让人不安的问题&#xff0c;这个问题可能是由多种原因引起的。在这个数字化的时代&#xff0c;电脑已经成为我们工作和娱乐中不可或缺的一部分。当电脑突然陷入黑屏状态&#xff0c;用户通常会感到困扰和焦虑。本文将介绍一些常见的电脑黑屏问题解决方法&#xf…

微服务之并行与分布式计算

一、概述 1.1集中式系统vs分布式系统 集中式系统 集中式系统完全依赖于一台大型的中心计算机的处理能力&#xff0c;这台中心计算机称为主机&#xff08;Host 或 mainframe &#xff09;&#xff0c;与中心计算机相连的终端设备具有各不相同非常低的计算能力。实际上大多数终…

注意力机制、self attention、target attention、双层attention

关于注意力机制要解决2个问题&#xff0c;一是怎么做在哪个层面上做&#xff0c;二是注意力系数如何得到&#xff0c;由谁产出。注意力机制应用广泛的本质原因是求和的普遍存在&#xff0c;只要是有求和的地方加权和就有用武之地。DIN/DIEN把注意力机制用在用户行为序列建模是为…

校园综合服务平台

码功能强大&#xff0c;ui 精美&#xff0c; 功能包含但不限于校园跑腿&#xff0c;外卖&#xff0c;组局&#xff0c;圈子&#xff0c;商城&#xff0c;抽奖&#xff0c;投票&#xff0c;团购&#xff0c;二手市场&#xff0c;签到&#xff0c;积分商城&#xff0c;一元购等&a…

Linux驱动开发:深入理解I2C时序

目录标题 I2C简介I2C时序关键点Linux内核中的I2C时序处理I2C适配器I2C算法I2C核心 代码示例&#xff1a;I2C设备访问调试I2C时序问题 在Linux驱动开发中&#xff0c;理解和正确处理I2C时序对于确保I2C设备正常工作至关重要。本文将详细介绍I2C通信协议的时序特征&#xff0c;并…

企业的核心竞争力,是有效制作电子说明书

在这个信息化的时代&#xff0c;各种产品和服务层出不穷&#xff0c;数不胜数。要想在众多竞争对手中脱颖而出&#xff0c;除了产品质量之外&#xff0c;还有很多因素。比如营销手段、价格优势或者是品牌效应。但今天我要说的&#xff0c;是一个可能容易被人忽视的一个关键点—…