STL:string和vector

🎯 一、string类:现代C++字符串处理

1.1 string类的诞生背景

在C语言中,字符串以 \0 结尾,操作函数(如strcpy、strcat)与数据分离,需要手动管理内存,极易造成越界访问。C++的string类将这些操作封装,提供了安全、高效的字符串处理方式。

1.2 string类核心接口详解

1.2.1 构造与赋值

cpp 复制代码
#include <string>
#include <iostream>
using namespace std;

void TestString() {
    // 1. 默认构造
    string s1;
    
    // 2. C字符串构造
    string s2("hello world");
    
    // 3. 拷贝构造
    string s3(s2);
    
    // 4. n个字符构造
    string s4(5, 'A');  // "AAAAA"
    
    // 5. 赋值操作
    s1 = "Hello";
    s2 = s1;
    
    // 6. assign函数
    s3.assign("C++ STL", 3);  // "C++"
}

1.2.2 容量操作

cpp 复制代码
void TestCapacity() {
    string str = "Hello";
    
    cout << "size: " << str.size() << endl;      // 5
    cout << "length: " << str.length() << endl;  // 5
    cout << "capacity: " << str.capacity() << endl;
    cout << "empty: " << str.empty() << endl;    // 0
    
    // 调整大小
    str.resize(10, '!');  // "Hello!!!!!"
    cout << str << endl;
    
    // 预分配空间(避免多次扩容)
    str.reserve(100);
    cout << "new capacity: " << str.capacity() << endl;
}

1.2.3 访问与遍历

