C++ STL:vector的使用方法及模拟实现

目录

一. vector概述

二. vector接口函数的使用方法和模拟实现

2.1 vector类模板的成员变量

2.2 构造函数的使用和模拟实现

2.2.1 构造函数的使用方法

2.2.2 构造函数的模拟实现

2.3 析构函数的模拟实现

2.4 赋值运算符重载函数的使用和模拟实现

2.4.1 函数的使用

2.4.2 函数的模拟实现

2.5 vector类对象容量相关函数的使用和模拟实现

2.5.1 函数的使用

2.5.2 函数的模拟实现

2.6 迭代器相关函数的使用和模拟实现

2.6.1 函数的使用

2.6.2 函数的模拟实现

2.7 数据插入函数的使用和模拟实现

2.7.1 函数的使用

2.7.2 函数的模拟实现

2.8 数据删除函数的使用及模拟实现

2.8.1 函数的使用

2.8.2 函数的模拟实现

附录:vector类模拟实现完整版


一. vector概述

vector是可以动态改变容量大小的顺序存储容器,其本质为模板类,用于在一块连续的空间存储特定类型的数据。

简单来说,可以将vector理解为顺序表或数组,可以通过特定的函数,对一个vector类对象完成增删查改等操作,可以通过下标访问特定位置处的元素。

图1.1 vector的结构示意图

二. vector接口函数的使用方法和模拟实现

2.1 vector类模板的成员变量

我们假设vector的模板参数类型为template <class T>,并且定义了一个普通对象迭代器iterator和const属性对象迭代器const_iterator:

  • typedef T* iterator
  • typedef const T* const_iterator

vector类有三个成员变量,分别为_start、_finish、_endOfStorage,它们的类型都为iterator

  1. _start:为指向第一个元素的位置的指针。
  2. _finish:指向最后一个有效数据后面那个位置处的指针。
  3. _endOfStorage:指向可用空间末尾位置的指针。
图2.1 vector类成员变量意义图解

2.2 构造函数的使用和模拟实现

2.2.1 构造函数的使用方法

C++ STL标准中给出了四种常用的方法构造vector类对象:

  1. 默认方法构造:explicit vector() -- 构造出的对象不存储任何数据。
  2. 给定n个初始化值来构造:explicit vector(size_t n, const T& val = T()) -- 创建的类含有n个数据,均为val。
  3. 给出一段迭代器区间,以迭代器区间中的数据作为初始化值进行初始化 --vector(InputIterator first, InputIterator)
  4. 拷贝构造:vector(const vector& v)
int main()
{
	vector<int> v1;  //构造空类
	vector<int> v2(3, 5);   //构造类,用3个5作为初始化值
	vector<int> v3(v2.begin(), v2.end());   //用指向v2起始位置和终止位置的迭代的区间的数据作为初始化值
	vector<int> v4(v3);   //拷贝构造

	//输出v1到v4类中的值
	for (auto e : v1)  //输出空
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto e : v2)  //5 5 5
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto e : v3)  //5 5 5
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto e : v4)  //5 5 5
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

2.2.2 构造函数的模拟实现

这里我对默认构造、迭代器区间构造以及拷贝构造进行模拟实现。

  • 默认构造函数:只需要将vector的三个成员变量_start、_finish、_endOfStorage均初始化为nullptr即可。
  • 迭代器区间构造:检查迭代器的有效性,调用push_back(尾插函数),将迭代器区间中的数据依次插入到vector对象中即可。
  • 拷贝构造:可以通过本本分分进行深拷贝来构造,也可以通过创建一个临时对象来构造。
        vector()  //默认构造函数
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{ }

		//以迭代器区间数据为初始化值的构造函数
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endOfStorage, v._endOfStorage);
		}

		//拷贝构造函数
		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());  //构建与v相同的临时对象
			swap(tmp);  //交换this和tmp的内容,这样tmp被析构时,就会销毁原来_start指向的空间
		}

2.3 析构函数的模拟实现

