数据结构顺序表
一.顺序表的概念
1.线性表
线性表是一种简单的顺序表
线性表是n个具有相同特性的数据元素的有序序列。
线性表在逻辑上可以想象成是连续的一条线段,线段上有很多个点
相关术语:表头,表尾,前驱,后继,空表
1.2.线性表的存储结构:
1.线性表的顺序存储-顺序表--用顺序储存实现的线性表
2.链性存储--链表(后期讲)
顺序储存:
逻辑上相邻的元素,在内存中也存放在相邻的位置。
实际上,线性表的顺序储存就是用数组来实现的
二.顺序表的模拟实现
先声明,往后实现各种数据结构,如果没有事先说明,默认里面储存的是int类型的数据
顺序表可以用数组来实现,而数组有两种不同的申请方式
因此,顺序表也有两种方式
按照数组的申请方式,有以下两种实现方式:
1.数组采用静态分配,此时的顺序表称为静态顺序表。
2.数组采用动态分配,此时的顺序表称为动态顺序表
1.1.动态申请
通过new和delete来申请和释放空间
int *a; //接收new出来的数组地址
int capacity; //b标记当前数组的实际大小
int n; //标记有效个元素个数
eg:往顺序表末尾依次插入[1,2,3,4,...,10]
第一次空间不够,扩容
a = new int[4];
capacity = 4;
第二次空间不够,扩容:
int *t = new int[capacity * 2]; 扩容时,一般是原来的两倍,也可以多倍
memcpy(t,a,sizeof(int) * capacity);
delete[] a;
第三次空间不够,扩容:
int *t = new int[capacity * 2];
memcpy(t,a,sizeof(int) * capacity);
delete[] a;
delete释放
优点:
按需所取,需要多少空间就申请多少空间
缺点:
动态实现会进行空间的申请与释放以及数据的拷贝
而这些操作都是非常耗时的
因此我们只演示不模拟
1.2静态实现
直接创建一个足够大的数组,来充当顺序表
静态分配就是直接向内存申请一大块连续的区域,然后将需要存放的数组放在这一大块连续的区域 上。 动态分配就是按需所取。按照需要存放的数据的数量,合理的申请大小合适的空间来存放数据。
优点:
1. 不需要动态管理内存,代码书写上会比较方便。
2. 没有动态管理内存中申请以及释放空间的时间开销。
缺点:
1. 一旦空间占满,新来的数据就会溢出。
2. 如果为了保险而申请很大的空间,数据量小的情况下,会浪费很多空间。
通过两者对比会发现,并没有一种实现方式就是绝对完美的。想要书写方便以及运行更快,就要承担空间不够或者空间浪费的情况;想要空间上合理分配,就要承担时间以及代码书写上的消耗。 在后续的学习中,会经常看到各种情况的对比。这就要求我们掌握各种数据结构的特点,从而在解决实际问题的时候,选择一个合适的数据结构。 在算法竞赛中,我们主要关心的其实是时间开销,空间上是基本够用的。因此,定义一个超大的静态 数组来解决问题是完全可以接受的。因此,关于顺序表,采用的就是静态实现的方式。
2.创建
创建一个足够大的数组充当顺序表:int a[1000010];
同时还需要一个变量,用来标记顺序表当前有多少个元素: int n = 0;
#include <iostream>using namespace std;const int N = 1e6 + 10;
//根据顺序实际情况而定//创建顺序表
int a[N];
int n;//标记 int main()
{return 0;}
添加一个元素
尾插
在顺序表的表尾的后面,插入一个新元素
例:往一个空表中依次尾插[2,5,1,3]
约定:
下标为0的位置,不储存有效数据。
也就是说数据从a[1]开始储存。
#include <iostream>using namespace std;const int N = 1e6 + 10;
//根据顺序实际情况而定//创建顺序表
int a[N];
int n;//标记 顺序表里又=有多少个元素//打印顺序表
void print()
{for(int i =1; i <= n; i++){cout << a[i] << " ";}cout << endl << endl;} //尾插
void push_back(int x)
{a[++n] = x; } int main()
{//测试尾插push_back(2);print();push_back(5);print();push_back(1);print();push_back(3); print();return 0;} //输出2// 2 5// 2 5 1// 2 5 1 3
时间复杂度:
直接放在后面即可,时间复杂度为 O(1) 。
头插
在顺序表表头的前面,插入一个新元素
eg;往顺序表[2,5,1,3]头插入一个元素 10
方法1:直接放表头:a[1] = 10;
方法2:放在之前空出来的位置上:a[0] = 10;
方法3:将顺序表中所有元素统一右移一位,然后放在表头--不能从前往后一个一个移动,可以从后往前移
#include <iostream>using namespace std;const int N = 1e6 + 10;
//根据顺序实际情况而定//创建顺序表
int a[N];
int n;//标记 顺序表里又=有多少个元素//打印顺序表
void print()
{for(int i =1; i <= n; i++){cout << a[i] << " ";}cout << endl << endl;} //尾插
void push_back(int x)
{a[++n] = x; } //头插 void push_front(int x){//1.先把[1,n]的元素统一向后移动一位for(int i = n;i >= 1; i--){a[i + 1] = a[i]; } //2.把x放表头a[1] = x;n++;//元素个数 +1 }int main()
{//测试尾插push_back(2);print();push_back(5);print();push_back(1);print();push_back(3); print();//测试头插push_front(10);print(); return 0;} //输出2// 2 5// 2 5 1// 2 5 1 3 // 10 2 5 1 3
时间复杂度:
由于需要将所有元素右移一位,时间复杂度为 O(N) 。
在任意位置插入
往顺序表[10,2,5,1,3]第3个位置插入新元素 0
也就是把0 放在2 与 5 之间
步骤
1.将[p,n]内所有的元素统一右移
2.把新的元素放在p位置上
3.元素个数 +1
#include <iostream>using namespace std;const int N = 1e6 + 10;
//根据顺序实际情况而定//创建顺序表
int a[N];
int n;//标记 顺序表里又=有多少个元素//打印顺序表
void print()
{for(int i =1; i <= n; i++){cout << a[i] << " ";}cout << endl << endl;} //尾插
void push_back(int x)
{a[++n] = x; } //头插 void push_front(int x){//1.先把[1,n]的元素统一向后移动一位for(int i = n;i >= 1; i--){a[i + 1] = a[i]; } //2.把x放表头a[1] = x;n++;//元素个数 +1 }//在任意位置插入void insert(int p,int x){//1.先把[p,n]的元素统一向后移动一位for(int i = n; i >= p; i--){a[i + 1] = a[i];} a[p] = x;n++;//不要忘记总个数+1} int main()
{//测试尾插push_back(2);print();push_back(5);print();push_back(1);print();push_back(3); print();//测试头插push_front(10);print(); //任意插入insert(3,0);print();return 0;} //输出2// 2 5// 2 5 1// 2 5 1 3 // 10 2 5 1 3// 10 2 0 5 1 3
最坏情况下需要数组中所有元素右移,时间复杂度为 O(N);
思考一下,这3个函数有没有bug呢?
当然有
数组存满了,就不能再存了,所以在写尾插,头插,任意插函数时,应该加上判断
但是,我们一般不去管这个判断怎么写,因为我们在调用时,应该自己去判断该函数调用是否合法,不合法时就不要使用
还有一个小bug,就是任意插函数中的p也是要合法的,因为,顺序表是连续的,p不能超过其顺序
删除元素
尾删
删顺序表表尾元素
直接void pop_back()然后使用n--
时间复杂度为O(1)
#include <iostream>using namespace std;const int N = 1e6 + 10;
//根据顺序实际情况而定//创建顺序表
int a[N];
int n;//标记 顺序表里又=有多少个元素//打印顺序表
void print()
{for(int i =1; i <= n; i++){cout << a[i] << " ";}cout << endl << endl;} //尾插
void push_back(int x)
{a[++n] = x; } //头插
void push_front(int x){//1.先把[1,n]的元素统一向后移动一位for(int i = n;i >= 1; i--){a[i + 1] = a[i]; } //2.把x放表头a[1] = x;n++;//元素个数 +1 }//在任意位置插入
void insert(int p,int x){//1.先把[p,n]的元素统一向后移动一位for(int i = n; i >= p; i--){a[i + 1] = a[i];} a[p] = x;n++;//不要忘记总个数+1}void pop_back()
{n--; } int main()
{//测试尾插push_back(2);print();push_back(5);print();push_back(1);print();push_back(3); print();//测试头插push_front(10);print(); //任意插入insert(3,0);print();//测试尾删cout << "尾删" << endl; pop_back();print(); pop_back();print(); return 0;}
///输出
//2//2 5//2 5 1//2 5 1 3//10 2 5 1 3//10 2 0 5 1 3//尾删
//10 2 0 5 1//10 2 0 5
头删
删顺序表表头元素
先将所有元素左移一个元素(从前往后移),再进行删除
涉及元素移动,时间复杂度为O(N)
//头删
void pop_front()
{//先把2~n区间的元素从前往后左移一个元素for(int i = 2; i <= n; i++){a[i - 1] = a[i];} n--;//左移完要删除元素 }
//测试头删cout << "头删" << endl;pop_front();print(); pop_front();print();
任意删
删除任意位置p的元素
涉及元素移动,时间复杂度为O(N)
//任意删
void erase(int p)
{//把[p + 1,n]的元素,统一左移一位for(int i = p + 1; i <= n; i++){a[i - 1] = a[i];} n--;}
//任意删cout << "任意删" << endl;erase(3);print();erase(2);print();
以上删除操作仍然是有bug的,和插入时的bug差不多
查找元素
按值查找
查找成功:返回储存位置的下标
查找失败:返回0或者-1
最坏情况要便厉整个数组,时间复杂度位O(N);
#include <iostream>using namespace std;const int N = 1e6 + 10;
//根据顺序实际情况而定//创建顺序表
int a[N];
int n;//标记 顺序表里又=有多少个元素//打印顺序表
void print()
{for(int i =1; i <= n; i++){cout << a[i] << " ";}cout << endl << endl;} //尾插
void push_back(int x)
{a[++n] = x; } //头插
void push_front(int x){//1.先把[1,n]的元素统一向后移动一位for(int i = n;i >= 1; i--){a[i + 1] = a[i]; } //2.把x放表头a[1] = x;n++;//元素个数 +1 }//在任意位置插入
void insert(int p,int x){//1.先把[p,n]的元素统一向后移动一位for(int i = n; i >= p; i--){a[i + 1] = a[i];} a[p] = x;n++;//不要忘记总个数+1}//按值查找
int find(int x)
{for(int i = 1; i<= n; i++){if(a[i] == x) return i;} return 0;} int main()
{//测试尾插push_back(2);print();push_back(5);print();push_back(1);print();push_back(3); print();//测试头插push_front(10);print(); //任意插入insert(3,0);print();//测试按值查找for(int i = 1; i<= 10; i++){cout << "查找" << i <<": ";cout << find(i) << endl; } return 0;}
输出
2
2 5
2 5 1
2 5 1 3
10 2 5 1 3
10 2 0 5 1 3
查找1: 5
查找2: 2
查找3: 6
查找4: 0
查找5: 4
查找6: 0
查找7: 0
查找8: 0
查找9: 0
查找10: 1
该代码无bug
按位查找
返回顺序表中的第p位元素return a[p];
时间复杂度为O(1)
//按位查找
int at(int p)
{return a[p];
}
有bug。p的位置应该合法
修改元素
按位修改
时间复杂度为O(1)
//按位修改
int change(int p, int x)//把p位置的值改为x
{a[p] = x;
}
p的位置应该合法
清空顺序表
我们这里实现的简单形式的时间复杂度为O(1)
但是正常应该为O(N)
//清空顺序表
int clear()
{n = 0;}
但是在有些情况下该代码是有问题的因为这里储存的都是int类型,如果是其他类型,需要移动数值
封装静态顺序表
使用类或者结构体封装一个静态顺序表
#include <iostream>using namespace std;const int N = 50;//使用类或者结构体封装一个静态顺序表
class SqList
{int a[N];int n;public://构造函数SqList(){n = 0;} //尾插void push_back(int x){a[++n] = x;}//打印void print(){for(int i = 1;i <= n;i++){cout << a[i] << " ";}cout << endl;} };int main()
{SqList s1,s2;//创建了两个顺序表//两个顺序表是互不干扰,独立的 //SqList s[N];//创建一个数组(多个) for(int i = 1; i <= 5; i++){s1.push_back(i);s2.push_back(i * 2);} s1.print();s2.print();return 0;
}//输出
//1 2 3 4 5
//2 4 6 8 10
注意:
• 为什么这里讲了封装? 最重要的原因是想让大家知道,接下来我们要学习的STL为什么可以通过"."调用各种各样的接口。
• 为什么我们后面不做封装了?
a. 我们做题如果用到某个数据结构,一般仅需要一个,最多两个,所以没有必要封装。 因为封装之后,还要去写xxx.xxx,比较麻烦;
b. 如果要用到多个相同的数据结构,那么推荐使用STL,更加方便。
动态顺序表
创建-vector
动态顺序表就不带着实现了,因为涉及空间申请和释放的 new 和 delete 效率不高,在算法竞赛中使用会有超时的风险。而且实现一个动态顺序表代码量很大,我们不可能在竞赛中傻乎乎的实现一 个动态顺序表来解决问题。
但是我们要明白一点,竞赛代码和工程代码是不一样的。在我们以后工作写项目的时候,还是需要动态申请空间的方式。因此,希望大家还是需要掌握动态顺序表的实现。
如果需要用动态顺序表,有更好的方式:C++ 的 STL 提供了一个已经封装好的容器 vector ,有的地方也叫作可变长的数组。vector 的底层就是一个会自动扩容的顺序表,其中创建以及增删查改等等的逻辑已经实现好了,并且也完成了封装。
接下来就重点学习 vector 的使用。
#include <iostream>
#include <vector>using namespace std;const int N = 10;//初始化 int main()
{//1.创建 vectorvector<int> a1;//最简单的创建方式 (不需要初始化 //创建了一个名字为a1 的空的可变长数组,里面都是 int 类型 的数据vector<int> a2(N);//这种创建方式需要初始化 //即创建了一个大小为10 的可变长数组,里面的值默认为0 vector<int> a3(N,2);// 创建了一个大小为10 的可变长数组,里面的值默认为2 vector<int> a4 = {1,2,3,4,5};//初始化列表的创建方式//以上为核心 //<>--其里面可以存放任意一种数据类型,这就体现了模板的作用,也体现了模板的强大之处//这样,vector里面就可以存放我们见过的所有的数据类型,甚至是STL本身vector<string> a5; //存字符串vector<node> a6; //存结构体 vector<vector<int>> a7; //创建了一个可变长数组vector<int> a8[N];//与第二种方式分开,这里是[]//()是为其指定初始值//[]创建了一个大小为N的可变长数组 类比与int a[N];return 0;
}
size/empty
1. size :返回实际元素的个数;
2. empty :返回顺序表是否为空,因此是一个bool 类型的返回值。
a. 如果为空:返回true
b. 否则,返回false
#include <iostream>
#include <vector>using namespace std;const int N = 10;//初始化 struct node
{int a,b;string s;
}; void print(vector<int>& a)
{for(int i = 0; i < a.size(); i++){cout << a[i] << " ";}cout << endl;
}int main()
{//1.创建 vectorvector<int> a1;//最简单的创建方式 (不需要初始化 //创建了一个名字为a1 的空的可变长数组,里面都是 int 类型 的数据vector<int> a2(N);//这种创建方式需要初始化 //即创建了一个大小为10 的可变长数组,里面的值默认为0 vector<int> a3(N,2);// 创建了一个大小为10 的可变长数组,里面的值默认为2 vector<int> a4 = {1,2,3,4,5};//初始化列表的创建方式//以上为核心 //<>--其里面可以存放任意一种数据类型,这就体现了模板的作用,也体现了模板的强大之处//这样,vector里面就可以存放我们见过的所有的数据类型,甚至是STL本身vector<string> a5; //存字符串vector<node> a6; //存结构体 vector<vector<int>> a7; //创建了一个可变长数组vector<int> a8[N];//与第二种方式分开,这里是[]//()是为其指定初始值//[]创建了一个大小为N的可变长数组 类比与int a[N];//2.size / empty//printprint(a2);//输出 0 0 0 0 0 0 0 0 0 0 print(a3);////0 0 0 0 0 0 0 0 0 0 //2 2 2 2 2 2 2 2 2 2print(a4);//1 2 3 4 5 if(a1.empty()) cout << "空" << endl;else cout << "不空" << endl;//如果a1 里是空,返回空,否则-不空//输出 空 if(a2.empty()) cout << "空" << endl;else cout << "不空" << endl;//输出 不空 return 0;
}
begin / end
1. begin :返回起始位置的迭代器(左闭);
2. end :返回终点位置的下一个位置的迭代器(右开);
利用迭代器可以访问整个 vector ,存在迭代器的容器就可以使用范围 for 遍历。
#include <iostream>
#include <vector>using namespace std;const int N = 10;//初始化 struct node
{int a,b;string s;
}; void print(vector<int>& a)
{
// for(int i = 0; i < a.size(); i++)
// {
// cout << a[i] << " ";
// }
// cout << endl;//利用迭代器来便历 - 迭代器类型 vector<int>::iterator
// for(auto it = a.begin(); it != a.end(); it++)
// {
// cout << *it << " ";
// }
// cout << endl; //太麻烦了,c++给我们提供了一种//使用语法糖 - 范围forfor(auto x : a){cout << x << " ";} cout << endl;}int main()
{//1.创建 vectorvector<int> a1;//最简单的创建方式 (不需要初始化 //创建了一个名字为a1 的空的可变长数组,里面都是 int 类型 的数据vector<int> a2(N);//这种创建方式需要初始化 //即创建了一个大小为10 的可变长数组,里面的值默认为0 vector<int> a3(N,2);// 创建了一个大小为10 的可变长数组,里面的值默认为2 vector<int> a4 = {1,2,3,4,5};//初始化列表的创建方式//以上为核心 //<>--其里面可以存放任意一种数据类型,这就体现了模板的作用,也体现了模板的强大之处//这样,vector里面就可以存放我们见过的所有的数据类型,甚至是STL本身vector<string> a5; //存字符串vector<node> a6; //存结构体 vector<vector<int>> a7; //创建了一个可变长数组vector<int> a8[N];//与第二种方式分开,这里是[]//()是为其指定初始值//[]创建了一个大小为N的可变长数组 类比与int a[N];//2.size / empty//print
// print(a2);
// //输出 0 0 0 0 0 0 0 0 0 0
//
// print(a3);
// //
// //0 0 0 0 0 0 0 0 0 0
// //2 2 2 2 2 2 2 2 2 2
//
// print(a4);
// //1 2 3 4 5
//
// if(a1.empty()) cout << "空" << endl;
// else cout << "不空" << endl;
// //如果a1 里是空,返回空,否则-不空
// //输出 空
//
// if(a2.empty()) cout << "空" << endl;
// else cout << "不空" << endl;
// //输出 不空 print(a2);print(a3);print(a4);//输出//0 0 0 0 0 0 0 0 0 0//2 2 2 2 2 2 2 2 2 2//1 2 3 4 5 return 0;
}
pudh_back / pop_back
#include <iostream>
#include <vector>using namespace std;const int N = 10;//初始化 struct node
{int a,b;string s;
}; void print(vector<int>& a)
{
// for(int i = 0; i < a.size(); i++)
// {
// cout << a[i] << " ";
// }
// cout << endl;//利用迭代器来便历 - 迭代器类型 vector<int>::iterator
// for(auto it = a.begin(); it != a.end(); it++)
// {
// cout << *it << " ";
// }
// cout << endl; //太麻烦了,c++给我们提供了一种//使用语法糖 - 范围forfor(auto x : a){cout << x << " ";} cout << endl;}int main()
{//1.创建 vectorvector<int> a1;//最简单的创建方式 (不需要初始化 //创建了一个名字为a1 的空的可变长数组,里面都是 int 类型 的数据vector<int> a2(N);//这种创建方式需要初始化 //即创建了一个大小为10 的可变长数组,里面的值默认为0 vector<int> a3(N,2);// 创建了一个大小为10 的可变长数组,里面的值默认为2 vector<int> a4 = {1,2,3,4,5};//初始化列表的创建方式//以上为核心 //<>--其里面可以存放任意一种数据类型,这就体现了模板的作用,也体现了模板的强大之处//这样,vector里面就可以存放我们见过的所有的数据类型,甚至是STL本身vector<string> a5; //存字符串vector<node> a6; //存结构体 vector<vector<int>> a7; //创建了一个可变长数组vector<int> a8[N];//与第二种方式分开,这里是[]//()是为其指定初始值//[]创建了一个大小为N的可变长数组 类比与//int a[N];//尾插以及尾删for(int i = 0;i < 5; i++){a1.push_back(i);print(a1); } while(!a1.empty()){print(a1);a1.pop_back();}
//输出
//0
//0 1
//0 1 2
//0 1 2 3
//0 1 2 3 4
//0 1 2 3 4
//0 1 2 3
//0 1 2
//0 1
//0 return 0;
}
front / back
1. push_back :尾部添加一个元素
2. pop_back :尾部删除一个元素 当然还有 insert 与 erase 。
不过由于时间复杂度过高,尽量不使用。
时间复杂度:O(1) 。
#include <iostream>
#include <vector>using namespace std;const int N = 10;//初始化 struct node
{int a,b;string s;
}; void print(vector<int>& a)
{
// for(int i = 0; i < a.size(); i++)
// {
// cout << a[i] << " ";
// }
// cout << endl;//利用迭代器来便历 - 迭代器类型 vector<int>::iterator
// for(auto it = a.begin(); it != a.end(); it++)
// {
// cout << *it << " ";
// }
// cout << endl; //太麻烦了,c++给我们提供了一种//使用语法糖 - 范围forfor(auto x : a){cout << x << " ";} cout << endl;}int main()
{//1.创建 vectorvector<int> a1;//最简单的创建方式 (不需要初始化 //创建了一个名字为a1 的空的可变长数组,里面都是 int 类型 的数据vector<int> a2(N);//这种创建方式需要初始化 //即创建了一个大小为10 的可变长数组,里面的值默认为0 vector<int> a3(N,2);// 创建了一个大小为10 的可变长数组,里面的值默认为2 vector<int> a4 = {1,2,3,4,5};//初始化列表的创建方式//以上为核心 //<>--其里面可以存放任意一种数据类型,这就体现了模板的作用,也体现了模板的强大之处//这样,vector里面就可以存放我们见过的所有的数据类型,甚至是STL本身vector<string> a5; //存字符串vector<node> a6; //存结构体 vector<vector<int>> a7; //创建了一个可变长数组vector<int> a8[N];//与第二种方式分开,这里是[]//()是为其指定初始值//[]创建了一个大小为N的可变长数组 类比与//int a[N];//尾插以及尾删
// for(int i = 0;i < 5; i++)
// {
// a1.push_back(i);
// print(a1);
// }
//
// while(!a1.empty())
// {
// print(a1);
// a1.pop_back();
// }
// // front / back cout << a4.front() << " " << a4.back() << endl;//输出 1 5 return 0;
}
resize
修改 vector 的大小。
• 如果大于原始的大小,多出来的位置会补上默认值,一般是 0 。
• 如果小于原始的大小,相当于把后面的元素全部删掉。
时间复杂度: O(N)
//resizevector<int> aa(5,1);print(aa);//1 1 1 1 1//扩大成 10aa.resize(10);print(aa);//1 1 1 1 1 0 0 0 0 0//缩小为3aa.resize(3);print(aa); //1 1 1
clear
• 清空 vector
底层实现的时候,会遍历整个元素,一个一个删除
时间复杂度:O(N)
#include <iostream>
#include <vector>using namespace std;const int N = 10;//初始化 struct node
{int a,b;string s;
}; void print(vector<int>& a)
{
// for(int i = 0; i < a.size(); i++)
// {
// cout << a[i] << " ";
// }
// cout << endl;//利用迭代器来便历 - 迭代器类型 vector<int>::iterator
// for(auto it = a.begin(); it != a.end(); it++)
// {
// cout << *it << " ";
// }
// cout << endl; //太麻烦了,c++给我们提供了一种//使用语法糖 - 范围forfor(auto x : a){cout << x << " ";} cout << endl;}int main()
{//1.创建 vectorvector<int> a1;//最简单的创建方式 (不需要初始化 //创建了一个名字为a1 的空的可变长数组,里面都是 int 类型 的数据vector<int> a2(N);//这种创建方式需要初始化 //即创建了一个大小为10 的可变长数组,里面的值默认为0 vector<int> a3(N,2);// 创建了一个大小为10 的可变长数组,里面的值默认为2 vector<int> a4 = {1,2,3,4,5};//初始化列表的创建方式//以上为核心 //<>--其里面可以存放任意一种数据类型,这就体现了模板的作用,也体现了模板的强大之处//这样,vector里面就可以存放我们见过的所有的数据类型,甚至是STL本身vector<string> a5; //存字符串vector<node> a6; //存结构体 vector<vector<int>> a7; //创建了一个可变长数组vector<int> a8[N];//与第二种方式分开,这里是[]//()是为其指定初始值//[]创建了一个大小为N的可变长数组 类比与//int a[N];//尾插以及尾删
// for(int i = 0;i < 5; i++)
// {
// a1.push_back(i);
// print(a1);
// }
//
// while(!a1.empty())
// {
// print(a1);
// a1.pop_back();
// }
// // front / back //cout << a4.front() << " " << a4.back() << endl;//输出 1 5 //resizevector<int> aa(5,1);print(aa);//1 1 1 1 1//扩大成 10aa.resize(10);print(aa);//1 1 1 1 1 0 0 0 0 0//缩小为3aa.resize(3);print(aa); //1 1 1//clearcout << aa.size() << endl;aa.clear() ;cout << aa.size() << endl; //输出//1 1 1 1 1//1 1 1 1 1 0 0 0 0 0//1 1 1//3//0 return 0;
}
补充pair
pair 是C++标准库中的一个模板类,用于将两个值组合成一个单一对象,通常用于存储键值对或返 回多个值。
它有两个公有成员 first 和 second ,分别表示第一个值和第二个值。
我们可以把 pair 理解成C++为我们提供一个结构体,里面有两个变量:
struct pair{type first;type second;};
使用的时候,可以指定 first 和 second 为我们想要的任意类型。
指定的方式为 pair< 第一个关键字的类型 , 第二个关键字的类型 > ,
比如:
pair<int, int> p1; // 第一个int,第二个intpair<long long, int> p2; // 第一个long long,第二个int pair<string, int> p3; // 第一个 sting,第二个int
不过,一般使用 pair 的时候,上述方式要写很多代码,我们一般会 typedef 一下:
typedef pair<int, int> PII;
PII p1;typedef pair<long long, long long> PLL;
PLL p2;
好咯uu们,顺序表就到这里咯,下一篇将写链表~
