C++——vector容器模拟实现

news2024/10/7 4:37:34

目录

1. 基本成员函数

2. 默认成员函数

2.1 构造函数

2.2 析构函数

2.3 拷贝构造函数

2.4 赋值运算符重载函数

3. 容器访问相关函数

3.1 operator[ ]运算符重载

3.2 迭代器

3.3 范围for

4. vector空间增长问题

4.1 vector 容量和大小

4.2 vector扩容

4.3 重新定义大小

5. vector插入和删除

5.1 尾插

5.2 插入insert

5.3 尾删

5.4 erase删除

5.5 clear清空数据

6.总代码

6.1 vector.h

6.2 test.cpp

1. 基本成员函数

基本成员函数为三个指针,分别指向vector的开始,有效数字的位置,和容器的容量。

namespace cpp
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
	private:
		iterator _start;	  //指向容器的头
		iterator _finish;	  //指向有效数据的尾
		iterator _endofstoage;//指向容器的尾
	};
}

2. 默认成员函数

2.1 构造函数

  • (1)无参构造   vector<T> v

只需要把每个成员变量初始化为nullptr即可。

//无参构造函数
vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstoage(nullptr)
{}
  • (2)有参构造    vector(v.begin(),v.end())

有残构造是将区间[first,last]中的数据拷贝给本身。

vector的带参构造函数首先在初始化列表对基本成员变量初始化,在将迭代器区间在[first, last)的数据一个个尾插到容器当中即可:

//带参构造函数
template <class InputIterator>//函数模板
vector(InputIterator first, InputIterator last)
	: _start(nullptr)
	, _finish(nullptr)
	, _endofstoage(nullptr)
{
    //将迭代器区间在[first, last)的数据一个个尾插到容器当中
	while (first != last)
	{
		push_back(*first);
		first++;
	}
}
  • (3)用n个val去初始化vector      vector(n,val)

vector的构造函数还支持用n个val去初始化,只需要先调用reserve函数开辟n个大小的空间,再利用for循环把val的值依次push_back尾插进去即可。

//用n个val来构造vector
vector(size_t n, const T& val = T())
	: _start(nullptr)
	, _finish(nullptr)
	, _endofstoage(nullptr)
{
	reserve(n);
	for (size_t i = 0; i < n; i++)
	{
		push_back(val);
	}
}

这样写会出现一个问题:内存寻址错误。当我想实现下面的语句时:

cpp::vector<int> v(10, 4);

这里我调用的地方两个参数都是int,此时调用构造函数时匹配的是第二个传迭代器区间的构造函数,导致这样的原因在于编译器会优先寻找最匹配的那个函数。此构造函数的第一个参数是unsigned int类型,所以不会优先匹配此构造函数。因此我们需要再重载一个第一个参数为int类型的构造函数即可解决:
 

vector(int n, const T& val = T())
	: _start(nullptr)
	, _finish(nullptr)
	, _endofstoage(nullptr)
{		
    reserve(n);
	for (int i = 0; i < n; i++)
	{
		push_back(val);
	}
}

2.2 析构函数

首先判断该容器_start是否为空,不为空就释放空间+置空即可。

//析构函数
~vector()
{
	if (_start)//避免释放空指针
	{
		delete[] _start;//释放容器所指向的空间
		_start = _finish = _endofstoage = nullptr;//置空
	}	
}

2.3 拷贝构造函数

  • vector(const vector &vec)

拷贝构造可以借助先前string的拷贝构造思路,利用现代方法解决,首先对基本成员变量进行初始化,接着建立一个tmp的模板将要拷贝的数据利用构造函数去传递过去,再将这个tmp模板与自己交换即可。

//拷贝构造函数
vector(const vector<T>& v)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstoage(nullptr)
{
	vector<T> tmp(v.begin(), v.end());//调用构造函数
	swap(tmp);
}

2.4 赋值运算符重载函数

这里是传值传参,没有引用传参,直接利用vector调用构造函数返回的值与左值进行swap交换即可进行赋值。

