当前位置: 首页 > news >正文

《STL 六大组件之容器探秘:深入剖析 vector》

目录

  • 一、vector 简介
  • 二、vector 的常用接口
    • 1. 构造函数(constructor function)
    • 2. 与迭代器相关的接口(iterator)
    • 3. 与容量相关的接口(capacity)
    • 4. 与访问和修改有关的接口(access、modify)
  • 三、与 vector 相关的精选习题
    • 1. 只出现一次的数字
    • 2. 杨辉三角
    • 3. 删除有序数组中的重复项
    • 4. 只出现一次的数字ii
    • 5. 只出现一次的数字iii
    • 6. 数组中出现次数超过一半的数字
    • 7. 电话号码字母组合

一、vector 简介

  vecotr 是 C++ STL 中的容器之一,用来存储同类型的多个变量,与数组很相似。但其与数组又有许多不同,vector 是一个类模版,而数组的本质是指针。且使用数组时申请多少空间则只能使用多少空间;而 vector 可以动态增容,空间随着元素的增多而增大。

二、vector 的常用接口

  下面从各个方面介绍一下 vector 容器的常用接口,并展示其用法。

1. 构造函数(constructor function)

  下面介绍 vector 常用的四个构造函数

(1)函数原型

// 1. 默认构造函数,构造一个空的 vector 对象
vector();  
// 2. 构造一个包含 n 个值为 val 的vector 对象
vector(size_type n, const value_type& val = value_type());  
// 3. 复制构造函数,构造一个和 v 一样的 vector
vector(const vector& v);
// 4. 该构造函数是一个函数模版,使用一段迭代器来构造该 vector,迭代器可以是任意类型的迭代器
template<class InputIterator>
vector(InputIterator first, InputIterator last);

(2)使用演示

// 1. constructor function
void test1()
{
	// 1. 默认构造函数
	vector<int> vt_i;
	cout << "size_1: " << vt_i.size() << endl;
	cout << "capacity_1: " << vt_i.capacity() << endl;

	// 2. n 个 val
	vector<char> vt_c(5, 'x');
	for (const auto& e : vt_c)
		cout << e;
	cout << endl;

	// 3. 复制构造函数
	vector<double> vt_d;
	// 插入数据
	for (int i = 0; i < 10; ++i)
		vt_d.push_back(i);
	// 复制构造
	vector<double> vt_d_cpy(vt_d);
	for (const auto& e : vt_d_cpy)
		cout << e << " ";
	cout << endl;

	// 4. 迭代器构造
	string str1("Go for it, everyone!");
	vector<char> vt_c_1(str1.begin() + 3, str1.end());
	for (const auto& e : vt_c_1)
		cout << e;
	cout << endl;
}

(3)运行结果
在这里插入图片描述

2. 与迭代器相关的接口(iterator)

  下面简单介绍 vector 常用的 4 个迭代器接口及其用法,主要分为正序迭代器和反序迭代器。

(1)函数原型

// 1. 正序迭代器
iterator begin();
iterator end();
// 2. 反序迭代器
reverse_iterator rbegin();
reverse_iterator rend();

// const 迭代器
// 为 const vector 对象服务
const_iterator begin() const;
const_iterator end() const;
const_reverse_begin() const;
const_reverse_end() const;

(2)使用演示

// 2. iterator
void test2()
{
	vector<int> vt_i;
	// 插入数据
	for (int i = 1; i < 10; ++i)
		vt_i.push_back(i);
	// 正序遍历
	cout << "正序遍历: ";
	vector<int>::iterator it = vt_i.begin();
	while (it != vt_i.end())
	{
		// 打印当前迭代器
		cout << *it << " ";
		// 下一个
		++it;
	}
	cout << endl;
	// 逆序遍历
	cout << "逆序遍历: ";
	vector<int>::reverse_iterator rit = vt_i.rbegin();
	while (rit != vt_i.rend())
	{
		// 打印当前迭代器
		cout << *rit << " ";
		// 下一个
		++rit;
	}
	cout << endl;
}

(3)运行结果
在这里插入图片描述

3. 与容量相关的接口(capacity)

  下面介绍 vector 常用的 5 个与容量有关的接口

(1)函数原型

