C++ vector

目录

前言 STL简介

1. 什么是STL

2. STL的版本

3. STL的六大组件

4. STL的重要性

5. 如何学习STL        

6. STL的缺陷        

一、vector的介绍及使用

1.1 vector的介绍

1.1.1 认识vector

1.1.2 成员类型

​编辑

1.1.3 成员函数一览

1.1.4 非成员函数重载 

1.2 vector的使用

1.2.1 构造、析构与赋值操作符重载

1.2.2 reserve 与 resize

1.2.3 insert、erase 与 find

extra train

1. 二叉树的前序遍历

2. 只出现一次的数字

3. 杨辉三角

二、vector的深度剖析及模拟实现

2.1 vector的深度剖析         

2.1.1 memcpy 浅拷贝问题

2.2.2 iterator 迭代器失效问题        

insert 迭代器失效

erase 迭代器失效        

2.2 vector的模拟实现        

extra train   

1. 只出现一次的数字 II    

2. 电话号码的字母组合  



前言 STL简介

1. 什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架

2. STL的版本

原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本--所有STL实现版本的始祖。
P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程风格上看,阅读性非常高。

3. STL的六大组件

4. STL的重要性

STL在将来面试、笔试和工作的时候都非常重要,网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

5. 如何学习STL        

        

6. STL的缺陷        

1. STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。
2. STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
3. STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
4. STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的。

        


一、vector的介绍及使用

注意:作者能力有限,无法根据英文文档翻译出较为准确的中文意思,vector具体相关知识参考网站  cplusplus.com/reference/vector/vector/        

1.1 vector的介绍

1.1.1 认识vector