析构函数在vector对象的生命周期结束时由编译器自动调用,并不需要用户来显示地进行调用,因此不需要探究其使用方法。

模拟实现的~vector()函数,只需要释放_start指向的内存空间,然后将三个成员变量都置为nullptr即可。

		~vector()
		{
			delete[] _start;
			_start = _finish = _endOfStorage = nullptr;
		}

2.4 赋值运算符重载函数的使用和模拟实现

2.4.1 函数的使用

我们只需要获取两个已经存在的类对象,将其中一个的作为右值赋给另外一个即可,赋值之后,两个类对象的数据、容量均相同。

int main()
{
	vector<int> v1(3, 6);
	vector<int> v2(5, 1);

	v2 = v1;
		
	for (auto e : v2)  //6 6 6
	{
		cout << e << " ";
	}
	cout << endl;
}

2.4.2 函数的模拟实现

通过创建一个临时的类对象tmp,来实现对vector对象之间的赋值,其中vector对象中存储的内容和右值相同。

		vector& operator=(const vector<T>& v)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
			return *this;
		}

2.5 vector类对象容量相关函数的使用和模拟实现

2.5.1 函数的使用

  • size:获取vector对象中存储的有效数据个数。
  • capacity:获取vector对象的容量(最多存储多少个数据)。
  • reserve:将vector对象的容量扩大到n,如果对象当前的容量小于等于n,则不执行任何操作。
  • resize:删除数据,或将vector对象的容量扩大到n并进行初始化。
int main()
{
	vector<int> v1(5, 3);
	cout << "size = " << v1.size() << endl;   //获取有效数据个数
	cout << "capacity = " << v1.capacity() << endl;   //获取容量

	v1.reserve(7);  //扩容
	cout << "capacity = " << v1.capacity() << endl;   //获取容量

	v1.resize(10, 5);  //扩容并初始化
	cout << "capacity = " << v1.capacity() << endl;   //获取容量
	for (auto e : v1)  
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

2.5.2 函数的模拟实现

  • size:通过指针减法来实现,即:_finish - _start,获取对象中的数据个数。
  • capacity:与size一样,通过指针减法来实现,_endOfStorage - _start。
  • reserve:检查待扩容容量n是否大于原容量capacity,如果小于,就不执行任何操作,如果大于,就开辟一块新的内存空间,并将原来_start指向的内存空间的内容拷贝到新的内存空间中去,释放掉原来_start指向的内存空间,对_start、_finish、_endOfStorage进行更新。
  • resize:函数参数为size_t n,如果n < size(),那么就将数据删除到n个,如果大于capacity,就扩容,将从_finish到_endOfStroage的内存空间的内容都初始化为指定值。
		size_t capacity() const //获取对象容量函数
		{
			return _endOfStorage - _start;
		}

		size_t size() const //数据个数获取函数
		{
			return _finish - _start;
		}

		void reserve(size_t n)  //扩容函数
		{
			if (n > capacity())
			{
				T* tmp = new T[n];   //新的存储数据的空间
				size_t sz = size();  //获取数据个数

				//将原来的内容拷贝到新的空间中去
				for (size_t i = 0; i < sz; ++i)
				{
					tmp[i] = _start[i];
				}
				
				delete[] _start;  //释放原空间
				_start = tmp;  //_start指向新空间

				//更新容量(_finish、_endOfStroage)
				_finish = _start + sz;
				_endOfStorage = _start + n;
			}
		}

		//扩容 + 初始化 或 删除数据
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}

				while (_finish != _endOfStorage)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

2.6 迭代器相关函数的使用和模拟实现

2.6.1 函数的使用

  • begin:返回vector对象中首个元素的地址。
  • end:返回vector对象中最后一个元素后面那个位置的地址。

迭代器主要用于遍历数据,以及作为find(数据查找函数)的返回值,以及插入数据函数insert和删除数据函数erase的位置参数。