// 1. 返回当前 vector 对象的 size
size_type size() const;
// 2. 返回当前 vector 对象的 capacity
size_type capacity() const;
// 3. 判断当前 vector 对象是否为空
bool empty() const;
// 4. 改变当前 vector 对象的 size
// 如果 n < size,则缩减 size;如果 n > size,则把新增得元素设置为 val;
// 当然,如果 n > capacity,则会进行增容
void resize(size_type n, value_type val = value_type());
// 5. 改变当前对象的 capacity
// 重新开辟一个大小为 n 的空间,把原来的数据拷贝到新的空间(拷贝不了的数据舍弃),释放旧空间;
void reserve(size_type n);

(2)使用演示

// 3. capacity
void test3()
{
	vector<int> vt_i;
	// 插入数据,同时并观察 vector 对象在 VS 环境下 capacity 的增长情况
	cout << "修改前" << endl;
	for (int i = 0; i < 20; ++i)
	{
		vt_i.push_back(i);
		cout << "capacity: " << vt_i.capacity() << endl;
	}
	cout << "size: " << vt_i.size() << endl << endl;
	// 改变当前 vector 对象的 capacity
	cout << "修改 capacity 后" << endl;
	vt_i.reserve(50);
	cout << "capacity: " << vt_i.capacity() << endl;
	cout << "size: " << vt_i.size() << endl << endl;
	// 改变当前对象的 size
	cout << "修改 size 后" << endl;
	vt_i.resize(25, 0);
	cout << "capacity: " << vt_i.capacity() << endl;
	cout << "size: " << vt_i.size() << endl << endl;
	// 打印当前 vector 对象
	for (const auto& e : vt_i)
		cout << e << " ";
	cout << endl;
}

(3)运行结果
在这里插入图片描述

4. 与访问和修改有关的接口(access、modify)

  下面介绍 vector 的 6 个与访问和修改有关的接口

(1)函数声明

// 1. 下标运算符重载(分普通 vector 对象和 const vector 对象)
reference operator[](size_type n);
const reference operator[](size_type n) const;
// 2. 尾插、尾删
void push_back(const value_type& val);
void pop_back();
// 3. 插入、删除
iterator insert(iterator position, const value_type& val);
iterator erase(iterator position);
// 4. 交换两个 vector 对象
void swap(vector& v);

(2)使用演示

// 4. access、modify
void test4()
{
	vector<int> vt_i;
	// 使用尾插插入数据插入数据
	for (int i = 1; i <= 10; ++i)
		vt_i.push_back(i);
	// 使用下标运算符访问 vector 对象中的元素
	for (int i = 0; i < 10; ++i)
		cout << vt_i[i] << " ";
	cout << endl;
	// 使用尾删删除数据
	for (int i = 0; i < 5; ++i)
		vt_i.pop_back();
	// 打印尾删后的 vector 对象
	for (int i = 0; i < 5; ++i)
		cout << vt_i[i] << " ";
	cout << endl;
	// 插入数据
	vt_i.insert(vt_i.begin(), 0);
	vt_i.insert(vt_i.end(), 6);
	vt_i.insert(vt_i.begin() + 3, 99);
	for (const auto& e : vt_i)
		cout << e << " ";
	cout << endl;
	// 删除数据
	vt_i.erase(vt_i.begin() + 3);
	vt_i.erase(vt_i.end() - 1);
	vt_i.erase(vt_i.begin());
	for (const auto& e : vt_i)
		cout << e << " ";
	cout << endl;
}

(3)运行结果
在这里插入图片描述

三、与 vector 相关的精选习题

1. 只出现一次的数字

题目链接:link

题目描述:

  1. 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

  2. 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例:
在这里插入图片描述

提示:
在这里插入图片描述

(1)解题思路
  两个相同的数异或为 0,而任何数与 0 异或不变。把 vector 中的所有数异或,则最终的结果就是 vector 中只出现一次的数。

(2)解题代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        // 把 vector 对象 nums 中的所有元素异或
        // 最终的结果为 nums 中只出现一次的数
        int ret = 0;
        for (const auto& e : nums)
            ret ^= e;
        // 返回
        return ret;
    }
};

2. 杨辉三角

题目链接:link

