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

简介: vector的使用及经典题目

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

正文开始@边通书

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

<img src=" title="">

1. (constuctor) & (destuctor)

:heart: 构造函数

(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/比较大小/+=

<img src=" title="">

:heart: 析构函数

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

2. 增删查改

2.0 reserve & resize

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

:heart: 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倍增长 ——

<img src=" title="">

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

<img src=" title="">

2.1 push_back & pop_back

尾插尾删。

2.2 find

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

<img src=" title="">

字符串中有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;

运行结果 ——

<img src=" title="">

3.2 迭代器 & 范围for

:heart: iterator 内嵌类型

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

运行结果 ——

<img src=" title="">

支持迭代器就支持范围for:candy:

    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 求余,结果则为只出现一次的数字。妙啊太妙了!

<img src=" title="">

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>就简洁很多。

<img src=" title="">

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)

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

<img src=" title="">

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;
    }
};

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

<img src=" title="">

持续更新~@边通书

相关文章
|
3月前
|
存储 编译器 C++
【C++】vector介绍+模拟实现
【C++】vector介绍+模拟实现
|
1月前
|
存储 C++ 索引
【C++打怪之路Lv9】-- vector
【C++打怪之路Lv9】-- vector
21 1
|
1月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化2
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
64 6
|
1月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化1
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
60 7
|
1月前
|
编译器 C++
【C++】—— vector模拟实现
【C++】—— vector模拟实现
|
1月前
|
编译器 C语言 C++
【C++篇】解密 STL 动态之魂:全面掌握 C++ vector 的高效与优雅
【C++篇】解密 STL 动态之魂:全面掌握 C++ vector 的高效与优雅
51 3
|
1月前
|
C++
【C++】C++ STL探索:Vector使用与背后底层逻辑(三)
【C++】C++ STL探索:Vector使用与背后底层逻辑
|
1月前
|
编译器 Linux C++
【C++】C++ STL探索:Vector使用与背后底层逻辑(二)
【C++】C++ STL探索:Vector使用与背后底层逻辑
|
1月前
|
编译器 C++ 容器
【C++】C++ STL探索:Vector使用与背后底层逻辑(一)
【C++】C++ STL探索:Vector使用与背后底层逻辑
|
1月前
|
算法 C++ 容器
C++之打造my vector篇(下)
C++之打造my vector篇(下)
28 0