图2.1 begin和end返回值指向的位置
int main()
{
	vector<int> v1(5, 2);   //v1中存储5个2
	const vector<int> v2(5, 5);   //v2中存储5个5

	vector<int>::iterator it1 = v1.begin();
	while (it1 != v1.end())  //调用普通对象迭代器,将v1的每个成员+1并打印
	{
		(*it1)++;
		cout << *it1 << " ";  //3 3 3 3 3
		++it1;
	}
	cout << endl;

	vector<int>::const_iterator it2 = v2.begin();   
	while (it2 != v2.end())  //调用const对象迭代器,打印v2的每个数据
	{
		//(*it2)++;   //const对象成员变量的值不能被改变,报错
		cout << *it2 << " ";  //5 5 5 5 5
		++it2;
	}
	cout << endl;

	return 0;
}

2.6.2 函数的模拟实现

begin函数直接返回_start,end函数直接返回_finsih即可。注意begin和end都要写成重载的形式,以适用于普通对象和const属性对象。

		iterator begin()
		{
			return _start;
		}

		const_iterator begin() const
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator end() const
		{
			return _finish;
		}

2.7 数据插入函数的使用和模拟实现

2.7.1 函数的使用

  • push_back:在vector对象尾部插入一个数据。
  • insert:在pos位置处插入数据。

其中insert函数返回指向插入数据位置处的指针,这样做是为了防止迭代器失效。insert函数的函数原型为iterator insert(iterator pos, const T& val),即:在pos位置处插入val值。

迭代器失效发生在原vector对象容量不足无法容纳新数据时,此时函数会先执行扩容操作,然后再插入数据。而扩容实际上是新开辟了一块内存空间,将原来vector中的内容复制到了新的空间,而我们传给函数的pos指向的是原来那块空间的某个位置。综上,如果扩容,pos就不再指向vector对象的空间,从而引发迭代器失效。

如图2.2所示,在capacity = 4的vector对象的pos位置(第二个数据位置)插入一个新数据5,而原来的对象中已经存放了4个数据,那么函数会新开一块空间,然后指向插入。但此时pos依然指向原来vector的内存空间,但这块空间已经换给了操作系统。

图2.2  迭代器失效问题及insert返回值图解
int main()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);  //将1、2、3依次尾插到v1尾部

	for (auto e : v1)  //打印v1中的每个元素
	{
		cout << e << " ";  //1 2 3
	}
	cout << endl;

	vector<int>::iterator pos = find(v1.begin(), v1.end(), 2);  //找v1中数据2的位置
	pos = v1.insert(pos, 10);  
	pos = v1.insert(pos, 20);  
	pos = v1.insert(pos, 30);  //在pos位置依次插入10、20、30

	for (auto e : v1)  //打印v1中的每个元素
	{
		cout << e << " ";  //1 30 20 10 2 3
	}
	cout << endl;

	return 0;
}

2.7.2 函数的模拟实现

  • push_back函数:首先检查是否需要扩容,需要就调用reserve函数扩容。然后在_finish指向的位置插入特定值,更新_finish的指向。
  • insert函数的实现:检查是否需要扩容,如果需要,就调用reserve函数扩容,并且在扩容的同时更改形参pos的指向。然后将pos位置往后的数据全部后移,在pos位置插入数据,更新_finish的指向,返回pos。
		void push_back(const T& val)  //尾插数据函数
		{
			if (_finish == _endOfStorage)
			{
				//如果没有剩余容量,就扩容
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}

			*_finish = val;
			++_finish;
		}

		//在pos位置插入数据
		iterator insert(iterator pos, const T& val)
		{
			if (_finish == _endOfStorage)
			{
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + len;
			}

			//pos开始的数据后移一个单位
			iterator end = _finish;
			while (end > pos)
			{
				*end = *(end - 1);
				--end;
			}
	        //在pos位置插入数据
			*pos = val;
			++_finish;

			return pos;
		}

2.8 数据删除函数的使用及模拟实现

2.8.1 函数的使用

  • erase函数:删除pos位置处的元素,返回插入指向插入数据位置处的指针pos。