题目描述:
在这里插入图片描述

示例:
在这里插入图片描述

提示:
在这里插入图片描述

(1)解题思路
  把每一行的第一个元素对齐成一列,则所求杨辉三角其实是一个等腰三角形。第一列的元素和正对角线的元素均为 1,其他元素均为其正上方的元素和左上角的元素之和。
注意:这里需要使用 resize 来调整 vector 的 size,这样可以使用下标运算符来访问每个元素。

(2)解题代码

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        // 创建二维 vector
        vector<vector<int>> vv_i;
        // 根据输入的行数确定二维 vector 的大小
        vv_i.resize(numRows);
        // 给二维 vector 中的每个一维 vector 确定大小
        // 并设置每个一维 vector 中元素的值
        for (int i = 0; i < numRows; ++i)
        {
            // 设置每行的大小
            vv_i[i].resize(i+1);
            // 设置每行的数据
            for (int j = 0; j < i + 1; ++j)
            {
                if (0 == j || i == j)
                    vv_i[i][j] = 1;
                else
                    vv_i[i][j] = vv_i[i-1][j-1] + vv_i[i-1][j];
            }
        }
        // 返回
        return vv_i;
    }
};

3. 删除有序数组中的重复项

题目链接:link

题目描述:在这里插入图片描述

示例:
在这里插入图片描述

提示:
在这里插入图片描述

(1)解题思路
  使用前后指针法(pre 和 cur),pre 不动,cur 往后走,当 cur 和 pre 所指对象不同时,++pre 后与 cur 交换。重复上面的步骤,直到 cur 走到末尾。最终 pre 所在位置减去 nums 的初始位置的差值即所求 k 值。

(2)解题代码

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        // 前后指针
        int pre = 0, cur = 0;
        // cur 往前走,当 cur 和 pre 所指对象不同时交换
        while (cur < nums.size())
        {
            if (nums[cur] != nums[pre])
                swap(nums[cur], nums[++pre]);
            // 下一个
            ++cur;
        }
        // 返回
        return pre + 1;
    }
};

4. 只出现一次的数字ii

题目链接:link

题目描述:
在这里插入图片描述

示例:
在这里插入图片描述

提示:
在这里插入图片描述

(1)解题思路
  由于除了一个数之外,其他的数都出现三次。那么在 32 位的 int 中,为 1 的位出现的次数应该为 3n 或者 3n+1,当出现次数为 3n+1 时,则该位就是只出现一次的数字的为 1 位。当然,有可能为 0,所以最后还原的时候使用数字 0 来还原该数。

(2)解题代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        // 存储 1 出现的次数
        int times_one[32] = {0};
        for (const auto& e : nums)
        {
            for (int i = 0; i < 32; ++i)
            {
                if ((e >> i) & 1)
                    ++times_one[i];
            }
        }
        // 根据为 1 位的出现次数还原该数
        int target = 0;
        for (int i = 0; i < 32; ++i)
        {
            if (times_one[i] % 3)
                target |= (1 << i);
        }
        // 返回
        return target;
    }
};

5. 只出现一次的数字iii

题目链接:link

题目描述:在这里插入图片描述

示例:
在这里插入图片描述

提示:
在这里插入图片描述

(1)解题思路
  把数组中的数全部异或,出现两次的数全部异或为 0,得到的结果就是只出现一次的两个数异或的结果。异或的规则是相同为 0,相异为 1,找出异或结果中为 1 的位,根据该位为 0 还是为 1 分成两组数,这两组数分别异或得到的结果就是只出现一次的两个数。

(2)解题代码

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        // 所有元素异或,得到的结果是两个只出现一次的数的异或
        int result = 0;
        for (const auto& e : nums)
            result ^= e;
        // 找出异或结果中为 1 的位
        int i = 0;
        for (i = 0; i < 32; ++i)
        {
            if ((result >> i) & 1)
                break;
        }
        // 以该位为分界线,为 1 一组,为 0 一组
        // 分别异或,最终的结果就是所求的两位只出现一次的数
        int ret1 = 0, ret2 = 0;
        for (const auto& e : nums)
        {
            if ((1 << i) & e)
                ret1 ^= e;
            else
                ret2 ^= e;
        }
        // 把结果插入 vector 对象
        vector<int> ret;
        ret.push_back(ret1);
        ret.push_back(ret2);
        // 返回
        return ret;
    }
};