//赋值运算符重载
vector<T>& operator=(vector<T> v)//调用构造
{
	this->swap(v);//交换这两个对象
	return *this;//返回
}

3. 容器访问相关函数

3.1 operator[ ]运算符重载

直接返回pos位置的数据即可进行下标+[ ]的方式进行访问。

//operator[]运算符重载
T& operator[](size_t pos)
{
    assert(pos < size());//检测pos的合法性
    return _start[pos];
}

为了方便const对象也可以调用[ ]运算符重载,因此还推出了一个const版本的[ ]运算符重载。

//const版本的[]运算符重载
const T& operator[](size_t pos) const
{
	assert(pos < size());//检测pos的合法性
	return _start[pos];	
}

3.2 迭代器

vector的begin直接返回容器的_start起始位置即可,vector的end返回容器的_finish的位置。

//begin
iterator begin()
{
	return _start;//返回容器起始位置
}
//end
iterator end()
{
	return _finish;//返回有效数据下一个的地址
}

const版本的迭代器如下:

//const版本迭代器
const_iterator begin() const
{
	return _start;
}
//end
const_iterator end() const
{
	return _finish;
}

3.3 范围for

范围for的底层是通过迭代器实现:

void test_vector()
{
	cpp::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
    //范围for
	for (auto e : v)
	{
		cout << e << " ";//1 2 3 4 5
	}
}

4. vector空间增长问题

4.1 vector 容量和大小

指针相减可以得到对应的个数,因此获取size只需_finish - _start。获取capacity只需_endofstoage - _start

  • (1)size
size_t size() const //最好加上const,普通对象和const对象均可调用
{
	return _finish - _start; //指针相减就能得到size的个数
}
  • (2)capacity
size_t capacity() const
{
	return _endofstoage - _start;
}

4.2 vector扩容

  • reserve(int len);

reserve扩容和string的扩容非常相似。先开辟一块新的扩好容的空间,如果旧空间里头有数据,那么就利用for循环将容器中的数据一个一个拷贝到新空间,再释放旧空间,最后指向新空间。如果没有,直接指向新空间即可。

//reserve扩容
void reserve(size_t n)
{
	size_t sz = size();//提前算出size()的大小,方便后续更新_finish
	if (n > capacity())
	{
		T* tmp = new T[n];
		if (_start)//判断旧空间是否有数据
		{
			//不能用memcpy,因为memcpy是浅拷贝
			for (size_t i = 0; i < size(); i++)
			{
				tmp[i] = _start[i];//将容器当中的数据一个个拷贝到tmp当中
			}
			delete[] _start;//释放旧空间
		}
		_start = tmp;//指向新空间
	}
	//更新_finish和_endofstoage
	_finish = _start + sz;
	_endofstoage = _start + n;
}

注意:

  1. 在扩容结束后要记得更新_finish和_endofstoage,这里的_finsh要加上原先的size()长度,要先用变量sz保存下来,否则后续扩容后会更改指针的指向由原先的_start变为tmp,若直接+ size()函数的返回值会导致结果为随机值。
  2. 不能使用memcpy进行数据拷贝,因为memcpy是浅拷贝,它会将一段内存空间中内容原封不动的拷贝到另外一段内存空间中,导致后续delete时拷贝过的数据一并给delete了,具体下篇博文详谈。

4.3 重新定义大小

  • resize(int num);    resize(int num.elem);//用elem填充
  1. 如果 n 小于当前容器的size(),则内容将减少到其前 n 个元素,删除超出(并销毁)的元素。
  2. 如果 n 大于当前容器 size(),则通过在末尾插入所需数量的元素以达到 n 的大小来扩展内容。若指定了 val,则新元素将初始化为 val 的副本,否则,它们将进行值初始化。
  3. 如果 n 也大于当前容器容量capacity(),则会自动重新分配分配的存储空间。