vector是表示大小可变的数组的序列容器
1. 与数组类似,vector也采用的连续存储空间来存储元素,可以采用下标对vector的元素进行访问,但是它的大小是可以动态改变的,而且它的大小会被容器自动处理
2 本质上,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小以此来增加存储空间
3 vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大(不同的库采用不同的策略权衡空间的使用和重新分配)
4 与其它动态序列容器相比(deque、 list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好

1.1.2 成员类型

1.1.3 成员函数一览

vector的许多成员函数都与string类似(左边一栏为函数名,右边一栏为简要作用的描述)

        

1.1.4 非成员函数重载 

        

1.2 vector的使用

注意:一些常见的、通用的不再赘述(例如:size()求大小、capacity()求容量、~vector()析构、push_back()尾插 等不作详细介绍,主要介绍接口有些复杂但是比较常用的一些函数)

1.2.1 构造、析构与赋值操作符重载

接口展示:

void test_vector1()
{
	// 构造	Construct vector 
	vector<int> v1;
	vector<int> v2(10, 0);

	vector<int> v3(v2.begin(), v2.end());
	string str("hello world");
	vector<int> v4(str.begin(), str.end());
	vector<int> v5(v4);
    
	// 遍历 operator[]  迭代器iterator  循环for(size_t)  范围for(auto)
	for (size_t i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " ";
	}
	cout << endl;

	vector<int>::iterator it = v4.begin();
	while (it != v4.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	for (auto e : v5)
	{
		cout << e << " ";
	}
	cout << endl;

	// 析构
	// ~vector()

	// 赋值
	vector<int> v7(7, 0);
	vector<int> v8(8, 0);
	v8 = v7;
	v7 = vector<int>();
	cout << "Size of v7: " << int(v7.size()) << endl;
	cout << "Size of v8: " << int(v8.size()) << endl;
}

output:
0 0 0 0 0 0 0 0 0 0
104 101 108 108 111 32 119 111 114 108 100
104 101 108 108 111 32 119 111 114 108 100
Size of v7: 0
Size of v8: 7

1.2.2 reserve 与 resize

接口展示:

void test_vector2()
{
	size_t sz;
	vector<int> v;
	// v.reserve(100);
	// v.resize(100);
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

void test_vector3()
{
	vector<int> v1;
	cout << v1.max_size() << endl;

	vector<int> v;
	v.reserve(100);		// size = 0    capacity 100
	v.resize(100);		// size = 100  capacity 100

	for (int i = 0; i < 100; i++)
	{
		v[i] = i;
	}

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

output:
making v grow:
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94
capacity changed: 141
1073741823
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

1.2.3 insert、erase 与 find

接口展示:        

void test_vector4()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	// insert

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.insert(v.begin(), 0);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	// find

	auto it = find(v.begin(), v.end(), 3);
	if (it != v.end())
	{
		v.insert(it, 30);
	}

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	it = find(v.begin(), v.end(), 3);
	if (it != v.end())
	{
		v.erase(it);
	}

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	// clear & shrink_to_fit

	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.clear();			// size = 0
	v.shrink_to_fit();	// size == 0 ? capacity = 0 : capacity--

	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

output:
1 2 3 4
0 1 2 3 4
0 1 2 30 3 4
0 1 2 30 4
5
6
0
0

extra train

1. 二叉树的前序遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

// 144.二叉树的前序遍历

class Solution {
public:
    void preorder(TreeNode* root, vector<int>& v)
    {
        if(root == nullptr)
            return;
        v.push_back(root->val);
        preorder(root->left, v);
        preorder(root->right, v);
    }

    vector<int> preorderTraversal(TreeNode* root) 
    {
        vector<int> vec;
        preorder(root, vec);
        return vec;
    }
};

2. 只出现一次的数字

136. 只出现一次的数字 - 力扣(LeetCode)

//136. 只出现一次的数字

class Solution {
public:
	int singleNumber(vector<int>& nums) {
		int val = 0;
		for (auto e : nums)
		{
			val ^= e;
		}
		return val;
	}
};

3. 杨辉三角

118. 杨辉三角 - 力扣(LeetCode)     

//118. 杨辉三角

class Solution {
public:
    vector<vector<int>> generate(int numRows) 
    {
        // 二维数组
        vector<vector<int>> vv;
        vv.resize(numRows);
        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;
        }
        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的深度剖析及模拟实现

2.1 vector的深度剖析         

2.1.1 memcpy 浅拷贝问题

void reserve(size_t n)
{
	if (n > capacity())
	{	
		// 创建新的空间
		T* tmp = new T[n];
		size_t sz = size();
		if (_start)
		{
			// 拷贝数据 并 删除旧的空间
			memcpy(tmp, _start, sizeof(T) * n);	//浅拷贝
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + sz;
		_endofstorage = _start + n;
	}
}

        

        

2.2.2 iterator 迭代器失效问题        

insert 迭代器失效
void insert(iterator pos, const T& x)
{
	assert(pos >= _start);
	assert(pos <= _finish);
	if (_finish == _endofstorage)
	{
		reserve(capacity() == 0 ? 4 : capacity() * 2);
	}

	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}
	*pos = x;
	++_finish;
}

        

为了防止迭代器失效,需要我们记录_start与pos的相对位置,及时更新pos。具体的实现方法见<vector的模拟实现>

erase 迭代器失效        
void erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos <= _finish);
	vector<int>::iterator it = pos - 1;
	while (it < _finish)
	{
		*(it - 1) = *it;
		++it;
	}
	--_finish;
}

void test_vector()
{
    // 测试三组用例
	// 1 2 3 4 5        output: 1 3 5
	// 1 2 3 4 5 6      output: 崩溃
	// 2 2 3 4 5        output: 2 3 5
	std::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);

	for (auto e : v)
	{
		cout << e << " ";
	}cout << endl;

	auto it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it);
            // it = v.erase(it);
		}
		++it;
	}

	for (auto e : v)
	{
		cout << e << " ";
	}cout << endl;
}

erase后的迭代器无法正常使用,为了防止出现问题,我们可以记录erase后的迭代器并返回接受,更新原来的迭代器。具体的实现方法见<vector的模拟实现>

        

2.2 vector的模拟实现        

#pragma once
#include <assert.h>

