C++学习笔记——数据结构


记录c++学习中的常用数据结构容器,以及它们的常用方式。
如有错误,欢迎指出帮助修正。

0. begin()、front()、end()、back()、rbegin()、rend()

对这些函数功能的一些理解:
begin():用法类似头部指针的类,可以通过*访问值,类似指针加法进行偏移【常用遍历方式】
front():返回第一个元素的值
end():不可使用,作为结束标记符号存在的位置
back():返回最后一个元素的值
rbegin():反向遍历的头部指针
rend():反向遍历的结束标记

1. a[5] 数组(长度固定,连续存储)

C风格的数组,也可以使用多个[]创建多维数组。
长度固定。
成员在内存中以栈的形式连续存储,可以通过指针偏移直接访问成员。
缺点:在作为单个参数传递到函数中时,只能传递头部指针,而不能同时传递长度,如果不将长度作为额外参数传入的话,将会无法判断是否越界

#include <iostream>

using namespace std;
 
int main ()
{
    // 创建一个int类型的数组,长度为10
   int a[5];
 
   // 为每一个成员赋值          
   for (int i = 0; i < 5; i++) {
       a[i] = i;
   }
 
   // 遍历数组并输出
   for (int i = 0; i < 5; i++) {
       cout << a[i] << endl;
   }
 
   return 0;
}

运行得到:

0
1
2
3
4

2. string 字符串

字符串分两种:
一种是C语言风格的以char[]形式定义,以’\0’为结尾的字符数组(这种字符串在c++中也可以通过头部【即数组名】直接在cout中输出完整内容),通过#include <cstring>调用库函数。
此处指另一种:c++中的字符串类,通过#include <string>调用库函数。

注:反转字符串需要额外调用库#include <algorithm>

输入一行字符串【不包括换行符】

#include <iostream>
#include <string>
// 调用反转函数reserve需要的库
#include <algorithm>
 
using namespace std;
 
int main ()
{
    // 创建一个字符串
    string s1 = "123";
    cout << "字符串s1为:" << s1 << endl;

    string s2 = "abc";
    // 创建一个未定义的字符串
    string s3;

    // 获取字符串长度可以使用size或者length
    // c++中的size()和length()没有区别
    int length;
    length = s1.size();
    cout << "字符串s1的长度为:" << length << endl;
    cout << "字符串s1的长度为:" << s1.length() << endl;

    // 字符串可以直接复制覆盖别的字符串
    s2 = s1;
    cout << "被覆盖后的s2为:" << s2 << endl;

    // 字符串可以用加法拼接
    s3 = s1 + s2;
    cout << "s1与s2拼接:" << s3 << endl;

    // 反转字符串
    reverse(s3.begin(), s3.end());
    cout << "反转s3:" << s3 << endl;


    // 在字符串的末尾添加【字符串】
    // 注:此处输入只能是双引号的字符串,单引号的char会报错
    // 后续类似的注意点都用同样的括号重点标注出来
    s1.append("4");
    cout << "在s1的末尾添加4:" << s1 << endl;

    // 在指定位置插入【字符串】
    s1.insert(0, "0");
    cout << "在s1的第0位添加0:" << s1 << endl;
    // 在指定位置插入多个相同【字符】
    s1.insert(1, 2,  '9');
    cout << "在s1的第1位添加2个9:" << s1 << endl;
    // 将字符串s1从下标1开始的2个字符放到字符串s2的下标0位置
    s2.insert(0, s1,  1, 2);
    cout << "将字符串s1从下标1开始的2个字符放到字符串s2的下标0位置:" << s2 << endl;


    // 查找子串位置
     int pos = s1.find("991"); 
    cout << "查找s1中的子串991的第一个开始位置:" << pos << endl;
    // 从头部开始查找子串
    int first = s1.find_first_of("9");
    // 从尾部开始查找子串
    int last = s1.find_last_of("9");
    cout << "查找s1中的第一个9与最后一个9的位置:" << first <<  " " << last << endl;

    // 替换子串
    // 从s1的下标2开始的2个字符替换为8
    // 注:是把2个字符的字符串整体替换为字符串"8",而不是替换成2个8
    s1.replace(2, 2, "8");
    cout << "从s1的下标2开始的2个字符替换为8:" << s1 << endl;

    // 切片出一个新的子串
    // 从s1的下标3开始的2个字符被复制出来作为一个新的字符串end()
    string s4 = s1.substr (3, 2);
    cout << "从s1的下标3开始的2个字符被复制出来作为一个新的字符串:" << s4 << endl;

   return 0;
}