//resize
//void resize(size_t n, T val = T())
void resize(size_t n, const T& val = T()) //利用T()调用默认构造函数的值进行初始化,这样写说明C++的内置类型也有自己的构造函数
{
	//如果 n > capacity()容量,就需要扩容
	if (n > capacity())
	{
		reserve(n);
	}
	//如果 n > size(),就需要把有效数据_finish到_start + n之间的数据置为缺省值val
	if (n > size())
	{
		while (_finish < _start + n)
		{
			*_finish = val;
			_finish++;
		}
	}
	//如果 n < size(),更新有效数据到_start + n
	else
	{		
    	_finish = _start + n;
	}
}
  • 补充:C++的内置类型也有自己的构造函数和析构函数,这样才能更好的支持模板。
void test()
{
	int i = 0;
	int j = int();
	int k = int(1);
	cout << i << endl;//0
	cout << j << endl;//0
	cout << k << endl;//1
}

4.4 swap交换

直接调用库函数的swap去进行成员变量的交换即可。

//交换函数
void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstoage, v._endofstoage);
}

5. vector插入和删除

5.1 尾插

  • push_back(elem)

先判断是否需要扩容,把尾插的值赋过去,再更新有效数据地址_finish即可:

void push_back(const T& x)
{
	//检测是否需要扩容
	if (_finish == _endofstoage)
	{
		size_t newcapcacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapcacity);
	}
	*_finish = x;
	_finish++;
}

这里push_back还可以复用下文实现好的insert进行尾插,当insert中的pos为_finish时,insert实现的就是push_back尾插。而_finish可以通过调用迭代器end函数来解决。

void push_back(const T& x)
{
	//法二:复用insert
	insert(end(), x); //当insert中的参数pos为end()时,就是尾插
}

5.2 插入insert

首先要检查插入的位置是否越界,以及是否需要扩容。接着检测是否需要扩容。再挪动数据,最后把值插入进去。

  • 注意

注意扩容以后,pos就失效了,要记得更新pos,否则会发生迭代器失效。可以通过设定变量n来计算扩容前pos指针位置和_start指针位置的相对距离,最后在扩容后,让_start再加上先前算好的相对距离n就是更新后的pos指针的位置了。其实这里还有一个迭代器失效的问题,具体是啥,后续专门出一篇迭代器失效的文章。下面给出完善修正后的insert:
 

//insert
iterator insert(iterator pos, const T& x)
{
	//检测参数合法性
	assert(pos >= _start && pos <= _finish);
	//检测是否需要扩容
	/*扩容以后pos就失效了,需要更新一下*/
	if (_finish == _endofstoage)
	{
		size_t n = pos - _start;//计算pos和start的相对距离
		size_t newcapcacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapcacity);
		pos = _start + n;//防止迭代器失效,要让pos始终指向与_start间距n的位置
	}
	//挪动数据
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *(end);
		end--;
	}
	//把值插进去
	*pos = x;
	_finish++;
	return pos;
}

5.3 尾删

首先判断_finish是否大于_start,若大于,直接_finsh--即可,否则啥也不需要操作。

void pop_back()
{
	if (_finish > _start)//判断是否可以进行删除
	{
		_finish--;
	}
}

pop_back也可以复用下文的erase实现,当erase的参数为_finish时,实现的就是尾删,而_finish可以通过调用迭代器end()函数来解决。

void pop_back()
{
	//法二:复用erase
	erase(end() - 1);
    //不能用end()--,因为end()是传值返回,返回的是临时对象,临时对象具有常性,不能自身++或--,因此要用end() - 1
}

5.4 erase删除

首先要检查删除位置pos的合法性,其次从pos + 1的位置开始往前覆盖即可删除pos位置,最后记得返回的值为删除位置的下一个位置,其实返回的就是pos,因为在pos删除后,下一个值会覆盖到pos的位置上。

//erase
iterator erase(iterator pos)
{
	//检查合法性
	assert(pos >= _start && pos < _finish);
	//从pos + 1的位置开始往前覆盖,即可完成删除pos位置的值
	iterator it = pos + 1;
	while (it < _finish)
	{
		*(it - 1) = *it;		
        it++;
	}
	_finish--;
	return pos;
}
  • 补充说明:
  1. 一般vector删除数据,都不考虑缩容的方案,当size() < capacity() / 2 时,可以考虑开一个size()大小的新空间,拷贝数据,释放旧空间。缩容的本质是时间换空间。一般设计不会考虑缩容,因为实际比较关注时间效率,不是太关注空间效率,因为现在硬件设备空间都比较大,空间存储也比较便宜。
  2. erase也会存在失效,erase的失效是意义变了,或者不存在有效访问数据有效范围。
  3. 一般不会使用缩容的方案,那么erase的失效,一般也不存在野指针的失效。