cpp 复制代码
void TestAccess() {
    string str = "Hello World";
    
    // 1. 下标访问
    for (size_t i = 0; i < str.size(); ++i) {
        cout << str[i] << " ";  // 或使用 str.at(i)
    }
    cout << endl;
    
    // 2. 迭代器
    for (auto it = str.begin(); it != str.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // 3. 范围for(C++11)
    for (char ch : str) {
        cout << ch << " ";
    }
    cout << endl;
    
    // 4. 反向迭代器
    for (auto rit = str.rbegin(); rit != str.rend(); ++rit) {
        cout << *rit << " ";
    }
    cout << endl;
}

1.2.4 修改操作

cpp 复制代码
void TestModify() {
    string str = "Hello";
    
    // 1. 追加
    str += " World";      // "Hello World"
    str.append("!!!");    // "Hello World!!!"
    str.push_back('!');   // "Hello World!!!!"
    
    // 2. 插入
    str.insert(5, " C++");  // "Hello C++ World!!!!"
    
    // 3. 删除
    str.erase(5, 4);      // "Hello World!!!!"
    str.erase(str.begin() + 5);  // 删除位置5的字符
    
    // 4. 替换
    str.replace(6, 5, "STL");  // "Hello STL!!!!"
    
    // 5. 交换
    string s2 = "Bye";
    swap(str, s2);
}

1.2.5 查找操作

cpp 复制代码
void TestFind() {
    string str = "Hello World Hello C++";
    
    // 查找子串
    size_t pos = str.find("Hello");
    if (pos != string::npos) {
        cout << "Found at: " << pos << endl;  // 0
    }
    
    // 从指定位置查找
    pos = str.find("Hello", 1);
    cout << "Found at: " << pos << endl;  // 12
    
    // 反向查找
    pos = str.rfind("Hello");
    cout << "rfind: " << pos << endl;  // 12
    
    // 查找字符
    pos = str.find_first_of("aeiou");
    cout << "First vowel at: " << pos << endl;  // 1
    
    pos = str.find_last_of("aeiou");
    cout << "Last vowel at: " << pos << endl;  // 17
}

1.2.6 获取子串与比较

cpp 复制代码
void TestSubstrCompare() {
    string str = "Hello World";
    
    // 获取子串
    string sub = str.substr(6, 5);  // "World"
    cout << sub << endl;
    
    // 比较
    string s1 = "abc";
    string s2 = "abd";
    
    cout << s1.compare(s2) << endl;  // -1 (s1 < s2)
    cout << (s1 == s2) << endl;      // 0
    cout << (s1 < s2) << endl;       // 1
}

1.3 string类模拟实现

1.3.1 传统深拷贝实现

cpp 复制代码
class String {
public:
    // 构造函数
    String(const char* str = "") {
        if (str == nullptr) {
            _str = new char[1];
            *_str = '\0';
        } else {
            _str = new char[strlen(str) + 1];
            strcpy(_str, str);
        }
    }
    
    // 拷贝构造函数(深拷贝)
    String(const String& other) {
        _str = new char[strlen(other._str) + 1];
        strcpy(_str, other._str);
    }
    
    // 赋值运算符重载(传统写法)
    String& operator=(const String& other) {
        if (this != &other) {
            // 先申请新空间
            char* temp = new char[strlen(other._str) + 1];
            strcpy(temp, other._str);
            
            // 释放旧空间
            delete[] _str;
            
            // 赋值新空间
            _str = temp;
        }
        return *this;
    }
    
    // 现代写法(利用拷贝构造+交换)
    String& operator=(String other) {
        swap(_str, other._str);
        return *this;
    }
    
    // 析构函数
    ~String() {
        delete[] _str;
        _str = nullptr;
    }
    
    // 其他接口...
    
private:
    char* _str;
};

1.3.2 写时拷贝(Copy-On-Write)简介

写时拷贝是一种优化技术,多个对象共享同一份资源,只有当某个对象需要修改时,才进行深拷贝。

// 引用计数实现写时拷贝

cpp 复制代码
class CowString {
private:
    struct StringData {
        char* _str;
        int _refCount;  // 引用计数
        
        StringData(const char* str) {
            _str = new char[strlen(str) + 1];
            strcpy(_str, str);
            _refCount = 1;
        }
        
        ~StringData() {
            delete[] _str;
        }
    };
    
    StringData* _data;
    
    // 写时拷贝:需要修改时创建副本
    void CopyOnWrite() {
        if (_data->_refCount > 1) {
            StringData* newData = new StringData(_data->_str);
            --_data->_refCount;
            _data = newData;
        }
    }
    
public:
    // ... 其他成员函数
};

1.4 string类面试题实战

1.4.1 仅反转字母

https://leetcode.cn/problems/reverse-only-letters/description/

cpp 复制代码
class Solution {
public:
    string reverseOnlyLetters(string s) {
        if (s.empty()) return s;
        
        int left = 0, right = s.size() - 1;
        while (left < right) {
            while (left < right && !isalpha(s[left])) ++left;
            while (left < right && !isalpha(s[right])) --right;
            swap(s[left++], s[right--]);
        }
        return s;
    }
};

1.4.2 第一个只出现一次的字符

cpp 复制代码
class Solution {
public:
    int firstUniqChar(string s) {
        int count[256] = {0};
        
        // 统计每个字符出现的次数
        for (char ch : s) {
            count[ch]++;
        }
        
        // 再次遍历找到第一个出现一次的字符
        for (int i = 0; i < s.size(); i++) {
            if (count[s[i]] == 1) {
                return i;
            }
        }
        return -1;
    }
};

1.4.3 字符串相加

https://leetcode.cn/problems/add-strings/description/

cpp 复制代码
class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.size() - 1;
        int j = num2.size() - 1;
        int carry = 0;
        string result = "";
        
        while (i >= 0 || j >= 0 || carry > 0) {
            int n1 = i >= 0 ? num1[i] - '0' : 0;
            int n2 = j >= 0 ? num2[j] - '0' : 0;
            
            int sum = n1 + n2 + carry;
            result.push_back(sum % 10 + '0');
            carry = sum / 10;
            
            i--;
            j--;
        }
        
        reverse(result.begin(), result.end());
        return result;
    }
};

🎯 二、vector容器:动态数组深度解析

2.1 vector基本特性

vector是C++标准模板库中的动态数组,支持快速随机访问,在尾部插入删除高效,在中间插入删除需要移动元素。

2.2 vector核心接口

2.2.1 构造与初始化

