【c++ 学习笔记】vector 介绍

🙊 vector 的介绍及使用 🙊

💖 vector 的介绍

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

💖 vector 的使用

💖 vector 的结构

vector 的结构如下:

template < class T, class Alloc = allocator<T> > class vector; // generic template

class Alloc = allocator 是一个空间配置器,第一个模板参数就是 vector 的数据类型。

💖 vector 的成员函数介绍

vector 一共有 6 个默认的成员函数分别是

1、迭代器

在这里插入图片描述
2、capacity 相关的成员函数

在这里插入图片描述
3、访问数据相关的成员函数

在这里插入图片描述
4、修改相关的成员函数

在这里插入图片描述
vectorstring 的区别是 string 只管理字符类型的数组,而 vector 可以管理任意类型的数组。

💖 vector 的常用接口

💖 vector 构造函数

vector 的常用接口如下:

在这里插入图片描述

其中第一个是全缺省的函数接口,使用全缺省可以用自己写的空间配置器传参,一般不需要管,只将其当作无参的函数即可。
第二个是用 n 个 value 初始化,value_type 是 vector 里面存的数据类型,size_type 是一个无符号整型。
第三个是以一段迭代器区间初始化。
第四个是拷贝构造

💖 vector 的析构函数

析构函数自动调用,对里面的空间进行释放。

~vector();

💖 vector 的赋值

赋值完成的是一个深拷贝。

vector& operator= (const vector& x);

💖 insert 用法

vector 使用迭代器去实现 insert,在某个位置插入 val,插入一个迭代器区间等。

在这里插入图片描述

💖 erase 用法

erase 也是使用迭代器实现
在这里插入图片描述

💖 vector 常见的遍历方式

如下使用了三种方式遍历 vector

void test_vector1()
{
	//类模板的显示实例化,调用无参的构造函数创建 v 对象
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//遍历 vector
	//使用 operator[]
	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
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

int main()
{
	test_vector1();
	return 0;
}

拷贝构造函数的使用方法如下:

vector<int> copy(v);
	for (auto e : copy)
	{
		cout << e << " ";
	}
	cout << endl;

也可以使用 n 个 value 进行构造:

explicit vector (size_type n, const value_type& val = value_type();

使用方式如下:

void test_vector2()
{
	//使用10个1对v1进行初始化
	vector<int> v1(10, 1);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

还可以使用迭代器区间构造,这里的迭代器是一个模板,除了传 vector 容器的迭代器之外,还可以传其他容器的迭代器,

template <class InputIterator>
         vector (InputIterator first, InputIterator last,
                 const allocator_type& alloc = allocator_type());

使用方式如下:

void test_vector2()
{
	//使用10个1对v1进行初始化
	vector<int> v1(10, 1);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
	vector<int> v2(v1.begin(), v1.end());
	for (auto e : v2)
	{
		cout << e << " ";
	}
	cout << endl;

	//只要类型匹配,就是string容器里面的数据能够存到vector容器里面,就可以这样使用
	//这里string里面存的是char类型的数据,而char类型可以赋值给int类型
	string s1("hello world");
	vector<int> v3(s1.begin(), s1.end());
	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;
}

也可以用迭代器实现反向遍历

void test_vector3()
{
	//类模板的显示实例化,调用无参的构造函数创建 v 对象
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//reverse_iterator 是通过类模板实例化一个类的反向迭代器类型
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
	}
	cout << endl;
}

💖 vector 空间增长问题

  1. capacity 的代码在 vs 和 g++ 下分别运行会发现,vs 下 capacity 是按 1.5 倍增长的,g++ 是按 2 倍增长的。这个问题经常会考察,不要固化的认为,vector 增容都是 2 倍,具体增长多少是根据具体的需求定义的。vsPJ 版本 STLg++SGI 版本 STL
  2. reserve 只负责开辟空间,如果确定知道需要用多少空间,reserve 可以缓解 vector 增容的代价缺陷问题。
  3. resize 在开空间的同时还会进行初始化,影响 size

测试代码如下:

// 测试vector的默认扩容机制
void TestVectorExpand()
{
 size_t sz;
 vector<int> v;
 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';
 }
 }
}
vs:运行结果:vs下使用的STL基本是按照1.5倍方式扩容
making foo 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
g++运行结果:linux下使用的STL基本是按照2倍方式扩容
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128