后续专门推出一篇博文讲解迭代器失效。这里先给出结论:

  1. erase(pos)以后pos失效了,pos的意义变了,但是在不同平台下面对于访问pos的反应是不一样的,我们用的时候要以失效的角度去看待此问题。
     
  2. 对于insert和erase造成迭代器失效问题,linux的g++平台检查很佛系,基本靠操作系统本身野指针越界检擦机制。windows下VS系列检擦更严格一些,使用一些强制检擦机制,意义变了可能会检擦出来。
  3. 虽然g++对于迭代器失效检查时是非常佛系的,但是套在实际场景中,迭代器意义变了,也会出现各种问题。

5.5 clear清空数据

只需要把起始位置的指针_start赋给有效数据指针_finish即可完成数据的清空。

//clear清空数据
void clear()
{
	_finish = _start;
}

6.总代码

6.1 vector.h

#pragma once
#include<assert.h>
#include<iostream>
using namespace std;

namespace cpp
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
/*构造函数*/
	//无参构造
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{}
	//带参构造
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			//将迭代器区间在[first, last)的数据一个个尾插到容器当中
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}
	//用n个val来构造vector
		vector(size_t n, const T& val = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}
		vector(int n, const T& val = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}


	//交换函数
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstoage, v._endofstoage);
		}
/*拷贝构造函数*/
		//vector(const vector& v) 也可以这样写,但不推荐
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
		}

/*赋值运算符重载*/
		//vector& operator=(vector v) 也可以这样写,但不推荐
		vector<T>& operator=(vector<T> v)
		{
			this->swap(v);
			return *this;
		}

/*析构函数*/
		~vector()
		{
			if (_start)//避免释放空指针
			{
				delete[] _start;//释放容器所指向的空间
				_start = _finish = _endofstoage = nullptr;//置空
			}
		}


/*容器访问相关函数*/
	//迭代器
		//begin
		iterator begin()
		{
			return _start;
		}
		//end
		iterator end()
		{
			return _finish;
		}
	//const版本迭代器
		const_iterator begin() const
		{
			return _start;
		}
		//end
		const_iterator end() const
		{
			return _finish;
		}

	//operator[]运算符重载
		T& operator[](size_t pos)
		{
			assert(pos < size());//检测pos的合法性
			return _start[pos];
		}
		//const版本的[]运算符重载
		const T& operator[](size_t pos) const
		{
			assert(pos < size());//检测pos的合法性
			return _start[pos];
		}

/*空间增长问题*/
	//size
		size_t size() const //最好加上const,普通对象和const对象均可调用
		{
			return _finish - _start; //指针相减就能得到size的个数
		}
	//capacity
		size_t capacity() const
		{
			return _endofstoage - _start;
		}

	//reserve扩容
		void reserve(size_t n)
		{
			size_t sz = size();//提前算出size()的大小,方便后续更新_finish
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start)//判断旧空间是否有数据
				{
					//不能用memcpy,因为memcpy是浅拷贝
					for (size_t i = 0; i < size(); i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;//释放旧空间
				}
				_start = tmp;//指向新空间
			}
			//更新_finish和_endofstoage
			_finish = _start + sz;
			_endofstoage = _start + n;
		}
	//resize
		//void resize(size_t n, T val = T())
		void resize(size_t n, const T& val = T()) //利用T()调用默认构造函数的值进行初始化,这样写说明C++的内置类型也有自己的构造函数
		{
			//如果 n > capacity()容量,就需要扩容
			if (n > capacity())
			{
				reserve(n);
			}
			//如果 n > size(),就需要把有效数据_finish到_start + n之间的数据置为缺省值val
			if (n > size())
			{
				while (_finish < _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
			//如果 n < size(),更新有效数据到_start + n
			else
			{
				_finish = _start + n;
			}
		}


/*增加*/
	//push_back
		void push_back(const T& x)
		{
			//检测是否需要扩容
			if (_finish == _endofstoage)
			{
				size_t newcapcacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapcacity);
			}
			*_finish = x;
			_finish++;
		/*	
			法二:复用insert
			insert(end(), x); //当insert中的参数pos为end()时,就是尾插
		*/
		}
	//insert
		iterator insert(iterator pos, const T& x)
		{
			//检测参数合法性
			assert(pos >= _start && pos <= _finish);
			//检测是否需要扩容
			/*扩容以后pos就失效了,需要更新一下*/
			if (_finish == _endofstoage)
			{
				size_t n = pos - _start;//计算pos和start的相对距离
				size_t newcapcacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapcacity);
				pos = _start + n;//防止迭代器失效,要让pos始终指向与_start间距n的位置
			}
			//挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *(end);
				end--;
			}
			//把值插进去
			*pos = x;
			_finish++;
			return pos;
		}