namespace myvector
{
	// 模板
	template<class T>
	class vector
	{
	public:
		// 迭代器(反向迭代器未实现)
		typedef T* iterator;
		typedef const T* const_iterator;

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const 
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		vector()	// 注意:该构造函数不能删去,有其它函数需要使用它
		{}

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		// size_t 类型的 n
		vector(size_t n, const T& val = T())
		{// const T& val 的缺省参数最好还是给 T()
			reserve(n);	// 扩容,检查都交给reserve
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		// int 类型的 n(为了更好地匹配使用)
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		// copy构造
		vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (auto& e : v)
			{
				push_back(e);
			}
		}

		// 初始化列表
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{}

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

		void swap(vector<T>& v)
		{
			// “swap”前面加上“std::”确保调用库中的swap
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}

		// 赋值操作符重载
		vector<T>& operator=(vector<T> tmp)
		{
			// 赋值需要传参,传参调用copy构造
			// 实际上是copy构造帮助完成了赋值
			swap(tmp);
			return *this;
		}

		void push_back(const T& x)
		{
			/*if (_finish == _endofstorage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}

			*_finish = x;
			++_finish;*/
			
			// 借用 insert 
			insert(end(), x);
		}

		// []操作符重载(可读可写)
		T& operator[](size_t pos)
		{
			assert(pos < size());

			return _start[pos];
		}
		// []操作符重载(只读)
		const T& operator[](size_t pos) const
		{
			assert(pos < size());

			return _start[pos];
		}

		// 容量
		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();
				if (_start)
				{
					// 拷贝数据 并 删除旧的空间
					//memcpy(tmp, _start, sizeof(T) * n);	//浅拷贝
					for (size_t i = 0; i < sz; i++)		
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}

		//void resize(size_t n, T val = T())	两种写法是一致的
		void resize(size_t n, const T& val = T())
		{
			if (n <= size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
		}

		// 插入
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _endofstorage)
			{
				size_t len = pos - _start;	
				// reserve扩容导致迭代器pos失效(pos指向的原来的_start已经释放了)
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				// 及时更改pos
				pos = _start + len;			
			}

			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		// 删除(返回值用于解决迭代器失效的问题)
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			vector<int>::iterator it = pos - 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			return pos;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

        

extra train   

1. 只出现一次的数字 II    

137. 只出现一次的数字 II - 力扣(LeetCode)        

//137. 只出现一次的数字 II
// 数电解法
class Solution137I {
public:
	int singleNumber(vector<int>& nums)
	{
		int a = 0, b = 0;
		for (auto e : nums)
		{
			b = ~a & (b ^ e);
			a = ~b & (a ^ e);
		}
		return b;
	}
};

// 按位解法
class Solution137II {
public:
	int singleNumber(vector<int>& nums)
	{
		int ans = 0;
		for (int i = 0; i < 32; ++i)
		{
			int total = 0;
			for (auto num : nums)
			{
				// 取出num的第i个二进制位
				total += ((num >> i) & 1);  // & 按位与
			}
			if (total % 3)
			{
				ans |= (1 << i);    // | 按位或
			}
		}
		return ans;
	}
};

2. 电话号码的字母组合  

17. 电话号码的字母组合 - 力扣(LeetCode)  

// 17. 电话号码的字母组合

class Solution17 {
	const char* numStrArr[10] = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };
public:
	void Combine(const string& digits, int i, vector<string>& ret, string combineStr)
	{
		if (i == digits.size())
		{
			ret.push_back(combineStr);
			return;
		}

		int num = digits[i] - '0';
		string str = numStrArr[num];
		for (size_t j = 0; j < str.size(); j++)
		{
			Combine(digits, i + 1, ret, combineStr + str[j]);
		}
	}

	vector<string> letterCombinations(string digits)
	{
		vector<string> v;
		if (digits.empty())
		{
			return v;
		}
		string str;
		Combine(digits, 0, v, str);
		return v;
	}
};

        

        


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

φ冰霰ξ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值