【C++】vector的使用及经典题目解题报告@STL


由于详细学习了string类,vector的学习成本很低。下面提出 重点进行验证,必要时直接查文档即可(连我自己都不看自己写的这哈哈)。关于vector的灵活运用,还要看题目,比如4.6运用了vector<vector>创建一个m行n列的二维数组等等。关于迭代器失效问题,在模拟实现时已详谈。

正文开始@边通书

vector是动态增长的顺序容器。

1. (constuctor) & (destuctor)

❤️ 构造函数

(constructor)接口说明
vector()无参构造
vector(const vector& x)拷贝构造
vector (size_type n, const value_type& val = value_type())构造并初始化n个val(value_type即T)少
vector (InputIterator first, InputIterator last)用迭代器区间初始化

演示,调试可观察:

	vector<int> v1; //无参构造
	vector<int> v2(10); // 构造10个,val
	vector<int> v3(++v2.begin(), --v2.end()); //迭代器区间初始化
	vector<int> v4(v2); //拷贝构造

思考,string类可以用vector代替吗?不可以,因为有\0,且要提供一系列字符串专有的接口,如c_str/比较大小/+=

❤️ 析构函数

不用管,自动调用析构释放资源。

2. 增删查改

2.0 reserve & resize

Capacity说明
reserve扩容
resize扩容+初始化 / 删除数据(capacity不变,防止抖动)
  • resize初始化可以自己指定值,也可以使用类型的缺省值。如int类型的缺省值为0、指针类型缺省值为nullptr

❤️ vector是如何增容的?

#include<iostream>
#include<vector>

using namespace std;

int main()
{
	size_t sz;
	vector<int> foo;
	sz = foo.capacity();
	cout << "making foo grow:\n";
	for (int i = 0; i<100; ++i) 
	{
		foo.push_back(i);
		if (sz != foo.capacity()) 
		{
			sz = foo.capacity();
			std::cout << "capacity changed: " << sz << '\n';
		}
	}
}

在vs下,大约是1.5倍增长 ——

g++以标准的2倍增长 ——

2.1 push_back & pop_back

尾插尾删。

2.2 find

vector类中并没有find,这是因为算法库中就提供了一个模板函数,在迭代器区间中查找(左闭右开),若查找到了,就返回迭代器;没找到就返回s.end()

字符串中有find,是因为不仅要查字符,还要查子串。

	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())
	{
		cout << "找到了!" << *it << endl;
	}

2.3 insert & erase

可头插头删。

Modifiers说明
insert在position(迭代器)之前插入val
erase删除position(迭代器)位置的数据

insert & erase 可以搭配 find 使用。string类的insert和erase通常支持下标,因为它find也刚好返回下标。

	// 头插
	v.insert(v.begin(), 0);

3. 遍历

先尾插一下1,2,3,4.

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

3.1 []

	// 遍历 - 可读可写
	for (size_t i = 0; i < v.size(); i++)
	{
		v[i]++;
		cout << v[i] << " ";
	}
	cout << endl;

运行结果 ——

3.2 迭代器 & 范围for

❤️ iterator 内嵌类型

	// 遍历 - 可读可写
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		*it -= 1;
		cout << *it << " ";
		it++;
	}
	cout << endl;

运行结果 ——

支持迭代器就支持范围for🍬

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

4. 解题报告

4.1 只出现一次的数字i

题目链接:136. 只出现一次的数字 - 力扣(LeetCode) (leetcode-cn.com)

著名的单身狗问题,可查看我《剑指offer》专栏。

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

4.2 只出现一次的数字ii

题目链接:137. 只出现一次的数字 II - 力扣(LeetCode) (leetcode-cn.com)

思路:考虑数字的二进制形式,对于出现三次的数字,各 二进制位 出现的次数都是 3 的倍数。因此,统计所有数字的二进制位中 1 的出现次数,并对 3 求余,结果则为只出现一次的数字。妙啊太妙了!

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        size_t sum = 0; // 统计个二进制位中1的个数
        for(size_t i = 0; i<32; i++)
        {
            sum = 0;
            for(auto e : nums)
            {
                sum += (e>>i) & 1;
            }
            sum %= 3;
            ret += (sum<<i);
        }
        return ret;       
    }
};