/*删除*/
	//pop_back
		void pop_back()
		{
			if (_finish > _start)
			{
				_finish--;
			}
		/*
			法二:复用erase
			erase(end() - 1); 
			//不能用end()--,因为end()是传值返回,返回的是临时对象,临时对象具有常性,不能自身++或--,因此要用end() - 1
		*/
		}
	//erase
		iterator erase(iterator pos)
		{
			//检查合法性
			assert(pos >= _start && pos < _finish);
			//从pos + 1的位置开始往前覆盖,即可完成删除pos位置的值
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				it++;
			}
			_finish--;
			return pos;
		}
	//clear清空数据
		void clear()
		{
			_finish = _start;
		}

	private:
		iterator _start;	  //指向容器的头
		iterator _finish;	  //指向有效数据的尾
		iterator _endofstoage;//指向容器的尾
	};
}

6.2 test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include<vector>
#include"vector.h"
void test()
{
	int i = 0;
	int j = int();
	int k = int(1);
	cout << i << endl;//0
	cout << j << endl;//0
	cout << k << endl;//1
}
void test_vector1()
{
	cpp::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	cpp::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	v.pop_back();
	v.pop_back();
	/*for (auto e : v)
	{
		cout << e << " ";
	}*/
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
}

void test_vector2()
{
	cpp::vector<int> v;
	v.resize(10, -2);
	for (auto e : v)
	{
		cout << e << " ";
	}
}

void test_vector3()
{
	//在所有的偶数前面插入2
	cpp::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	cpp::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			//it = v.insert(it, 20);
			it++;
		}
		it++;
	}
	for (auto e : v)
	{
		cout << e << " ";
	}

}
//在所有的偶数前面插入2
	/*v.push_back(5);
	v.push_back(6);*/
	
void test_vector4()
{
	
	cpp::vector<int> v;
	//v.reserve(10);
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << v.size() << ":" << v.capacity() << endl;
	auto pos = find(v.begin(), v.end(), 2);
	if (pos != v.end())
	{
		v.erase(pos);
	}
	cout << *pos << endl;
	*pos = 10;
	cout << *pos << endl << endl;
	cout << v.size() << ":" << v.capacity() << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	
}

