前言
本期看看这位熟悉又陌生的朋友——vector。
博主水平有限,不足之处望请斧正!
是什么
vecotr
是序列容器,可变大小的数组。
*vector
有矢量、向量的意思,用其命名可能想强调“序列”这个概念。
class template
std::vector
template < class T, class Alloc = allocator<T> > class vector; // generic template
*Alloc
是空间配置器(内存池),后面讲。
怎么用
有了数据结构顺序表的基础加上string的接口使用,vector的接口基本一看就懂了。CPP文档
遍历
void t1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for(size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << ' ';
}
cout << endl;
vector<int>::iterator it = v.begin();
while(it != v.end())
{
cout << *it << ' ';
++it;
}
cout << endl;
for(auto e : v)
{
cout << e << ' ';
}
cout << endl;
}
1 2 3 4
1 2 3 4
1 2 3 4
按这个说法……vector<char>
和 string
不是一样吗?
还是没法划等号的:
- string的结尾是
\0
。 - 因意义不同,接口也有差异。
迭代器构造
void t2()
{
vector<int> v1;
v1.resize(10, 1);
for(auto e : v1) cout << e << ' ';
cout << endl;
vector<int> v2(v1.begin() + 1, v1.end() - 1);
for(auto e : v2) cout << e << ' ';
cout << endl;
}
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
需要注意,迭代器都是左闭右开的。
reserve
void t3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
cout << v.capacity() << endl;
v.reserve(10);
cout << v.capacity() << endl;
v.reserve(4);
cout << v.capacity() << endl;
}
resrve
仍然是不缩容的。缩容是有代价的,没法原地缩,只能异地开辟、拷贝、释放(涉及到内存管理)。这可以理解成是一种空间换时间。
resize
缩容 / 扩容并初始化(需要可以填数据)
void t4()
{
vector<int> v;
v.resize(20, 3);
for(auto e : v) cout << e << ' ';
cout << endl;
}
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
resize
和 reserve
一疏忽就容易用混
void t5()
{
vector<int> v;
v.reserve(10); //size没变
cout << "size:" << v.size() << endl;
cout << "capacity:" << v.capacity() << endl;
for(size_t i = 0; i < 10; ++i) v[i] = i;
for(size_t i = 0; i < 10; ++i) cout << v[i] << ' ';
cout << endl;
}
断言原因:[]
内根据size
判断是否越界。
正常情况下resize好用些。
assert
assert
也是有缺点的,在release
版本下会失效。不过都到release
这一步了,程序基本没什么触发断言的地方了。
#内置类型的构造函数
看看resize
void resize (size_type n, value_type val = value_type());
size_type
= unsigned int
value_type
= The first template parameter
val
的缺省值给了一个匿名对象,当模版参数绑定的是int
,不就等于内置类型也有构造函数了?
是的,
void t6()
{
int a = int();
int b = int(10);
cout << a << endl;
cout << b << endl;
}
0 //默认初始化为0
10
而且这是有意义的,如果缺省值给了个0,自定义类型就照顾不到了,你0不一定能赋给自定义类型,所以
内置类型也有构造函数。
#vector的扩容机制
void t7()
{
vector<int> v;
size_t size;
size = v.capacity();
cout << "vector growing...\n" << endl;
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (size != v.capacity())
{
cout << "capacity changed to " << v.capacity() << endl;
size = v.capacity();
}
}
}
vs2019下:1.5x
vector growing...
capacity changed to 1
capacity changed to 2
capacity changed to 3
capacity changed to 4
capacity changed to 6
capacity changed to 9
capacity changed to 13
capacity changed to 19
capacity changed to 28
capacity changed to 42
capacity changed to 63
capacity changed to 94
capacity changed to 141
g++:2x(2倍左右比较合适,往下太少导致频繁扩容;往上太多用不完浪费)
vector growing...
capacity changed to 1
capacity changed to 2
capacity changed to 4
capacity changed to 8
capacity changed to 16
capacity changed to 32
capacity changed to 64
capacity changed to 128
提前开空间,可以避免扩容
void t7()
{
vector<int> v;
v.reserve(100);
size_t size;
size = v.capacity();
cout << "vector growing..." << endl;
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (size != v.capacity())
{
cout << "capacity changed to " << v.capacity() << endl;
size = v.capacity();
}
}
}
vector growing...
find
void t8()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if(it != v.end())
v.insert(it, 5);
for(auto e : v) cout << e << ' ';
cout << endl;
}
1 2 5 3 4
需要注意,vector
没有find
。因为有迭代器,find
只需要用迭代器遍历找就行,所以实现一个放进<algorithm>
最方便,每个容器都能用。
swap
//1.成员函数
void swap (vector& x);
//2.非成员函数
template <class T, class Alloc>
void swap (vector<T,Alloc>& x, vector<T,Alloc>& y);
大佬真是用心良苦,怕我们用错成 swap(v1, v2)
,还专门写了个非成员函数的swap
给咱用。就算写错,也不会走算法库的深拷贝,而是走这个更匹配的。
使用方面,能讲的大概也就这些了,和string
非常像。
OJ练手
1. 只出现一次的数字
给你一个 非空 整数数组 nums
,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1]
输出:1
示例 2 :
输入:nums = [4,1,2,1,2]
输出:4
示例 3 :
输入:nums = [1]
输出:1
提示:
1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
- 除了某个元素只出现一次以外,其余每个元素均出现两次。
思路和算法:
将vector
内的数异或起来,每个位上,相同的会抵消,不同的会留下,最终ret
的32位由“不同的”组成
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int ret = 0;
for(auto e : nums) ret ^= e; //相同抵消
return ret;
}
};
2. 杨辉三角
给定一个非负整数 *numRows
,*生成「杨辉三角」的前 numRows
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:
输入: numRows = 1
输出: [[1]]
提示:
1 <= numRows <= 30
思路和算法:
杨辉三角的第n行有n个元素,除去第一个和最后一个,每一个都是上一行的元素 + 上一行左边的元素
1
1 1
1 2 1
1 3 3 1
杨辉三角:vv[i][j] = vv[i-1][j-1] + vv[i-1][j]
class Solution {
public:
vector<vector<int>> generate(int numRows)
{
//构造二维数组
vector<vector<int>> vv;
vv.resize(numRows); //vv含numRows个vector<int>
for(int i = 0; i < numRows; ++i)
{
vv[i].resize(i+1); //每一行含i+1个int
// vv[i][0] = vv[i][vv[i].size() - 1] = 1;
vv[i].front() = vv[i].back() = 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;
}
};
今天的分享就到这里了
这里是培根的blog,期待与你共同进步!
下期见~