数据结构顺序表

一.顺序表的概念

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静态实现

直接创建一个足够大的数组,来充当顺序表

静态分配就是直接向内存申请一大块连续的区域,然后将需要存放的数组放在这一大块连续的区域 上。 动态分配就是按需所取。按照需要存放的数据的数量,合理的申请大小合适的空间来存放数据。

优点:

  1. 不需要动态管理内存,代码书写上会比较方便。

  2. 没有动态管理内存中申请以及释放空间的时间开销。

缺点:

  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

  1. begin :返回起始位置的迭代器(左闭);

  2. 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

  1. push_back :尾部添加一个元素

  2. 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们,顺序表就到这里咯,下一篇将写链表~

相关推荐
煤球王子2 小时前
学而时习之:C++中的结构体
c++
码银2 小时前
【数据结构】单链表核心知识点梳理
数据结构
一只老丸2 小时前
HOT100题打卡第36天——二分查找
数据结构·算法
潼心1412o3 小时前
数据结构(长期更新)第7讲:栈
数据结构
散峰而望3 小时前
C++入门(算法) - 习题
开发语言·c++·算法·github
Fency咖啡3 小时前
redis进阶 - 底层数据结构
数据结构·数据库·redis
blog_wanghao3 小时前
PDF文件内容出现重叠现象解析
c++·pdf
yong99903 小时前
C++实现LBM模拟Couette流
开发语言·c++
2201_757830873 小时前
泛型的细节
java·开发语言·数据结构