C++入门-vector当中的重要知识点

大家好啊,这几天憋了个大的,现在我们就来聊聊C++中的vector的那些重要知识点,那么开始发车喽!
在这里插入图片描述

1.1 vector的介绍

在C++标准库中,std::vector是一个非常常用的动态数组容器,它提供了动态大小的数组功能,可以根据需要动态增加或减少数组的大小。std::vector是一个模板类,可以存储任意类型的元素,类似于数组,但具有更多的功能和灵活性。

特点:

  • 动态大小std::vector可以根据需要动态调整大小,无需在编译时指定数组大小。
  • 随机访问:通过下标可以快速访问std::vector中的元素,支持常数时间复杂度的随机访问。
  • 尾部插入和删除:可以在std::vector的尾部高效地插入和删除元素。
  • 迭代器支持:提供了迭代器接口,支持遍历容器中的元素。
  • 内存自动管理std::vector会自动管理动态内存分配和释放,避免了手动管理内存的麻烦。

下面我们通过演示代码来展示std::vector的基本用法:

#include <iostream>
#include <vector>

int main() {
    // 创建一个空的vector
    std::vector<int> vec;

    // 向vector中添加元素
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // 遍历vector并输出元素
    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

在上面的示例中,我们首先创建了一个空的std::vector<int>对象vec,然后使用push_back方法向vec中添加了三个整数元素,最后使用循环遍历vec并输出元素。这也是std::vector的基本用法。

1.2.1 vector的定义

在本节中,我们将重点讨论std::vector的构造函数和拷贝构造函数。

vector的构造函数

vector()
  • 功能:默认构造函数,创建一个空的std::vector对象。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    // 使用默认构造函数创建一个空的vector
    std::vector<int> vec;

    // 判断vector是否为空
    if (vec.empty()) {
        std::cout << "Vector is empty" << std::endl;
    }

    return 0;
}
  • 注释:使用默认构造函数可以创建一个空的std::vector对象,然后可以通过其他方法向其中添加元素。

vector的拷贝构造函数

vector (const vector& x)
  • 功能:拷贝构造函数,创建一个新的std::vector对象,其内容与参数x相同。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    // 创建一个包含元素的vector
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用拷贝构造函数创建一个新的vector
    std::vector<int> newVec(vec);

    // 输出新的vector内容
    for (int num : newVec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}
  • 注释:拷贝构造函数会创建一个新的std::vector对象,其内容与参数x相同,但是是独立的对象,对其中一个对象的修改不会影响另一个对象。

1.2.2 vector iterator 的使用

在C++中,std::vector提供了一组迭代器(iterator)来访问容器中的元素。迭代器是一种类似指针的对象,可以用于遍历容器中的元素。在本节中,我们将重点讨论begin()end()函数,这两个函数返回指向第一个元素和最后一个元素之后位置的迭代器。

使用begin()end()函数

begin()
  • 功能:返回指向容器中第一个元素的迭代器。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用begin()函数获取第一个元素的迭代器
    std::vector<int>::iterator it = vec.begin();

    // 输出第一个元素
    std::cout << "First element: " << *it << std::endl;

    return 0;
}
  • 注释begin()函数返回一个迭代器,指向容器中的第一个元素。
end()
  • 功能:返回指向容器中最后一个元素之后位置的迭代器。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用end()函数获取最后一个元素之后位置的迭代器
    std::vector<int>::iterator it = vec.end();

    // 输出最后一个元素之后位置的值
    std::cout << "Value after the last element: " << *it << std::endl;

    return 0;
}
  • 注释end()函数返回一个迭代器,指向容器中最后一个元素之后的位置。

1.2.3 vector 空间增长问题

我们知道,在C++中,std::vector是一个动态数组,它可以自动调整存储空间的大小以容纳新元素。在本节中,我们将重点讨论resize()reserve()函数,这两个函数用于管理std::vector的存储空间。

使用resize()reserve()函数