erase函数与insert函数一样,会存在迭代器失效问题。因为删除数据要将pos后面的数据向前移动,则会覆盖掉原来pos位置,如果想删除pos位置的数据之后,再删除pos位置后面的那个数据,如果指型++pos语句,就会存在迭代器失效。

下面的代码希望完成的操作是在vector<int>对象中删除所有的偶数数据,通过测试,我们发现:

  • 如果vector中的数据为1 2 3 4 5,正常删除所有偶数。
  • 如果vector中的数据为1 2 3 4,则程序会崩溃。
  • 如果vector中的数据为1 2 4 5,那么会有偶数没有被删除。
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		++it;
	}
图2.3 执行erase操作时迭代器失效问题图解

 

下面这段代码,通过接收erase的返回值并赋给it,就不会存在迭代器失效的问题。

	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			it = v.erase(it);
		}
		else
		{
			++it;
		}
	}

2.8.2 函数的模拟实现

检查pos是否越界,然后将pos后面的数据全部向前移动一个单位,再更新_finish,返回pos即可。

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			//将pos后面的数据向前移动一个单位
			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *begin;
				++begin;
			}

			--_finish;
			return pos;
		}

附录:vector类模拟实现完整版

namespace zhang
{
	template <class T>
	class vector
	{
	public:
		//迭代器定义
		typedef T* iterator;   //对于普通对象的迭代器
		typedef const T* const_iterator;  //对于const对象的迭代器

		//-------------------------------------------------------------------------
		//构造、赋值和析构相关函数

		vector()  //默认构造函数
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{ }

		//以迭代器区间数据为初始化值的构造函数
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endOfStorage, v._endOfStorage);
		}

		//拷贝构造函数
		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());  //构建与v相同的临时对象
			swap(tmp);  //交换this和tmp的内容,这样tmp被析构时,就会销毁原来_start指向的空间
		}

		//析构函数
		~vector()
		{
			delete[] _start;
			_start = _finish = _endOfStorage = nullptr;
		}

		//赋值函数
		vector& operator=(const vector<T>& v)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
			return *this;
		}

		//--------------------------------------------------------------------------
		//对象成员及容量相关函数

		size_t capacity() const //获取对象容量函数
		{
			return _endOfStorage - _start;
		}

		size_t size() const //数据个数获取函数
		{
			return _finish - _start;
		}

		void reserve(size_t n)  //扩容函数
		{
			if (n > capacity())
			{
				T* tmp = new T[n];   //新的存储数据的空间
				size_t sz = size();  //获取数据个数

				//将原来的内容拷贝到新的空间中去
				for (size_t i = 0; i < sz; ++i)
				{
					tmp[i] = _start[i];
				}
				
				delete[] _start;  //释放原空间
				_start = tmp;  //_start指向新空间

				//更新容量(_finish、_endOfStroage)
				_finish = _start + sz;
				_endOfStorage = _start + n;
			}
		}

		//扩容 + 初始化 或 删除数据
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}

				while (_finish != _endOfStorage)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		//成员访问操作符重载函数
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos) const
		{
			assert(pos < size());
			return _start[pos];
		}

		//--------------------------------------------------------------------------
		//增删查改相关函数

		void push_back(const T& val)  //尾插数据函数
		{
			if (_finish == _endOfStorage)
			{
				//如果没有剩余容量,就扩容
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}

			*_finish = val;
			++_finish;
		}

		//在pos位置插入数据
		iterator insert(iterator pos, const T& val)
		{
			if (_finish == _endOfStorage)
			{
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + len;
			}

			//pos开始的数据后移一个单位
			iterator end = _finish;
			while (end > pos)
			{
				*end = *(end - 1);
				--end;
			}
	        //在pos位置插入数据
			*pos = val;
			++_finish;

			return pos;
		}

		//删除pos位置处的数据
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			//将pos后面的数据向前移动一个单位
			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *begin;
				++begin;
			}

			--_finish;
			return pos;
		}

		//---------------------------------------------------------------------------
		//迭代器相关函数

		iterator begin()
		{
			return _start;
		}

		const_iterator begin() const
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator end() const
		{
			return _finish;
		}

	private:
		iterator _start;   //指向数组起始位置的指针
		iterator _finish;  //指向数组中最后一个元素后面那个位置的指针
		iterator _endOfStorage;   //指向存储空间末尾后面那个位置的指针
	};
}

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

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