namespace std
{
	void test_vector5()
	{

		vector<int> v;
		//v.reserve(10);
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		cout << v.size() << ":" << v.capacity() << endl;
		auto pos = find(v.begin(), v.end(), 4);
		if (pos != v.end())
		{
			v.erase(pos);
		}
		cout << *pos << endl;
		*pos = 10;
		cout << *pos << endl << endl;
		cout << v.size() << ":" << v.capacity() << endl;
		for (auto e : v)
		{
			cout << e << " ";
		}

	}
	void test_vector6()
	{
		//删除所有的偶数
		vector<int> v;
		//v.reserve(10);
		v.push_back(1);
		v.push_back(2);
		v.push_back(2);
		v.push_back(2);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			{
				it++;
			}
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
	}
}
void test_vector7()
{
	//在所有的偶数前面插入2
	cpp::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	cpp::vector<int> v2(v.begin(), v.end());
	for (auto e : v2)
	{
		cout << e << " ";//1 2 3 4 5 6
	}
	cout << endl;
	string s("hello world");
	cpp::vector<char> v3(s.begin(), s.end());
	for (auto e : v3)
	{
		cout << e << " "; //h e l l o   w o r l d
	}
	cout << endl;
	cpp::vector<int> v4(v2);
	for (auto e : v4)
	{
		cout << e << " ";//1 2 3 4 5 6
	}
	cout << endl;
	v4.pop_back();
	v4.pop_back();
	v4.pop_back();
	cpp::vector<int> v5;
	v5 = v4;
	for (auto e : v5)
	{
		cout << e << " ";//1 2 3 4 5 6
	}
}

void test_vector8()
{
	cpp::vector<int> v(10, 4);
	for (auto e : v)
	{
		cout << e << " ";
	}
}


void test_vector9()
{
	cpp::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//v.push_back(5);
	v.insert(v.begin(), 0); 
	for (auto e : v)
	{
		cout << e << " ";
	}
}

void test_vector10()
{
	//在所有的偶数前面插入2
	cpp::vector<int> v;
	//v.reserve(10);
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	cpp::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			it = v.insert(it, 20);
			it++;
		}
		it++;
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
}
void test1()
{
	vector<int> v;
	v.reserve(10);
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << v.size() << ":" << v.capacity() << endl;
	auto pos = find(v.begin(), v.end(), 2);
	if (pos != v.end())
	{
		v.insert(pos, 20);
	}
	cout << *pos << endl;
}
void test2()
{
	cpp::vector<int> v;
	//v.reserve(10);
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << v.size() << ":" << v.capacity() << endl;
	auto pos = find(v.begin(), v.end(), 4);
	if (pos != v.end())
	{
		v.erase(pos);
	}
	cout << *pos << endl;
	*pos = 10;
	cout << *pos << endl << endl;
	cout << v.size() << ":" << v.capacity() << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
}
void test3()
{
	std::vector<int> v;
	//v.reserve(10);
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << v.size() << ":" << v.capacity() << endl;
	auto pos = find(v.begin(), v.end(), 4);
	if (pos != v.end())
	{
		v.erase(pos);
	}
	cout << *pos << endl;
	*pos = 10;
	cout << *pos << endl << endl;
	cout << v.size() << ":" << v.capacity() << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
}
void test4()
{
	//删除所有的偶数
	std::vector<int> v;
	//v.reserve(10);
	v.push_back(1);
	v.push_back(2);
	v.push_back(2);
	v.push_back(2);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	auto it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			it = v.erase(it);
		}
		else
		{
			it++;
		}
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
}
namespace cpp
{
	class Solution {
	public:
		// 核心思想:找出杨辉三角的规律,发现每一行头尾都是1,中间第[j]个数等于上一行[j-1]+[j]
		vector<vector<int>> generate(int numRows) {
			vector<vector<int>> vv;
			// 先开辟杨辉三角的空间
			vv.resize(numRows);
			for (size_t i = 1; i <= numRows; ++i)
			{
				vv[i - 1].resize(i, 0);
				// 每一行的第一个和最后一个都是1
				vv[i - 1][0] = 1;
				vv[i - 1][i - 1] = 1;
			}
			for (size_t i = 0; i < vv.size(); ++i)
			{
				for (size_t j = 0; j < vv[i].size(); ++j)
				{
					if (vv[i][j] == 0)
					{
						vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
					}
				}
			}
			return vv;
		}
	};