resize()
  • 功能:改变std::vector的大小,如果新大小大于当前大小,则在末尾插入默认构造的元素;如果新大小小于当前大小,则删除多余的元素。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 改变vector的大小为5,多出的元素用0填充
    vec.resize(5);

    // 输出所有元素
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}
  • 注释resize()函数可以用来调整std::vector的大小,并在需要时填充默认值。
reserve()
  • 功能:为std::vector预留存储空间,但不改变std::vector的大小。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 预留存储空间为10个元素
    vec.reserve(10);

    // 输出当前存储空间大小
    std::cout << "Capacity after reserving: " << vec.capacity() << std::endl;

    return 0;
}
  • 注释reserve()函数可以用来预留存储空间,避免频繁重新分配内存。

1.2.4 vector 增删查改

我们知道,在C++中,std::vector是一个动态数组,提供了丰富的操作函数来实现元素的增删查改。本节将重点讨论push_backpop_backoperator[]这三个函数的用法。

push_back()

  • 功能:在std::vector的末尾添加一个元素。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 在末尾添加元素4
    vec.push_back(4);

    // 输出所有元素
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}
  • 注释push_back()函数可以用来向std::vector中添加新元素。

pop_back()

  • 功能:删除std::vector的末尾元素。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 删除末尾元素
    vec.pop_back();

    // 输出所有元素
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}
  • 注释pop_back()函数可以用来删除std::vector的末尾元素。

operator[]

  • 功能:通过下标访问std::vector中的元素。
  • 示例代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 访问第二个元素
    std::cout << "Element at index 1: " << vec[1] << std::endl;

    return 0;
}
  • 注释operator[]可以用来通过下标直接访问std::vector中的元素。

1.2.5 vector 迭代器失效问题

在使用C++中的std::vector时,需要注意迭代器失效的问题。当对std::vector进行插入或删除操作时,可能会导致迭代器失效,进而引发未定义行为。本节将重点讨论vector迭代器失效问题以及如何避免这种情况。

迭代器失效问题

std::vector中,当进行插入或删除操作时,会导致内存重新分配,原有的迭代器可能会指向无效的内存位置,从而造成程序崩溃或产生不可预测的结果。

避免迭代器失效的方法

  1. 使用索引代替迭代器:在插入或删除元素后,尽量避免继续使用之前的迭代器,可以通过索引来访问元素,因为索引不会失效。

  2. 重新获取迭代器:在每次插入或删除操作后,重新获取需要使用的迭代器,确保迭代器指向有效的元素。

  3. 使用erase函数:在删除元素时,可以使用erase函数返回下一个有效迭代器,避免使用失效的迭代器。

示例代码

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4};

    // 插入元素
    vec.insert(vec.begin() + 2, 5);

    // 删除元素
    vec.erase(vec.begin() + 1);

    // 使用迭代器遍历
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}

1.2.6 vector 在OJ中的使用

在我们平时做的OJ题中,std::vector是一个常用的数据结构,可以帮助我们解决各种算法问题。本节将介绍如何在OJ中正确地使用std::vector,并提供一些示例代码和习题。

使用示例

下面是一个简单的OJ题目:给定一个整数数组,将数组中的所有偶数移到数组的末尾,保持奇数和偶数的相对顺序不变。

#include <iostream>
#include <vector>

void moveEvenToEnd(std::vector<int>& nums) {
    std::vector<int> result;
    
    for (int num : nums) {
        if (num % 2 != 0) {
            result.push_back(num);
        }
    }
    
    for (int num : nums) {
        if (num % 2 == 0) {
            result.push_back(num);
        }
    }
    
    nums = result; // 将结果拷贝回原数组
}

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8};
    
    moveEvenToEnd(nums);
    
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

习题:

  1. 题目:给定一个整数数组,找出数组中出现次数超过一半的元素。

  2. 题目:给定一个字符串,统计字符串中每个字符出现的次数,并按字符顺序输出。

  3. 题目:给定两个有序整数数组,合并两个数组并保持有序。

习题详细解题思路和步骤:

1. 找出数组中出现次数超过一半的元素

解题思路

  • 使用std::vector记录每个元素出现的次数。
  • 找出出现次数超过一半的元素。

解题步骤

  1. 遍历数组,使用std::vector<int>记录每个元素出现的次数。
  2. 找出出现次数超过一半的元素。
2. 统计字符串中每个字符出现的次数,并按字符顺序输出

解题思路

  • 使用std::vector<int>记录每个字符出现的次数。
  • 按字符顺序输出统计结果。

解题步骤

  1. 遍历字符串,使用std::vector<int>记录每个字符出现的次数。
  2. 按字符顺序输出统计结果。
3. 合并两个有序整数数组并保持有序

解题思路

  • 使用两个指针分别指向两个数组的开头。
  • 比较大小后依次将较小的元素添加到结果数组中。

解题步骤

  1. 初始化两个指针分别指向两个数组的开头。
  2. 比较指针所指元素的大小,将较小的元素添加到结果数组中,并移动对应指针。
  3. 直到其中一个数组遍历完毕,将剩余数组的元素依次添加到结果数组中。
#include <iostream>
#include <vector>

// 找出数组中出现次数超过一半的元素
int findMajorityElement(std::vector<int>& nums) {
    int majorityElement = 0;
    int count = 0;
    
    for (int num : nums) {
        if (count == 0) {
            majorityElement = num;
            count = 1;
        } else if (num == majorityElement) {
            count++;
        } else {
            count--;
        }
    }
    
    return majorityElement;
}

// 统计字符串中每个字符出现的次数,并按字符顺序输出
void countAndOutputCharacters(const std::string& str) {
    std::vector<int> charCount(256, 0); // 假设为ASCII字符集
    
    for (char c : str) {
        charCount[static_cast<int>(c)]++;
    }
    
    for (int i = 0; i < 256; i++) {
        if (charCount[i] > 0) {
            std::cout << static_cast<char>(i) << ": " << charCount[i] << std::endl;
        }
    }
}

// 合并两个有序整数数组并保持有序
std::vector<int> mergeSortedArrays(const std::vector<int>& arr1, const std::vector<int>& arr2) {
    std::vector<int> mergedArray;
    int i = 0, j = 0;
    
    while (i < arr1.size() && j < arr2.size()) {
        if (arr1[i] < arr2[j]) {
            mergedArray.push_back(arr1[i]);
            i++;
        } else {
            mergedArray.push_back(arr2[j]);
            j++;
        }
    }
    
    while (i < arr1.size()) {
        mergedArray.push_back(arr1[i]);
        i++;
    }
    
    while (j < arr2.size()) {
        mergedArray.push_back(arr2[j]);
        j++;
    }
    
    return mergedArray;
}