相关文章

MybatisPlus------MyBatisX插件:快速生成代码以及快速生成CRUD(十二)

MybatisPlus------MyBatisX插件&#xff08;十二&#xff09; MyBatisX插件是IDEA插件&#xff0c;如果想要使用它&#xff0c;那么首先需要在IDEA中进行安装。 安装插件 搜索"MyBatisX"&#xff0c;点击Install&#xff0c;之后重启IDEA即可。 插件基本用途&…

蓝桥杯嵌入式第四课--定时器

前言蓝桥杯对于定时器这部分的考察主要集中在定时器中断、PWM输出以及输入捕获三个方面&#xff0c;本节课着眼于应用&#xff0c;介绍一下定时器的使用。定时器中断一、基础概念对没接触过定时器的新手来说&#xff0c;如果想要快速上手定时器的使用&#xff0c;首先要先对定时…

Python每日一练(20230318)

目录 1. 排序链表 ★★ 2. 最长连续序列 ★★ 3. 扰乱字符串 ★★★ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 排序链表 给你链表的头结点 head &#xff0c;请将其按 升序 …

卷积神经网络CNN识别MNIST数据集

这次我们将建立一个卷积神经网络&#xff0c;它可以把MNIST手写字符的识别准确率提升到99%&#xff0c;读者可能需要一些卷积神经网络的基础知识才能更好的理解本节的内容。 程序的开头是导入TensorFlow&#xff1a; import tensorflow as tf from tensorflow.examples.tutor…

C语言老题新解16-20 用命令行打印一些图案

文章目录11 打印字母C12 输出国际象棋棋盘。13 打印楼梯&#xff0c;同时在楼梯上方打印两个笑脸。14 输出9*9 口诀。15 有一道题要输出一个图形&#xff0c;然后Very Beautiful。11 打印字母C 11 用*号输出字母C的图案。 讲道理这绝对不该是个新人能整出来的活儿&#xff0c…

TCP/IP协议栈之数据包如何穿越各层协议(绝对干货)

所有互联网服务&#xff0c;均依赖于TCP/IP协议栈。懂得数据是如何在协议栈传输的&#xff0c;将会帮助你提升互联网程序的性能和解决TCP相关问题的能力。 我们讲述在Linux场景下数据包是如何在协议层传输的。 1、发送数据 应用层发送数据的过程大致如下&#xff1a; 我们把…

蓝桥杯嵌入式第五课--输入捕获

前言输入捕获的考题十分明确&#xff0c;就是测量输入脉冲波形的占空比和频率&#xff0c;对我们的板子而言&#xff0c;就是检测板载的两个信号发生器产生的信号&#xff1a;具体来说就是使用PA15和PB4来做输入捕获。输入捕获原理简介输入捕获能够对输入信号的上升沿和下降沿进…

WorkTool企微机器人接入智能问答

一、前言 最新版的企微机器人已经集成 Chat &#xff0c;无需开发可快速搭建智能对话机器人。 从官方介绍看目前集成版本使用模型为 3.5-turbo。 二、入门 创建 WorkTool 机器人 你可以通过这篇快速入门教程&#xff0c;来快速配置一个自己的企微机器人。 实现的流程如图&…

Windows与Linux端口占用、查看的方法总结

Windows与Linux端口占用、查看的方法总结 文章目录Windows与Linux端口占用、查看的方法总结一、Windows1.1Windows查看所有的端口1.2查询指定的端口占用1.3查询PID对应的进程1.4查杀死/结束/终止进程二、Linux2.1lsof命令2.2netstat命令一、Windows 1.1Windows查看所有的端口 …

