一.顺序表的概念
1.线性表
线性表是一种简单的顺序表
线性表是n个具有相同特性的数据元素的有序序列。
线性表在逻辑上可以想象成是连续的一条线段,线段上有很多个点
相关术语:表头,表尾,前驱,后继,空表
1.2.线性表的存储结构:
1.线性表的顺序存储-顺序表--用顺序储存实现的线性表
2.链性存储--链表(后期讲)
顺序储存:
逻辑上相邻的元素,在内存中也存放在相邻的位置。
实际上,线性表的顺序储存就是用数组来实现的
二.顺序表的模拟实现
先声明,往后实现各种数据结构,如果没有事先说明,默认里面储存的是int类型的数据
顺序表可以用数组来实现,而数组有两种不同的申请方式
因此,顺序表也有两种方式
按照数组的申请方式,有以下两种实现方式:
1.数组采用静态分配,此时的顺序表称为静态顺序表。
2.数组采用动态分配,此时的顺序表称为动态顺序表
1.1.动态申请
通过new和delete来申请和释放空间
cpp
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静态实现
直接创建一个足够大的数组,来充当顺序表
静态分配就是直接向内存申请一大块连续的区域,然后将需要存放的数组放在这一大块连续的区域 上。 动态分配就是按需所取。按照需要存放的数据的数量,合理的申请大小合适的空间来存放数据。
优点:
-
不需要动态管理内存,代码书写上会比较方便。
-
没有动态管理内存中申请以及释放空间的时间开销。
缺点:
-
一旦空间占满,新来的数据就会溢出。
-
如果为了保险而申请很大的空间,数据量小的情况下,会浪费很多空间。
通过两者对比会发现,并没有一种实现方式就是绝对完美的。想要书写方便以及运行更快,就要承担空间不够或者空间浪费的情况;想要空间上合理分配,就要承担时间以及代码书写上的消耗。 在后续的学习中,会经常看到各种情况的对比。这就要求我们掌握各种数据结构的特点,从而在解决实际问题的时候,选择一个合适的数据结构。 在算法竞赛中,我们主要关心的其实是时间开销,空间上是基本够用的。因此,定义一个超大的静态 数组来解决问题是完全可以接受的。因此,关于顺序表,采用的就是静态实现的方式。
2.创建
创建一个足够大的数组充当顺序表:int a[1000010];
同时还需要一个变量,用来标记顺序表当前有多少个元素: int n = 0;
cpp
#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]开始储存。
cpp
#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:将顺序表中所有元素统一右移一位,然后放在表头--不能从前往后一个一个移动,可以从后往前移
cpp
#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
cpp
#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)
cpp
#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)
cpp
//头删
void pop_front()
{
//先把2~n区间的元素从前往后左移一个元素
for(int i = 2; i <= n; i++)
{
a[i - 1] = a[i];
}
n--;//左移完要删除元素
}
cpp
//测试头删
cout << "头删" << endl;
pop_front();
print();
pop_front();
print();
任意删
删除任意位置p的元素
涉及元素移动,时间复杂度为O(N)
cpp
//任意删
void erase(int p)
{
//把[p + 1,n]的元素,统一左移一位
for(int i = p + 1; i <= n; i++)
{
a[i - 1] = a[i];
}
n--;
}
cpp
//任意删
cout << "任意删" << endl;
erase(3);
print();
erase(2);
print();
以上删除操作仍然是有bug的,和插入时的bug差不多
查找元素
按值查找
查找成功:返回储存位置的下标
查找失败:返回0或者-1
最坏情况要便厉整个数组,时间复杂度位O(N);
cpp
#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)
cpp
//按位查找
int at(int p)
{
return a[p];
}
有bug。p的位置应该合法
修改元素
按位修改
时间复杂度为O(1)
cpp
//按位修改
int change(int p, int x)//把p位置的值改为x
{
a[p] = x;
}
p的位置应该合法
清空顺序表
我们这里实现的简单形式的时间复杂度为O(1)
但是正常应该为O(N)
cpp
//清空顺序表
int clear()
{
n = 0;
}
但是在有些情况下该代码是有问题的因为这里储存的都是int类型,如果是其他类型,需要移动数值
封装静态顺序表
使用类或者结构体封装一个静态顺序表
cpp
#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 的使用。
cpp
#include <iostream>
#include <vector>
using namespace std;
const int N = 10;//初始化
int main()
{
//1.创建 vector
vector<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
cpp
#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.创建 vector
vector<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;
//输出 不空
return 0;
}
begin / end
-
begin :返回起始位置的迭代器(左闭);
-
end :返回终点位置的下一个位置的迭代器(右开);
利用迭代器可以访问整个 vector ,存在迭代器的容器就可以使用范围 for 遍历。
cpp
#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++给我们提供了一种
//使用语法糖 - 范围for
for(auto x : a)
{
cout << x << " ";
}
cout << endl;
}
int main()
{
//1.创建 vector
vector<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
cpp
#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++给我们提供了一种
//使用语法糖 - 范围for
for(auto x : a)
{
cout << x << " ";
}
cout << endl;
}
int main()
{
//1.创建 vector
vector<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
-
push_back :尾部添加一个元素
-
pop_back :尾部删除一个元素 当然还有 insert 与 erase 。
不过由于时间复杂度过高,尽量不使用。
时间复杂度:O(1) 。
cpp
#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++给我们提供了一种
//使用语法糖 - 范围for
for(auto x : a)
{
cout << x << " ";
}
cout << endl;
}
int main()
{
//1.创建 vector
vector<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)
cpp
//resize
vector<int> aa(5,1);
print(aa);
//1 1 1 1 1
//扩大成 10
aa.resize(10);
print(aa);
//1 1 1 1 1 0 0 0 0 0
//缩小为3
aa.resize(3);
print(aa);
//1 1 1
clear
• 清空 vector
底层实现的时候,会遍历整个元素,一个一个删除
时间复杂度:O(N)
cpp
#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++给我们提供了一种
//使用语法糖 - 范围for
for(auto x : a)
{
cout << x << " ";
}
cout << endl;
}
int main()
{
//1.创建 vector
vector<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
//resize
vector<int> aa(5,1);
print(aa);
//1 1 1 1 1
//扩大成 10
aa.resize(10);
print(aa);
//1 1 1 1 1 0 0 0 0 0
//缩小为3
aa.resize(3);
print(aa);
//1 1 1
//clear
cout << 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++为我们提供一个结构体,里面有两个变量:
cpp
struct pair
{
type first;
type second;
};
使用的时候,可以指定 first 和 second 为我们想要的任意类型。
指定的方式为 pair< 第一个关键字的类型 , 第二个关键字的类型 > ,
比如:
cpp
pair<int, int> p1; // 第一个int,第二个int
pair<long long, int> p2; // 第一个long long,第二个int
pair<string, int> p3; // 第一个 sting,第二个int
不过,一般使用 pair 的时候,上述方式要写很多代码,我们一般会 typedef 一下:
cpp
typedef pair<int, int> PII;
PII p1;
typedef pair<long long, long long> PLL;
PLL p2;
好咯uu们,顺序表就到这里咯,下一篇将写链表~