4.3 只出现一次的数字iii

题目链接:260. 只出现一次的数字 III - 力扣(LeetCode) (leetcode-cn.com)

著名的两只单身狗的故事。

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        // 1.把所有数字异或在一起
        int ret = 0;
        for(auto e : nums)
        {
            ret ^= e;
        }

        // 2.找到这两个单身狗数字不同的一位(二进制)
        size_t pos = 0;
        while(pos < 32)
        {
            if((ret >> pos) & 1 == 1)
                break;
            pos++;
        }

        // 3.分组,边分组边异或
        vector<int> retArr(2);
        for(size_t i = 0;i < nums.size();i++)
        {
            if((nums[i] >> pos)&1 == 1)
            {
                retArr[0] ^= nums[i];
            }
            else
            {
                retArr[1] ^= nums[i];
            }
        }
        return retArr;
    }
};

4.4 删除有序数组中的重复项

题目链接:26. 删除有序数组中的重复项 - 力扣(LeetCode) (leetcode-cn.com)

这题顺序表那儿就做过,可查看我数据结构经典题目专栏。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int dst = 0;
        int i = 0;
        int j = 1;
        while(i < nums.size())
        {
            while(j < nums.size() && nums[i] == nums[j])
            {
                j++;
            }
            nums[dst] = nums[i];
            dst++;
            i = j;
        }
        return dst;
    }
};

4.5 杨辉三角OJ

题目链接:118. 杨辉三角 - 力扣(LeetCode) (leetcode-cn.com)

在C语言中,动态开辟创建一个m行n列的二维数组相当麻烦,释放也很麻烦。有了vector<vector>就简洁很多。

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        vv.resize(numRows);
        // 1.开空间 + 初始化
        for(size_t i = 0; i < numRows ;i++ )
        {
            vv[i].resize(i+1);
            vv[i][0] = vv[i][i] = 1;
        }

        // 2.二维数组遍历
        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;
    }
};

4.6 电话号码组合

题目链接:17. 电话号码的字母组合 - 力扣(LeetCode) (leetcode-cn.com)

这是一道多路递归的深度遍历,简单的映射和回溯。递归深度取决于数字串长度;数字映射到的字符串多长,就是几路递归。

class Solution {
public:
    // 单参数构造
    string arr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

    void _letterCombinations(string& digits,size_t i,string comStr,vector<string>& v)
    {
        if(i == digits.size())
        {
            v.push_back(comStr);
            return ;
        }

        string str = arr[digits[i]-'0'];
        for(size_t j = 0; j < str.size(); j++)
        {
            _letterCombinations(digits,i+1,comStr + str[j],v);
        }
    }

    vector<string> letterCombinations(string digits) {
        vector<string> ret;
        string comStr;
        if(digits.empty())
            return ret;
        _letterCombinations(digits, 0, comStr,ret);
        return ret;
    }
};

注意:

  • 建立映射:不需要vector,因为是定长的,不用动态增长的。并且可以像string("abc")这样初始化,因为是单参数的构造函数支持隐式类型转换。

  • 借助comstr拼这个串儿。注意,上述代码不传引用,若传引用,则回过来时需要pop_back,如下。

class Solution {
public:
    // 单参数构造
    string arr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

    void _letterCombinations(string& digits,size_t i,string comStr,vector<string>& v)
    {
        if(i == digits.size())
        {
            v.push_back(comStr);
            return ;
        }

        string str = arr[digits[i]-'0'];
        for(size_t j = 0; j < str.size(); j++)
        {
            _letterCombinations(digits,i+1,comStr + str[j],v);
        }
    }

    vector<string> letterCombinations(string digits) {
        vector<string> ret;
        string comStr;
        if(digits.empty())
            return ret;
        _letterCombinations(digits, 0, comStr,ret);
        return ret;
    }
};

不清晰的可以去画递归展开图,在脑子里递也是蛮有感觉的。

持续更新~@边通书

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浮光 掠影

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

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

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

打赏作者

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

抵扣说明:

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

余额充值