int main() {
    // 示例用法
    std::vector<int> nums = {1, 2, 2, 2, 3, 2, 2, 2, 5};
    int majorityElement = findMajorityElement(nums);
    std::cout << "Majority Element: " << majorityElement << std::endl;
    
    std::string str = "hello";
    countAndOutputCharacters(str);
    
    std::vector<int> arr1 = {1, 3, 5, 7};
    std::vector<int> arr2 = {2, 4, 6, 8};
    std::vector<int> mergedArray = mergeSortedArrays(arr1, arr2);
    
    std::cout << "Merged Sorted Array: ";
    for (int num : mergedArray) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

2.1 std::vector的核心框架接口的模拟实现bit::vector

在本节中,我们将模拟实现bit::vector,一个简化版的std::vector,以便更好地理解std::vector的核心框架接口。

#include <iostream>
#include <algorithm>

template <typename T>
class bit_vector {
private:
    T* data;
    size_t capacity;
    size_t size;

public:
    // 默认构造函数
    bit_vector() : data(nullptr), capacity(0), size(0) {}

    // 复制构造函数
    bit_vector(const bit_vector& other) : capacity(other.capacity), size(other.size) {
        data = new T[capacity];
        std::copy(other.data, other.data + size, data);
    }

    // 析构函数
    ~bit_vector() {
        delete[] data;
    }

    // 返回指向第一个元素的迭代器
    T* begin() {
        return data;
    }

    // 返回指向最后一个元素之后的位置的迭代器
    T* end() {
        return data + size;
    }

    // 在vector末尾添加元素
    void push_back(const T& value) {
        if (size == capacity) {
            reserve(capacity == 0 ? 1 : capacity * 2);
        }
        data[size++] = value;
    }

    // 移除vector末尾的元素
    void pop_back() {
        if (size > 0) {
            --size;
        }
    }

    // 预留空间
    void reserve(size_t new_capacity) {
        if (new_capacity > capacity) {
            T* new_data = new T[new_capacity];
            std::copy(data, data + size, new_data);
            delete[] data;
            data = new_data;
            capacity = new_capacity;
        }
    }

    // 访问元素
    T& operator[](size_t index) {
        return data[index];
    }
};

int main() {
    bit_vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    
    for (int& num : vec) {
        std::cout << num << " ";
    }

    return 0;
}

在上面的代码中,我们定义了一个简化版的bit_vector类,模拟实现了std::vector的核心框架接口,包括构造函数、析构函数、迭代器的使用、增加元素、删除元素、访问元素等功能。

2.2 使用memcpy拷贝问题

在C++中,使用memcpy函数进行内存拷贝时,对于涉及到动态内存分配的对象(如std::vector),需要格外小心,因为memcpy只是简单的按字节拷贝,无法正确处理对象内部的指针等成员变量。

下面我们来演示一个使用memcpy拷贝std::vector的错误示例,并说明问题所在:

#include <iostream>
#include <vector>
#include <cstring>

int main() {
    std::vector<int> vec1 = {1, 2, 3, 4, 5};
    
    // 使用memcpy进行拷贝
    std::vector<int> vec2;
    vec2.resize(vec1.size());
    memcpy(vec2.data(), vec1.data(), vec1.size() * sizeof(int));

    // 输出vec2的内容
    for (int num : vec2) {
        std::cout << num << " ";
    }

    return 0;
}

在上面的代码中,我们试图通过memcpyvec1拷贝到vec2,但这样做是错误的。因为std::vector内部不仅仅包含元素数据,还包含了指向动态分配的内存空间的指针,这些指针在拷贝时无法正确处理,会导致潜在的内存错误。

正确的方式是使用std::vector提供的拷贝构造函数或赋值运算符来进行拷贝:

std::vector<int> vec3(vec1); // 使用拷贝构造函数
// 或
std::vector<int> vec4;
vec4 = vec1; // 使用赋值运算符

这样可以确保std::vector内部的指针等成员变量得到正确的处理,避免潜在的问题。

2.3 动态二维数组理解

动态二维数组是指在运行时动态分配内存以创建二维数组。在C++中,可以使用std::vector嵌套来实现动态二维数组,具有灵活性和便利性。

下面是一个演示如何创建和操作动态二维数组的示例代码:

#include <iostream>
#include <vector>

int main() {
    int rows = 3;
    int cols = 4;

    // 创建动态二维数组
    std::vector<std::vector<int>> matrix(rows, std::vector<int>(cols));

    // 初始化动态二维数组
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            matrix[i][j] = i * cols + j;
        }
    }

    // 输出动态二维数组
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            std::cout << matrix[i][j] << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

在上面的代码中,我们首先定义了行数和列数,然后使用std::vector嵌套创建了一个3行4列的动态二维数组matrix。接着我们对数组进行了初始化,并输出了数组的内容。

使用std::vector嵌套可以方便地创建和操作动态二维数组,同时避免了手动管理内存的复杂性,提高了代码的可读性和可维护性。动态二维数组在处理需要动态大小的二维数据时非常实用。

好了,感谢大家看到这,如果觉得本篇文章对你有帮助的话,还请点个赞支持一下,有什么问题也可以评论区留言,那么我们下次再见了,Peace~
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值