cpp 复制代码
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

void TestVectorConstruct() {
    // 1. 默认构造
    vector<int> v1;
    
    // 2. 指定大小和初始值
    vector<int> v2(5, 1);  // 5个1
    
    // 3. 使用迭代器范围构造
    int arr[] = {1, 2, 3, 4, 5};
    vector<int> v3(arr, arr + 5);
    
    // 4. 拷贝构造
    vector<int> v4(v3);
    
    // 5. 列表初始化(C++11)
    vector<int> v5 = {1, 2, 3, 4, 5};
    
    // 6. 移动构造(C++11)
    vector<int> v6(std::move(v5));
}

2.2.2 容量与大小管理

cpp 复制代码
void TestCapacity() {
    vector<int> v;
    
    // 容量增长特性(不同编译器实现不同)
    cout << "初始容量: " << v.capacity() << endl;
    
    for (int i = 0; i < 20; ++i) {
        v.push_back(i);
        cout << "size: " << v.size() 
             << ", capacity: " << v.capacity() << endl;
    }
    
    // VS下按1.5倍增长,g++下按2倍增长
    // 使用reserve预分配空间
    vector<int> v2;
    v2.reserve(100);  // 预分配100个元素空间
    cout << "预分配后容量: " << v2.capacity() << endl;
}

2.2.3 访问元素

cpp 复制代码
void TestAccess() {
    vector<int> v = {1, 2, 3, 4, 5};
    
    // 1. 下标访问
    cout << "v[2] = " << v[2] << endl;      // 3
    cout << "v.at(2) = " << v.at(2) << endl; // 3
    
    // 2. 访问首尾元素
    cout << "front: " << v.front() << endl; // 1
    cout << "back: " << v.back() << endl;   // 5
    
    // 3. 获取底层数组指针
    int* p = v.data();
    cout << "data[2] = " << p[2] << endl;   // 3
    
    // 4. 范围for遍历
    for (int num : v) {
        cout << num << " ";
    }
    cout << endl;
}

2.2.4 修改操作

cpp 复制代码
void TestModify() {
    vector<int> v = {1, 2, 3};
    
    // 1. 添加元素
    v.push_back(4);       // {1, 2, 3, 4}
    v.emplace_back(5);    // {1, 2, 3, 4, 5} C++11更高效
    
    // 2. 插入元素
    v.insert(v.begin() + 1, 99);  // {1, 99, 2, 3, 4, 5}
    
    // 3. 删除元素
    v.pop_back();                 // 删除最后一个
    v.erase(v.begin() + 1);       // 删除指定位置
    v.erase(v.begin(), v.begin() + 2);  // 删除范围
    
    // 4. 清空
    v.clear();
    
    // 5. 交换
    vector<int> v2 = {6, 7, 8};
    v.swap(v2);
}

2.3 迭代器失效问题详解

2.3.1 插入操作导致迭代器失效

cpp 复制代码
void TestIteratorInvalidation1() {
    vector<int> v = {1, 2, 3, 4, 5};
    auto it = v.begin();
    
    // 插入元素可能导致扩容,使迭代器失效
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);  // 可能触发扩容
        // it 可能失效!
    }
    
    // 正确做法:每次插入后重新获取迭代器
    it = v.begin();
}

2.3.2 删除操作导致迭代器失效

cpp 复制代码
void TestIteratorInvalidation2() {
    // 错误示例:删除偶数
    vector<int> v = {1, 2, 3, 4, 5, 6};
    auto it = v.begin();
    
    while (it != v.end()) {
        if (*it % 2 == 0) {
            v.erase(it);  // 删除后it失效,再++会出错
        }
        ++it;  // ERROR!
    }
    
    // 正确做法1:使用erase返回值
    it = v.begin();
    while (it != v.end()) {
        if (*it % 2 == 0) {
            it = v.erase(it);  // erase返回下一个有效迭代器
        } else {
            ++it;
        }
    }
    
    // 正确做法2:使用remove-erase惯用法
    v.erase(remove_if(v.begin(), v.end(), 
                     [](int x) { return x % 2 == 0; }), 
           v.end());
}