	void test7()
	{
		vector<vector<int>> vv = Solution().generate(5);
		for (size_t i = 0; i < vv.size(); ++i)
		{
			for (size_t j = 0; j < vv[i].size(); ++j)
			{
				cout << vv[i][j] << " ";
			}
			cout << endl;
		}
	}
}
int main()
{
	/*cpp::vector<int> v;
	size_t size = v.size();
	size_t capacity = v.capacity();
	cout << "size:" << size << endl;
	cout << "capacity:" << capacity << endl;

	v.push_back(1);
	cout << "size:" << size << endl;
	cout << "capacity:" << capacity << endl;
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);*/

	//test_vector1();
	//test();
	//test_vector2();
	//test_vector3();
	//std::test_vector5();
	//std::test_vector6();
	//test_vector8();
	//test_vector10();
	//test2();
	//test3();
	//test4();
	cpp::test7();
}

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

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

相关文章

均匀B样条采样从LiDAR数据中快速且鲁棒地估计地平面

文章&#xff1a;Fast and Robust Ground Surface Estimation from LiDAR Measurements using Uniform B-Splines 作者&#xff1a;Sascha Wirges, Kevin Rsch, Frank Bieder and Christoph Stiller 编辑&#xff1a;点云PCL 代码&#xff1a; https://github.com/KIT-MRT/poin…

全志V3S嵌入式驱动开发(编译器升级到7.5)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 看过我们文章的朋友都知道&#xff0c;前面为了做v3s的驱动&#xff0c;对linux kernel进行了两次升级。第一次升级是从4.10.y升级到4.14.y&#x…

【Python】open打开文件出现的错误解决

一、Python中关于打开open打开文件出现的错误解决 &#xff08;第一种&#xff09;UnicodeDecodeError: ‘utf-8’.......... &#xff08;第二种&#xff09;UnicodeDecodeError: ‘gbk’......... 二、问题解决 两种解决方式针对不同错误&#xff0c;实际应用中可以都试试…

PCB设计实验|第五周|LED显示电路PCB库设计|3月27日

目录 实验四 LED显示电路PCB库设计 一、实验原理 二、实验环境 三、实验结果 四、实验总结 实验四 LED显示电路PCB库设计 一、实验原理 LED(Light- Emitting-Diode中文意思为发光二极管)是一种能够将电能转化为可见光的半导体&#xff0c;它改变了白炽灯钨丝发光与节能…

裁剪图片软件有哪些?这些图片裁剪工具很好用

有哪些好用的图片裁剪软件呢&#xff1f;有时候&#xff0c;将一张大图缩小到更小的尺寸可以改善图片的质量&#xff0c;因为它可以减少像素和噪点。这对于那些需要在网上展示高质量图片的人来说尤其重要。裁剪后的图片可能更清晰、更锐利&#xff0c;并且更适合在各种设备上观…

Alex-Net 与 VGG-16

Alex-Net 由加拿大多伦多大学的 Alex Krizhevsky、Ilya Sutskever(G. E. Hinton 的两位博士生)和 Geoffrey E. Hinton 提出&#xff0c;网络名“Alex-Net”即 取自第一作者名。 下图所示是 Alex-Net 的网络结构&#xff0c;共含五层卷积层和三层全连接层。其中&#xff0c;Ale…

03.SELF-INSTRUCT+Alpaca

文章目录 前言泛读储备知识提示学习提示工程Promt Engineering答案工程 背景介绍研究SELF-INSTRUCT的动机研究意义&贡献 精读Overview种子任务步骤1&#xff1a;定义指令数据步骤2&#xff1a;自动指令数据生成步骤2.1指令生成步骤2.2指令分类步骤2.3实例生成步骤2.4筛选和…

19.组件之间传递数据

不同组件传递数据的时候&#xff0c;最好不要直接传递复杂数据类型(比如对象&#xff0c;数组) 前端需要处理的数据层级一般不会很多&#xff0c;需要在多处使用的数据一般会被放到数据库中 目录 1 组件的关系 2 父向子传递数据-props 3 子向父传递数据-自定义事件 4 …

分布式任务调度平台 XXL-JOB 实战

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

Scrum敏捷估算

无论是团队研发一款产品或者开发某一个项目&#xff0c;我们都需要回答“我们大概什么时间能够完成&#xff1f;”&#xff0c; 或者到某一个时间点&#xff0c;我们能够做到什么程度&#xff0c; 因此和传统的开发模式一样&#xff0c;我们在工作开始之前需要对我们需要做的事…

