【C++】vector


描述

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。

一、vector(标准库)

常见接口

在这里插入图片描述

vector使用

遍历
在这里插入图片描述

查找元素,插入元素
在这里插入图片描述

构造函数
在这里插入图片描述

  我们在以前写杨辉三角的时候使用的是一个二维数组,在学习完vector后,我们也可以使用vector解决杨辉三角的问题。

leetcode杨辉三角问题

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
    		//这里的vector<vector<int>>我们就可把他看做一个二维数组,由于内部实现一些函数,
    		//所以可以使用[]直接像数组那样使用,但是不是数组,是内部实现的运算符重载
            vector<vector<int>> vv;
            vv.resize(numRows,vector<int>());

            for(size_t i=0;i<vv.size();i++)
            {
                vv[i].resize(i+1,0); //扩容
                vv[i][0]=vv[i][vv[i].size()-1]=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]+vv[i-1][j-1];
                    }
                }
            }
            return vv;
    }
};

二、vector模拟实现

1.默认成员函数

template<class T>
	class vector
	{
	public:
		//迭代器
		typedef T* iterator;
		typedef const T* const_iterator;
		//空参的构造函数
		vector()
		{}
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}
		//重载,为防止构造器匹配到别的函数模板,需重载一个int类型的
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}

		//支持任意类型的迭代器构造,我们传迭代器可以传入不同类型,需要写泛型函数模板
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

		//拷贝构造
		vector(const vector<T>& v)
		{
			//提前开好所用空间,可以提高效率
			reserve(v.capacity());
			_start = new T[v.capacity()];
			//注意:memcpy(_start, v._start,sizeof(T) * v.size());  //不能完成深拷贝
			//使用拷贝构造就是深拷贝
			for (size_t i = 0; i < v.size(); ++i)
			{
				_start[i] = v._start[i];
			}
			//更新
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}
		//析构函数
		~vector()
		{
			//通过new出来的空间需要使用delete手动使用,再将指针指向空,防止内存泄露
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
	private: 
		//C++11 内置类型可以传缺省值,这里的缺省值传给初始化列表初始化
		iterator _start=nullptr;  //指向数组开头的指针
		iterator _finish=nullptr;	//指向数组最后一个有效元素的指针
		iterator _end_of_storage=nullptr;	//指向数组最后一个位置的指针
	};

2.resize

	//开空间 + 初始化
	void resize(size_t n,const T val=T())  //使用匿名函数初始化,这里为了可以初始化,可以使用 int a =int();
	{
		if (n < size())
		{
			//n<size,说明要调整的大小比我们原来的要小,删除数据,其实就是将指针位置改变一下,不需要真的删除元素
			_finish = _start + n;
		}
		else
		{
			
			if (n > capacity())
			{
				//这里 n>capacity 这种情况说明,要调整的大小比我们最大容量还要大,此时我们需要扩容
				reserve(n);
			}
			while (_finish != _start + n)
			{
				*_finish = val;
				++_finish;
			}
		}
	}

3.reserve

	void reserve(size_t n)
	{
		if (n > capacity())
		{
			size_t sz = size();
			//申请空间
			T* tmp = new T[n];
			//如果原数组中有元素,拷贝到新申请的空间上	
			if (_start)
			{
				for (size_t i = 0; i < sz; i++)
				{
					tmp[i] = _start[i];
				}
				delete[] _start;
			}
			//更新
			_start = tmp;
			_finish = _start + sz;  //小坑,不可使用size(),因为更新完_start后size()发生改变
			_end_of_storage = _start + n;
		}
	}

4.尾插,尾删

	//尾插
	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			//扩容
			reserve(capacity()==0?4:capacity()* 2);
		}
		*_finish = x;
		++_finish;
	}
	//尾删
	void pop_back()
	{
		assert(!empty()); //断言vector是否为空
		--_finish;
	}

5.insert(插入)

	void insert(iterator pos, const T& val)
	{
		assert(pos >= _start);
		assert(pos <= _finish);
	
		if (_finish == _end_of_storage)
		{
			//迭代器失效问题,需要更新pos,因为我们使用reserve是异地扩容,迭代器的值已被改变,我们需要记录一下相对位置
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : capacity() * 2);
	
			//扩容后更新pos,解决pos失效问题
			pos = _start + len;
		}
	
		iterator end = _finish - 1;
		while (end >= pos)
		{
			*(end + 1) = *end;
			--end;
		}
		*pos = val;
		++_finish;
	}

6.erase(删除)

iterator erase(iterator pos)
	{
		//也有迭代器失效问题,野指针  所以我们在删除指定位置的元素后,不可再使用原来的迭代器进行操作,返回一个新的迭代器可解决此问题
		assert(pos >= _start);
		assert(pos < _finish);

		iterator start = pos + 1;
		//挪动数据
		while (start != _finish)
		{
			*(start - 1) = *start;
			++start;
		}
		--_finish;

		return pos;
	}

7. [] 运算符重载

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

8.empty(判空),capacity(容量大小),size(元素个数)

	bool empty()
	{
		return _start == _finish;
	}

	size_t capacity() const
	{
		return _end_of_storage - _start; //指针 - 指针
	}
	size_t size() const
	{
		return _finish - _start;
	}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值