💖 resize 的用法

resize 就是开空间加初始化

void test_vector4()
{
	vector<int> v;
	v.resize(10, 0);
}

🙊 vector 例题 🙊

💖 只出现一次的数字

题目描述如下:

在这里插入图片描述
代码如下:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for(auto e:nums)
        {
            //用异或的方式,任何数与0异或就是任何数
            ret ^= e;
        }
        return ret;
    }
};

💖 杨辉三角

给定一个非负整数 numRows,生成 「杨辉三角」 的前 numRows 行。
「杨辉三角」 中,每个数是它左上方和右上方的数的和。

在这里插入图片描述
在这里插入图片描述

注意这里对 vector < vector < int >> 进行相关解释说明:

在这里插入图片描述
代码如下:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        //定义一个对象vv
        vector<vector <int>> vv;
        //需要 numRows 行,每一行的每一个对象是 vector<int>
        //所以这里开辟了 numRows 个空间,用匿名对象 vector<int>() 进行初始化
        vv.resize(numRows,vector<int>());
        //这里的 vv.size() 代表这个杨辉三角一共有多少行。用下标 i 来控制每一行
        for(size_t i = 0; i < vv.size(); ++i)
        {
            //每一行的数据有 i+1 个
            vv[i].resize(i+1,0);
            //每一行的第一个和最后一个改成1
            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;
    }
};

💖 电话号码的字母组合

给定一个仅包含数字 2 - 9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

在这里插入图片描述
在这里插入图片描述可以用递归进行问题的求解,加入输入的数字组合是 “2 3 7”,根据数字对应的字母进行组合,类似于二叉树的深度遍历,大致思路图解如下:
在这里插入图片描述代码如下:

class Solution {
    //首先通过数字可以找到对应的字符串
    string _numToStr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
	  //因为用到递归,所以这里还需要另外写一个递归子函数	
    //这里digits是数字串,如”237“,di 是数字串第几个字符,数字串走到第几个字符就说明递归到第几层
    //combinestr是组合出来的字符串,strV用来保存组合字符串
    void Combinations(const string& digits, size_t di, string combineStr, vector<string>& strV)
    {
    		 //当走完数字串,停止本次递归并返回
        if(di == digits.size())
        {
        		//组合出来的字符串在combineStr里面,将其存入strV中
            strV.push_back(combineStr);
            return;
        }
        //num是指当前走到数字串的第几个数字,对应组合字符串的第几层
        int num = digits[di]-'0';
        //取当前层的字符串,如第一层数字2对应的字符串就是“abc”
        string str = _numToStr[num];
        //进行排列组合
        //依次取str里面的每个字母,和上一层传过来的字母进行组合,组合好以后传给下一层再继续组合
        for(auto ch:str)
        {
            Combinations(digits,di+1,combineStr+ch,strV);
        }
    }
    vector<string> letterCombinations(string digits) {
        vector<string> strV;
        if(digits.size() == 0)
        {
            return strV;
        }
        Combinations(digits,0,"",strV);
        return strV;
    }
};
class Solution {
    string _numToStr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
    void Combinations(const string& digits, size_t di, string combineStr, vector<string>& strV)
    {
        if(di == digits.size())
        {
            strV.push_back(combineStr);
            return;
        }
        int num = digits[di]-'0';
        string str = _numToStr[num];
        for(auto ch:str)
        {
            Combinations(digits,di+1,combineStr+ch,strV);
        }
    }

递归展开图如下:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值