运行得到:

字符串s1为:123
字符串s1的长度为:3
字符串s1的长度为:3
被覆盖后的s2为:123
s1与s2拼接:123123
反转s3:321321
在s1的末尾添加4:1234
在s1的第0位添加0:01234
在s1的第1位添加2个9:0991234
将字符串s1从下标1开始的2个字符放到字符串s2的下标0位置:99123
查找s1中的子串991的第一个开始位置:1
查找s1中的第一个9与最后一个9的位置:1 2
从s1的下标2开始的2个字符替换为8:098234
从s1的下标3开始的2个字符被复制出来作为一个新的字符串:23

3. array 数组(长度固定,连续存储)

长度固定。
成员在内存中以栈的形式连续存储。
在作为单个参数传递到函数中时,能够同时传递长度,可以判断是否越界

#include <iostream>
#include <array>
 
using namespace std;
 
int main ()
{
    // 初始化一个数组并赋值、
    // 可以不给所有位置赋值
    array<int, 6> a1 = {1, 2, 3, 4};
    
    // 用指针遍历数组并输出所有元素
    // 没有被赋值过的元素输出是0
    cout << "a1中的元素包括:";
    for (auto p = a1.begin(); p != a1.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;

    // 获取数组的固定尺寸
    cout << "a1的内存大小为:" << sizeof(a1) << endl;
    // 未被赋值的成员依然占据了大小,与最大尺寸保持一致
    cout << "a1的当前尺寸为:" << a1.size() << endl;
    cout << "a1的最大尺寸为:" << a1.max_size() << endl;
    cout << "a1是否为空:" <<  a1.empty() << endl;

    // 访问元素
    cout << "a1的下标1的元素:" <<  a1.at(1) << endl;
    cout << "a1的头部元素:" <<  a1.front() << endl;
    cout << "a1的尾部元素:" <<  a1.back() << endl;

    // 全部初始化为固定值
    array<int, 6> a2;
    a2.fill(10);
    cout << "a2中的元素包括:";
    for (auto p = a2.begin(); p != a2.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;

    // 交换数组元素
    a1.swap(a2);
    cout << "交换后,a1中的元素包括:";
    for (auto p = a1.begin(); p != a1.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;
    cout << "交换后,a2中的元素包括:";
    for (auto p = a2.begin(); p != a2.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;

   return 0;
}

运行得到:

a1中的元素包括:1 2 3 4 0 0 
a1的内存大小为:24
a1的当前尺寸为:6
a1的最大尺寸为:6
a1是否为空:0
a1的下标1的元素:2
a1的头部元素:1
a1的尾部元素:0
a2中的元素包括:10 10 10 10 10 10 
交换后,a1中的元素包括:10 10 10 10 10 10 
交换后,a2中的元素包括:1 2 3 4 0 0

4. vector 向量(长度可变,连续存储)

长度可变。
成员在内存中连续存储。
可以判断是否越界
在各种算法中被广泛使用,出场率非常高的数据类型。

#include <iostream>
#include <vector>
 
using namespace std;

// 为了方便起见,写一个打印向量的函数
void print_vec(vector<int> v) {
     for (auto p = v.begin(); p != v.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;
}
 
 // 参考:https://blog.csdn.net/wkq0825/article/details/82255984
int main ()
{
    // vector初始化
    // 定义长度但不进行初始化
    vector<int> v1(10);
    // 定义长度且初始化为1
    vector<int> v2(10, 1);
    // 赋值一个原有向量到新向量
    vector<int> v3(v2);
    // 将v2下标0到下标2的元素给v4
    vector<int> v4(v2.begin(),v2.begin()+3);
    // 将数组作为向量初值
    int a1[7]={1,2,3,4,5,9,8};
    vector<int> v5(a1, a1+7);

    // 赋值
    v1 = {1, 2, 3, 4, 5, 6};

    // 将v2设置为长度6,每个元素初始化为3
    v2.assign(6, 3);
    cout << "将v2设置为长度6,每个元素初始化为3:";
    print_vec(v2);

    // 用指针遍历并输出所有元素
    // 与数组array不同,向量vector未被赋值的成员不会被输出
    cout << "v1中的元素包括:";
    // 自定义的打印函数
    print_vec(v1);
    cout << endl;

    // 获取向量的固定尺寸
    cout << "v1的内存大小为:" << sizeof(v1) << endl;
    // 与数组array不同,向量vector未被赋值的成员没有占据size大小
    cout << "v1的当前尺寸为:" << v1.size() << endl;
    cout << "v1的最大尺寸为:" << v1.max_size() << endl;
    cout << "v1是否为空:" <<  v1.empty() << endl;
    cout << endl;

    // 调整内存大小
    cout << "v2在内存中总共可以容纳的元素个数:" <<  v2.capacity() << endl;
    // 将v2的现有元素个数调至10个,多则删,少则补,其值随机
    v2.resize(10);
    cout << "将v2的现有元素个数调至10个,多则删,少则补,其值未初始化:";
    print_vec(v2);
    // 将v2的现有元素个数调至20个,多则删,少则补,其值为4
    v2.resize(20, 4);
    cout << "将v2的现有元素个数调至20个,多则删,少则补,其值为4:";
    print_vec(v2);
    // 手动给vec扩容内存空间,用于准备输入大量数据
    // 不会影响到size()返回的成员数量
    v2.reserve(30);
    cout << "扩容后,v2在内存中总共可以容纳的元素个数:" <<  v2.capacity() << endl;
    cout << endl;


    // 访问成员
    cout << "v1的头部元素为:" << v1.front() << endl;
    cout << "v1的尾部元素为:" << v1.back() << endl;
    cout << "v1的下标2的元素为:" << v1.at(2) << endl;
    cout << endl;

    // 在向量末尾插入元素
    v1.push_back(26);
    cout << "v1在末尾插入元素:";
    print_vec(v1);

    // 在向量末尾删除元素
    v1.pop_back();
    cout << "v1在末尾删除元素:";
    print_vec(v1);

    // 添加元素
    // 在v1的下标1处插入5
    v1.insert(v1.begin()+1,5);
    cout << "在v1的下标1处插入5:";
    print_vec(v1);

    // 在v1的下标1处插入3个值为4的数
    v1.insert(v1.begin()+1, 3, 4);
    cout << "在v1的下标1处插入3个值为4的数:";
    print_vec(v1);

    // 在v1的下标1处插入v2的第3个元素到第5个元素
    cout << "v2中的元素:";
    print_vec(v2);
    v1.insert(v1.begin()+1,v2.begin()+3,v2.begin()+6); 
    cout << "在v1的下标1处插入v2的第3个元素到第5个元素:";
    print_vec(v1);

    // 删除元素
    // 删除v1中下标1开始的2个元素
    v1.erase(v1.begin()+1, v1.begin()+3);
    cout << "删除v1中下标1开始的2个元素:";
    print_vec(v1);
    cout << endl;

    // 交换元素
    v1.swap(v2);
    cout << "交换后,v1为:";
    print_vec(v1);
    cout << "交换后,v2为:";
    print_vec(v2);

    // 清空元素
    v1.clear();
    cout << "清空后,v1为:";
    print_vec(v1);

   return 0;
}

运行得到:

将v2设置为长度6,每个元素初始化为3:3 3 3 3 3 3 
v1中的元素包括:1 2 3 4 5 6 

v1的内存大小为:24
v1的当前尺寸为:6
v1的最大尺寸为:4611686018427387903
v1是否为空:0

v2在内存中总共可以容纳的元素个数:10
将v2的现有元素个数调至10个,多则删,少则补,其值未初始化:3 3 3 3 3 3 0 0 0 0 
将v2的现有元素个数调至20个,多则删,少则补,其值为4:3 3 3 3 3 3 0 0 0 0 4 4 4 4 4 4 4 4 4 4 
扩容后,v2在内存中总共可以容纳的元素个数:30

v1的头部元素为:1
v1的尾部元素为:6
v1的下标2的元素为:3

v1在末尾插入元素:1 2 3 4 5 6 26 
v1在末尾删除元素:1 2 3 4 5 6 
在v1的下标1处插入5:1 5 2 3 4 5 6 
在v1的下标1处插入3个值为4的数:1 4 4 4 5 2 3 4 5 6 
v2中的元素:3 3 3 3 3 3 0 0 0 0 4 4 4 4 4 4 4 4 4 4 
在v1的下标1处插入v2的第3个元素到第5个元素:1 3 3 3 4 4 4 5 2 3 4 5 6 
删除v1中下标1开始的2个元素:1 3 4 4 4 5 2 3 4 5 6 

交换后,v1为:3 3 3 3 3 3 0 0 0 0 4 4 4 4 4 4 4 4 4 4 
交换后,v2为:1 3 4 4 4 5 2 3 4 5 6 
清空后,v1为:

需要导入#include<algorithm>库的进阶用法:

#include <iostream>
#include <vector>
// 需要算法头文件
#include<algorithm>
 
using namespace std;

// 为了方便起见,写一个打印向量的函数
void print_vec(vector<int> v) {
     for (auto p = v.begin(); p != v.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;
}
 
 // 参考:https://blog.csdn.net/wkq0825/article/details/82255984
int main ()
{
    // vector初始化
    vector<int> v1(10);
    v1 = {1, 2, 3, 4, 5, 6};
    // 定义长度且初始化为1
    vector<int> v2(3, 9);

    cout << "v1:";
    print_vec(v1);

    // 排序
    sort(v1.begin(), v1.end());
    cout << "v1排序之后:";
    print_vec(v1);

    // 反转
    reverse(v1.begin(), v1.end());
    cout << "v1反转之后:";
    print_vec(v1);

    // 复制
    cout << "v2:";
    print_vec(v2);
    // 把v2的所有元素复制覆盖到v1的索引3往后的数据
    copy(v2.begin(), v2.end(), v1.begin()+1);
    cout << "把v2的所有元素复制覆盖到v1的索引3往后的数据:";
    print_vec(v1);

    // 查找
    vector<int>::iterator index_p = find(v1.begin(), v1.end(), 9);
    // 返回的实际上是一个指针,指向值的位置
    if (index_p != v1.end()) {
        cout << "v1中9的位置的指针的值:" << *index_p << endl;
        // 将指针转换为索引值
        auto index = distance(v1.begin(), index_p);
        cout << "v1中9的位置的索引:" << index << endl;
    }
    else {
        cout << "v1中找不到9的位置" << endl;
    }

   return 0;

}

运行得到:

v1:1 2 3 4 5 6 
v1排序之后:1 2 3 4 5 6 
v1反转之后:6 5 4 3 2 1 
v2:9 9 9 
把v2的所有元素复制覆盖到v1的索引3往后的数据:6 9 9 9 2 1 
v1中9的位置的指针的值:9
v1中9的位置的索引:1

5. list 列表(长度可变,不连续存储)

长度可变。
成员在内存中以链表形式不连续存储,类似双向链表
优点:能够快速添加与删除任何位置的元素。
缺点:不适合随机访问,整体操作较慢。

参考:C++ List的用法(整理)

#include <iostream>
#include <list> 
// 需要算法头文件
#include<algorithm>
 
using namespace std;

// 从前往后打印一个list
void print_list(list<int> list) {
     for (auto p = list.begin(); p != list.end(); p++) {
        cout << *p << " ";
    }
    cout << endl;
}
 
 // 从后往前打印一个list
void rprint_list(list<int> list) {
     for (auto p = list.rbegin(); p != list.rend(); p++) {
        cout << *p << " ";
    }
    cout << endl;
}

// 条件筛选函数
bool is_small_than_5(const int& value) {
    return value < 5;
} 

int main ()
{
    // 创建一个list
    list<int> list1;
    // 声明一个迭代器
    list<int>::iterator iter;

    // 从尾部插入元素
    list1.push_back(4); 
    list1.push_back(8);
    list1.push_back(3);
    list1.push_back(1);

    // 正向遍历list1
    print_list(list1) ;
    // 反向遍历list1
    rprint_list(list1) ;

    // 从头部插入元素
    list1.push_front(9);
    cout << "在前方插入9:";
    print_list(list1) ;

    cout << endl;

    // 对列表赋值
    list<int> list2;
    // 赋值8个1
    list2.assign(8,1);
    cout << "赋值8个1:";
    print_list(list2) ;

    // 在列表中插入数据
    list2.insert(++list2.begin(),3,9); 
    cout << "在列表中插入数据3个9:";
    print_list(list2) ;
    // 向指定索引插入数据需要让迭代器自增抵达
    auto iter1 = begin(list2);
    // 在索引4的位置
    advance(iter1, 4);
    list2.insert(iter1, 3, 3);
    cout << "在在索引4的位置插入数据3个3:";
    print_list(list2) ;

    // 清除list2中的第2个元素   
    list2.erase(++list2.begin());   
    cout<<"清除list2中的第2个元素   ";   
    print_list(list2) ;

    cout << endl;

    // 前后弹出元素
    list1.pop_front();
    list1.pop_back(); 
    cout << "前后弹出元素:";
    print_list(list1) ;

    // 对list1排序
    list1.sort();   
    cout << "对list1排序:";
    print_list(list1);

     // 交换
    list1.swap(list2);
    cout << "把list1与list2交换:" << endl;
    print_list(list1);
    print_list(list2);

    // 合并,greater<int>为升序, less<int>为降序
    // 但升序和降序的作用机制有点迷惑,暂时还没理清
    list1.merge(list2, less<int>());
    cout << "对list1与list2合并:";
    print_list(list1);

    // 集合的属性显示
    cout << "第一个元素 :" << list1.front() << endl;
    cout << "最后一个元素 :" << list1.back() << endl;
    cout << "元素个数:" << list1.size() << endl;
    cout << "是否为空:" << list1.empty() << endl;
    cout << "能容纳的最大元素数量:" << list1.max_size() << endl;
    //cout << ":" << list1. << endl;

    // 删除连续的重复元素
    list1.unique();
    cout << "删除连续的重复元素:";
    print_list(list1);

    // 反转列表
    list1.reverse();
    cout << "反转列表:";
    print_list(list1);

    // 按照值删除所有元素1
    list1.remove(1);
    cout << "按照值删除所有元素1:";
    print_list(list1);

    // 删除所有小于5的数字
    list1.remove_if(is_small_than_5);
    cout << "删除所有小于5的数字:";
    print_list(list1);

    // 将list2中的值剪切到list1下标0开始的地方
    // list2中原本的值将会被清空,list1中的值会后移而不是被覆盖
    list2.assign(8,1);
    list1.splice(list1.begin(), list2);
    cout << "将list2中的值剪切到list1下标0开始的地方:";
    print_list(list1);

    // 清空
    list1.clear();
     cout << "清空元素:";
    print_list(list1);

   return 0;

}

运行得到:

4 8 3 1 
1 3 8 4 
在前方插入9:9 4 8 3 1 

赋值8个1:1 1 1 1 1 1 1 1 
在列表中插入数据3个9:1 9 9 9 1 1 1 1 1 1 1 
在在索引4的位置插入数据3个3:1 9 9 9 3 3 3 1 1 1 1 1 1 1 
清除list2中的第2个元素   1 9 9 3 3 3 1 1 1 1 1 1 1 

前后弹出元素:4 8 3 
对list1排序:3 4 8 
把list1与list2交换:
1 9 9 3 3 3 1 1 1 1 1 1 1 
3 4 8 
对list1与list2合并:1 3 4 8 9 9 3 3 3 1 1 1 1 1 1 1 
第一个元素 :1
最后一个元素 :1
元素个数:16
是否为空:0
能容纳的最大元素数量:768614336404564650
删除连续的重复元素:1 3 4 8 9 3 1 
反转列表:1 3 9 8 4 3 1 
按照值删除所有元素1:3 9 8 4 3 
删除所有小于5的数字:9 8 
将list2中的值剪切到list1下标0开始的地方:1 1 1 1 1 1 1 1 9 8 
清空元素:

6. unordered_map 哈希表(无序)

参考:详细介绍C++STL:unordered_map

map相当于java中的TreeMap,unordered_map相当于HashMap。
无论从查找、插入上来说,unordered_map的效率都优于hash_map,更优于map;
而空间复杂度方面,hash_map最低,unordered_map次之,map最大。

7. map 哈希表(有序)

map<int, string> map_1;
map_1[1] = "abc"

判断key是否存在于map中:

if (map_1.find(key) == map_1.end())
{cout << "没有key" << endl;}

8. set 集合(有序,去重)

用于去重与排序。
与python不同,c++的set可以用于排序。
自定义排序规则:

set<int, compare> sort_int;
// 自定义排序
struct compare
{
    bool operator()(const int &a, constint &b) const
    {
        return a > b;
    }
};

9. pair 对(包含两个有序数据的结构体)

pair<int, int>

通过a.firsta.second访问两个成员。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值