2.3.3 resize/reserve导致迭代器失效

cpp 复制代码
void TestIteratorInvalidation3() {
    vector<int> v = {1, 2, 3, 4, 5};
    auto it = v.begin();
    
    v.reserve(100);  // 扩容,it失效
    // v.resize(100);  // 同样会导致迭代器失效
    
    // 正确:操作后重新获取迭代器
    it = v.begin();
}

2.4 vector模拟实现关键点

2.4.1 基本框架

cpp 复制代码
template<class T>
class Vector {
public:
    typedef T* iterator;
    typedef const T* const_iterator;
    
    Vector() : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {}
    
    // 拷贝构造、赋值运算符等...
    
    iterator begin() { return _start; }
    iterator end() { return _finish; }
    
    size_t size() const { return _finish - _start; }
    size_t capacity() const { return _endOfStorage - _start; }
    
private:
    iterator _start;          // 指向第一个元素
    iterator _finish;         // 指向最后一个元素的下一个位置
    iterator _endOfStorage;   // 指向存储空间末尾
};

2.4.2 push_back实现

cpp 复制代码
void push_back(const T& value) {
    // 检查是否需要扩容
    if (_finish == _endOfStorage) {
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
    }
    
    // 放置元素
    *_finish = value;
    ++_finish;
}

2.4.3 reserve实现(深拷贝问题)

cpp 复制代码
void reserve(size_t n) {
    if (n > capacity()) {
        size_t oldSize = size();
        T* newStart = new T[n];  // 申请新空间
        
        // 关键:不能使用memcpy(浅拷贝)
        // memcpy(newStart, _start, sizeof(T) * oldSize);
        
        // 必须使用深拷贝
        for (size_t i = 0; i < oldSize; ++i) {
            // 调用拷贝构造或placement new
            new (newStart + i) T(_start[i]);
        }
        
        // 释放旧空间(调用析构函数)
        for (size_t i = 0; i < oldSize; ++i) {
            _start[i].~T();
        }
        delete[] _start;
        
        _start = newStart;
        _finish = _start + oldSize;
        _endOfStorage = _start + n;
    }
}

2.5 vector经典应用

2.5.1 杨辉三角

cpp 复制代码
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> triangle(numRows);
        
        for (int i = 0; i < numRows; ++i) {
            triangle[i].resize(i + 1, 1);  // 每行初始化为1
            
            // 计算中间值(从第三行开始)
            for (int j = 1; j < i; ++j) {
                triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
            }
        }
        
        return triangle;
    }
};

2.5.2 只出现一次的数字

https://leetcode.cn/problems/single-number/description/

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int result = 0;
        for (int num : nums) {
            result ^= num;  // 异或运算
        }
        return result;
    }
};

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

https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/

cpp 复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.empty()) return 0;
        
        int slow = 0;
        for (int fast = 1; fast < nums.size(); ++fast) {
            if (nums[fast] != nums[slow]) {
                ++slow;
                nums[slow] = nums[fast];
            }
        }
        return slow + 1;
    }
};
相关推荐
winfield8215 小时前
滑动时间窗口,找一段区间中的最大值
数据结构·算法
小徐Chao努力5 小时前
Go语言核心知识点底层原理教程【Slice的底层实现】
开发语言·算法·golang
2301_805962935 小时前
嘉立创EDA添加自己的元件和封装
java·开发语言
Rookie_explorers5 小时前
go私有仓库athens搭建
开发语言·后端·golang
傻啦嘿哟5 小时前
Python爬虫进阶:反爬机制突破与数据存储实战指南
开发语言·爬虫·python
2301_764441335 小时前
基于Streamlit构建的风水命理计算器
开发语言·python
赫凯5 小时前
【强化学习】第三章 马尔可夫决策过程
python·算法
智航GIS5 小时前
1.2 python及pycharm的安装
开发语言·python·pycharm
资生算法程序员_畅想家_剑魔5 小时前
算法-动态规划-13
算法·动态规划