Linux Vim基本操作(文件的打开和编辑)完全攻略(有图有真相)

首先学习如何使用 Vim 打开文件。 Vim 打开文件 使用 Vim 打开文件很简单&#xff0c;例如在命令行模式下打开一个自己编写的文件 /test/vi.test&#xff0c;打开方法如下&#xff1a; [rootitxdl ~]# vim /test/vi.test 刚打开文件时 Vim 处于命令模式&#xff0c;此时文件…

CTFshow-pwn入门-前置基础pwn26-pwn28

什么是ASLR 大多数的攻击都基于这样一个前提&#xff0c;即攻击者知道程序的内存布局&#xff0c;需要提前知道shellcode或者其他一些数据的位置。因此&#xff0c;引入内存布局的随机化能够有效增加漏洞利用的难度&#xff0c;其中一种技术就是ASLR&#xff08;Address Space…

无线wifi视频传输方案|基于qca9531方案SKW99的无线视频流云端推送方案

为满足物联网智慧校园&#xff0c;智能家居&#xff0c;智慧工厂&#xff0c;智能交通、智慧博物馆、培训机构等不同行业实时直播的需求。本篇以集成200万高清摄像头功能的高通方案qca9531 wifi模块SKW99为为例&#xff0c;简单介绍基于WiFi技术的无线视频流云端推送方案。 1、…

上位机与两台PLC之间无线PPI通信

在实际系统中&#xff0c;人机界面与PLC通常不在一起&#xff0c;中心计算机一般放置在控制室&#xff0c;而PLC安装在现场车间&#xff0c;二者之间距离往往从几十米到几千米。如果布线的话&#xff0c;需要挖沟施工&#xff0c;比较麻烦&#xff0c;这种情况下比较适合采用无…

0基础学习VR全景平台篇第47篇:底部菜单-场景/分组复制功能

大家好&#xff0c;欢迎观看蛙色VR官方系列——后台使用课程&#xff01; 本期为大家带来蛙色VR平台&#xff0c;底部菜单—场景/分组复制功能操作。 功能位置示意 一、本功能将用在哪里&#xff1f; 平台用户在编辑作品时可以使用本功能将作品中的某一分组或者某一场景进行复…

岩土工程监测案例:完整链条的振弦传感器、采集仪和在线监测系统

岩土工程监测案例&#xff1a;完整链条的振弦传感器、采集仪和在线监测系统 在岩土工程监测中&#xff0c;振弦传感器被广泛应用于测量土体或岩体的振动情况&#xff0c;以了解地震或其他振动事件对结构物或地基的影响。振弦传感器具有高精度、快速响应、易于安装和低成本等优…

django校园宿舍管理系统-计算机毕设 附源码84831

django校园宿舍管理系统 摘 要 本论文主要论述了如何使用Django开发一个校园宿舍管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述校园宿舍管理系统的当…

RocketMQ部署之动态设置JVM启动参数

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 背景 线上的RocketMQ集群有运行一段时间了。比如测试环境和线上环境的RocketMQ集群部署的机器内存大小肯定不一样。所以可能要写多个部署脚本。非常麻烦 官方的部…

一张图秒懂嵌入式Linux系统的启动流程

一图胜千言&#xff01;看图&#xff1a; 上图是嵌入式系统启动流程图&#xff0c;图中红色的数字圆点表示启动的先后顺序。主要分为 4 个阶段&#xff0c;分别是&#xff1a;第一阶段 bootloader&#xff0c;第二阶段uboot&#xff0c;第三阶段内核启动&#xff0c;第四阶段 a…

【数据管理架构】OLAP 与 OLTP:有什么区别?

这些术语经常相互混淆&#xff0c;那么它们的主要区别是什么&#xff1f;您如何根据自己的情况选择合适的术语&#xff1f; 我们生活在一个数据驱动的时代&#xff0c;使用数据做出更明智决策并更快响应不断变化的需求的组织更有可能脱颖而出。您可以在新的服务产品&#xff08…