基于GPT-4的免费代码生成工具

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

SpringCloud五大核心组件

Consul 等&#xff0c;提供了搭建分布式系统及微服务常用的工具&#xff0c;如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等&#xff0c;满足了构建微服务所需的所有解决方案。 服务发现——Netflix Eureka …

7个最受欢迎的Python库,大大提高开发效率

当第三方库可以帮我们完成需求时&#xff0c;就不要重复造轮子了 整理了GitHub上7个最受好评的Python库&#xff0c;将在你的开发之旅中提供帮助 PySnooper 很多时候时间都花在了Debug上&#xff0c;大多数人呢会在出错位置的附近使用print&#xff0c;打印某些变量的值 这个…

算法竞赛必考算法——动态规划(01背包和完全背包)

动态规划(一) 目录动态规划(一)1.01背包问题1.1题目介绍1.2思路一介绍(二维数组)1.3思路二介绍(一维数组) 空间优化1.4思路三介绍(输入数据优化)2.完全背包问题2.1题目描述&#xff1a;2.2思路一(朴素算法)2.3思路二(将k优化处理掉)2.4思路三(优化j的初始条件)总结1.01背包问题…

Spring Cloud Alibaba全家桶(四)——微服务调用组件Feign

前言 本文小新为大家带来 微服务调用组件Feign 的相关知识&#xff0c;具体内容包含什么是Feign&#xff0c;Spring Cloud Alibaba快速整合OpenFeign&#xff0c;Spring Cloud Feign的自定义配置及使用&#xff08;包括&#xff1a;日志配置、契约配置、自定义拦截器实现认证逻…

Autosar-ComM浅谈

文章目录 一、ComM概述二、和其他模块的依赖关系三、ComM通道状态机ComM模式与通讯能力关系表四、ComM中的PNC一、ComM概述 ComM全称是Communication Manager,顾名思义就是通信的管理,是BSW(基本软件)服务层的一个组件。 ComM的作用: 为用户简化Communication Stack的使用…

中断控制器

在Linux内核中&#xff0c;各个设备驱动可以简单地调用request_irq&#xff08;&#xff09;、enable_irq&#xff08;&#xff09;、disable_irq&#xff08;&#xff09;、 local_irq_disable&#xff08;&#xff09;、local_irq_enable&#xff08;&#xff09;等通用API来…

STM32----MPU6050

前言&#xff1a;最近几个月没有写文章了&#xff0c;因为这学期的事情真的有点多&#xff0c;但是想了想&#xff0c;文章还是要更新&#xff0c;总结自己学习的知识&#xff0c;真的很重要&#xff01;&#xff01;&#xff01; 废话不多说&#xff0c;正文开始&#xff1a;…

【vue.js】在网页中实现一个金属抛光质感的按钮

文章目录前言效果电脑效果手机效果说明完整代码index.html前言 诶&#xff1f;这有一个按钮(&#xff5e;&#xffe3;▽&#xffe3;)&#xff5e;&#xff0c;这是一个在html中实现的具有金属质感并且能镜面反射的按钮~ 效果 电脑效果 手机效果 说明 主要思路是使用 navig…

【算法基础】二分图(染色法 匈牙利算法)

一、二分图 1. 染色法 一个图是二分图,当且仅当,图中不含奇数环。在判别一个图是否为二分图⑩,其实相当于染色问题,每条边的两个点必须是不同的颜色,一共有两种颜色,如果染色过程中出现矛盾,则说明不是二分图。 for i = 1 to n:if i 未染色DFS(i, 1); //将i号点染色未…

Leetcode138. 复制带随机指针的链表

复制带随机指针的链表 第一步 拷贝节点链接在原节点的后面 第二步拷贝原节点的random &#xff0c; 拷贝节点的 random 在原节点 random 的 next 第三步 将拷贝的节点尾插到一个新链表 ,并且将原链表恢复 从前往后遍历链表 ,将原链表的每个节点进行复制&#xff0c;并l链接到原…