6. 数组中出现次数超过一半的数字

题目链接:link

题目描述:
在这里插入图片描述

示例:
在这里插入图片描述

(1)解题思路
  由于目标数字的出现次数超过该数组长度的一般,那么把该数组进行排序,排序过后的数组的中间元素就是目标数字。

(2)解题代码

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int>& numbers) {
        // 调用标准库中的 sort 函数进行排序
        sort(numbers.begin(), numbers.end());
        // 返回该数组的中间元素
        return numbers[numbers.size() / 2];
    }
};

7. 电话号码字母组合

题目链接:link

题目描述:
在这里插入图片描述

示例:
在这里插入图片描述

提示:
在这里插入图片描述

(1)解题思路

  1. 本题需要先创建一个字符串数组 digit_to_str,对应数字 2-9 所对应的字符组合;
  2. 根据输入的数字组合获得对应的字母组合,并尾插到一个空的 vector<string> cur_combi中;
  3. 重新设计一个递归函数,该函数中包含一个循环,该循环遍历 cur_combi 中的第一个 string 的所有字符,然后递归遍历第二个 string 的所有字符并和第一个 string 中的字符拼接,直到达到 cur_combi 的末尾,最后把拼接好的字符串存放到需要返回的 vector<string> ret 中。
  4. 本题的解题思路作者描述的不是很好,大家结合着解题代码一起看基本上应该就能看懂,实际上就是 for 循环里放了个递归

(2)解题代码


// 创建一个全局的字符串数组
const char char_combi[10][5] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

class Solution {
public:
    void recursion(vector<string>& ret, const vector<string>& cur_combi, int i, int end, string cur)
    {
        if (i < end)
        {
            // 遍历当前 string 的每个字符
            for (const auto& e : cur_combi[i])
            {
                // 递归
                recursion(ret, cur_combi, i+1, end, cur + e);
            }
        }
        else  // 尾插到 ret 中
        {
            ret.push_back(cur);
        }
    }

    vector<string> letterCombinations(string digits) {
        // 如果输入数字组合为空
        if ("" == digits)
            return vector<string>();
        // 获得当前数字组合对应的字母组合
        vector<string> cur_combi;
        for (const auto& e : digits)
            cur_combi.push_back(char_combi[e-'0']);
        // 通过递归函数进行递归获取字符串组合
        vector<string> ret;
        recursion(ret, cur_combi, 0, cur_combi.size(), "");
        // 返回
        return ret;
    }
};

相关文章:

  • Python | 计算散度
  • TF-IDF算法
  • scss预处理器对比css的优点以及基本的使用
  • 瑞盟MS35774/MS35774A低噪声 256 细分微步进电机驱动
  • 【动态规划】最长公共子序列问题 C++
  • 深入理解与使用 HashedWheelTimer:高效的时间轮定时器
  • python argparse 参数使用详解记录
  • Qt 制作验证码
  • SQL语句---特殊查询
  • 递归,搜索,回溯算法(一)
  • 多版本PHP开发环境配置教程:WAMPServer下MySQL/Apache/MariaDB版本安装与切换
  • ubuntu下docker 安装 graylog 6.1
  • HTML输出流
  • WebMvcConfigurer 的 addResourceLocations
  • Eplan许可管理的自动化工具
  • [Vue2]指令修饰符(一)
  • [问题收集]mysql主从分离过程中,数据不同步可能导致的后果以及应对策略
  • NFC 智能门锁全栈解决方案:移动端、服务器、Web 管理平台
  • src案例分享-逻辑漏洞
  • 软路由用联想j3710主板踩坑
  • 广州下调个人住房公积金贷款利率
  • 美联储主席:不打算先发制人地降息,将继续观望
  • “子宫内膜异位症”相关论文男性患者样本超六成?福建省人民医院发布情况说明
  • 综合治理食品添加剂滥用问题,国务院食安办等六部门联合出手
  • 中标多家学校采购项目的App查成绩需付费?涉事公司回应
  • 争抢入境消费红利,哪些城市有潜力?