目录
0、总结
Vector和List由C++ STL提供,分别#include<vector>和#include<list>就可以使用。反观C语言中就不存在Vector和List,若想使用必须自己编写相关代码。
Vector:顺序表
优点:和数组类似开辟一段连续的空间,并且支持随机访问,所以它的查找效率高其时间复杂度O(1)。
缺点:由于开辟一段连续的空间,所以插入删除会需要对数据进行移动比较麻烦,时间复杂度O(n),另外当空间不足时还需要进行扩容。具体扩容方法是:寻找一块大小为原向量空间2倍的连续空白内存,将原向量内容拷贝过去,并增加新的元素,最后删除原向量空间的数据。
List:链表
优点:底层实现是循环双链表,当对大量数据进行插入删除时,其时间复杂度O(1)
缺点:底层没有连续的空间,只能通过指针来访问,所以查找数据需要遍历其时间复杂度O(n),没有提供[]操作符的重载。
应用场景
vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用vector。list拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。
1、 vector
1.1 vector 说明
vector是向量类型,可以容纳许多类型的数据,因此也被称为容器
(可以理解为动态数组,是封装好了的类)
进行vector操作前应添加头文件#include <vector>
1.2 vector初始化:
方式1.
//定义具有10个整型元素的向量(尖括号为元素类型名,它可以是任何合法的数据类型),不具有初值,其值不确定
vector<int>a(10);
方式2.
//定义具有10个整型元素的向量,且给出的每个元素初值为1
vector<int>a(10,1);
方式3.
//用向量b给向量a赋值,a的值完全等价于b的值
vector<int>a(b);
方式4.
//将向量b中从0-2(共三个)的元素赋值给a,a的类型为int型
vector<int>a(b.begin(),b.begin+3);
方式5.
//从数组中获得初值
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7);
1.2 vector对象的常用内置函数使用(举例说明)
#include<vector>
vector<int> a,b;
//b为向量,将b的0-2个元素赋值给向量a
a.assign(b.begin(),b.begin()+3);
//a含有4个值为2的元素
a.assign(4,2);
//返回a的最后一个元素
a.back();
//返回a的第一个元素
a.front();
//返回a的第i元素,当且仅当a存在
a[i];
//清空a中的元素
a.clear();
//判断a是否为空,空则返回true,非空则返回false
a.empty();
//删除a向量的最后一个元素
a.pop_back();
//删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)结束
a.erase(a.begin()+1,a.begin()+3);
//在a的最后一个向量后插入一个元素,其值为5
a.push_back(5);
//在a的第一个元素(从第0个算起)位置插入数值5,
a.insert(a.begin()+1,5);
//在a的第一个元素(从第0个算起)位置插入3个数,其值都为5
a.insert(a.begin()+1,3,5);
//b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin()+1,b+3,b+6);
//返回a中元素的个数
a.size();
//返回a在内存中总共可以容纳的元素个数
a.capacity();
//将a的现有元素个数调整至10个,多则删,少则补,其值随机
a.resize(10);
//将a的现有元素个数调整至10个,多则删,少则补,其值为2
a.resize(10,2);
//将a的容量扩充至100,
a.reserve(100);
//b为向量,将a中的元素和b中的元素整体交换
a.swap(b);
//b为向量,向量的比较操作还有 != >= > <= <
a==b;
1.3. 顺序访问vector的几种方式,举例说明
1.3.1. 对向量a添加元素的几种方式
方式一.向向量a中添加元素
vector<int>a;
for(int i=0;i<10;++i){a.push_back(i);}
方式二.从数组中选择元素向向量中添加
int a[6]={1,2,3,4,5,6};
vector<int> b;
for(int i=0;i<=4;++i){b.push_back(a[i]);}
方式三.从现有向量中选择元素向向量中添加
int a[6]={1,2,3,4,5,6};
vector<int>b;
vector<int>c(a,a+4);
for(vector<int>::iterator it=c.begin();it<c.end();++it)
{
b.push_back(*it);
}
方式四.从文件中读取元素向向量中添加ifstream in("data.txt");
vector<int>a;
for(int i;in>>i){a.push_back(i);}
1.3.2从向量中读取元素
方式一.通过下标方式获取
int a[6]={1,2,3,4,5,6};
vector<int>b(a,a+4);
for(int i=0;i<=b.size()-1;++i){cout<<b[i]<<endl;}
方式二.通过迭代器方式读取
int a[6]={1,2,3,4,5,6};
vector<int>b(a,a+4);
for(vector<int>::iterator it=b.begin();it!=b.end();it++){cout<<*it<<" ";}
方式三.常见错误赋值方式
vector<int>a;
for(int i=0;i<10;++i){a[i]=i;}//下标只能用来获取已经存在的元素
1.4.其它几种常用的函数
#include<algorithm>
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列
sort(a.begin(),a.end());
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1
reverse(a.begin(),a.end());
//把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素
copy(a.begin(),a.end(),b.begin()+1);
//在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置
find(a.begin(),a.end(),10);
2、 list
2.1.list 说明
list 容器是由双向链表实现的,因此不能使用下标运算符 [] 访问其中的元素。
使用 list 的时候得加上 #include <list> 头文件以及得在 std 名字空间中使用。
2.2.list 定义和初始化
只需要简单的 list<TYPE> my_list; 就可以完成对一个 list 的定义了。不需要 new。
初始化的话就要用到 list 的构造函数。
一个简单的例子是:
int myints[] = {75,23,65,42,13};
list<int> mylist (myints, myints+5);
当然我们仍然可以使用一些函数对 list 增加删除元素。
2.3.list 函数介绍
这里介绍一些我觉得可能比较常用的函数。
迭代器:
函数名 | 作用 |
begin | 将迭代器定位到开头 |
end | 将迭代器定位到最后 |
rbegin | 将逆向迭代器定位到最后 |
rend | 将逆向迭代器定位到开头 |
C++ 11 标准又新增 cbegin, cend, crbegin 和 crend 这四个函数,返回的都是 const_iterator。
容量:
函数名 | 作用 |
empty | 检查容器是否为空 |
size | 返回当前容器内元素个数 |
max_size | 返回当前容器能容纳的最大元素数量 |
元素访问:
函数名 | 作用 |
front | 访问第一个元素 |
back | 访问最后一个元素 |
更改 list:
函数名 | 作用 |
assign | 为list分配新内容 |
push_front | 将元素插入到开头 |
pop_front | 删除第一个元素 |
push_back | 将元素插入到最后 |
pop_back | 删除最后一个元素 |
insert | 插入元素 |
erase | 删除元素 |
swap | 交换两个 list 内容 |
resize | 改变容器大小 |
clear | 删除容器所有内容 |
C++ 11 标准又增加了 emplace_front, emplace_back, emplace 这三个函数
操作:
函数名 | 作用 |
splice | 合并两个 list |
remove | 根据值删除元素 |
remove_if | 删除满足条件的元素 |
unique | 删除重复的值 |
merge | 合并两个 list并排序 |
sort | 对容器内的元素排序 |
reverse | 将元素反序 |
2.4.函数使用举例
这里介绍一些函数的使用例子,加深理解。函数的排序按字典序排的,方便从目录查找跳转。
1)assign()
#include <iostream>
#include <list>
using namespace std;
template <class T>
void print_list(list<T> my_list)
{
for (typename list<T>::iterator it = my_list.begin(); it != my_list.end(); ++it)
cout << ' ' << *it;
cout << '\n';
}
int main ()
{
list<int> first;
list<int> second;
first.assign (7, 100); // 7 ints with value 100
print_list(first);
// 100 100 100 100 100 100 100
second.assign (first.begin(),first.end()); // a copy of first
print_list(second);
// 100 100 100 100 100 100 100
int myints[]= {1776, 7, 4};
first.assign (myints, myints+3); // assigning from array
print_list(first);
// 1776 7 4
cout << "Size of first: " << int (first.size()) << '\n';
cout << "Size of second: " << int (second.size()) << '\n';
// Size of first: 3
// Size of second: 7
return 0
}
2)begin() —— 对 list 进行顺序遍历
end() 的代码和这个一模一样。
#include <iostream>
#include <list>
using namespace std;
int main ()
{
int myints[] = {75,23,65,42,13};
list<int> mylist (myints,myints+5);
cout << "mylist contains:";
for (list<int>::iterator it = mylist.begin(); it != mylist.end(); ++it)
std::cout << ' ' << *it;
cout << '\n';
// mylist contains: 75 23 65 42 13
return 0;
}
3)erase()——动态删除
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it1,it2;
// set some values:
for (int i=1; i<10; ++i) mylist.push_back(i*10);
// 10 20 30 40 50 60 70 80 90
it1 = it2 = mylist.begin(); // ^^
advance (it2,6); // ^ ^
++it1; // ^ ^
it1 = mylist.erase (it1); // 10 30 40 50 60 70 80 90
// ^ ^
it2 = mylist.erase (it2); // 10 30 40 50 60 80 90
// ^ ^
++it1; // ^ ^
--it2; // ^ ^
mylist.erase (it1,it2); // 10 30 60 80 90
// ^
cout << "mylist contains:";
for (it1=mylist.begin(); it1!=mylist.end(); ++it1)
cout << ' ' << *it1;
cout << '\n';
return 0;
}
使用 erase() 函数,我们可以实现动态的删除。
int main ()
{
list<int> mylist;
// set some values:
for (int i=1; i<5; ++i) mylist.push_back(i*10);
// 10 20 30 40
cout << "mylist contains:";
for (list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)
{
if (*it == 30)
it = mylist.erase(it);
cout << ' ' << *it;
}
cout << '\n';
// mylist contains: 10 20 40
return 0;
}
4)insert()——插入元素
insert 的参数还是到官网上查比较好,上面的表格里给出了链接。
下面的 ^ 表示当前的迭代器指向哪个元素。
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it;
// set some initial values:
for (int i=1; i<=5; ++i) mylist.push_back(i); // 1 2 3 4 5
it = mylist.begin();
++it; // it points now to number 2 ^
mylist.insert (it,10); // 1 10 2 3 4 5
// "it" still points to number 2 ^
mylist.insert (it,2,20); // 1 10 20 20 2 3 4 5
--it; // it points now to the second 20 ^
vector<int> myvector (2,30);
mylist.insert (it,myvector.begin(),myvector.end());
// 1 10 20 30 30 20 2 3 4 5
// ^
cout << "mylist contains:";
for (it=mylist.begin(); it!=mylist.end(); ++it)
cout << ' ' << *it;
cout << '\n';
// mylist contains: 1 10 20 30 30 20 2 3 4 5
return 0;
}
5)merge() —— 合并两个 list
#include <iostream>
#include <list>
using namespace std;
// compare only integral part:
bool mycomparison (double first, double second)
{
return ( int(first)<int(second) );
}
int main ()
{
list<double> first, second;
first.push_back (3.1);
first.push_back (2.2);
first.push_back (2.9);
second.push_back (3.7);
second.push_back (7.1);
second.push_back (1.4);
first.sort(); // 2.2 2.9 3.1
second.sort(); // 1.4 3.7 7.1
first.merge(second); // 1.4 2.2 2.9 3.1 3.7 7.1
// (second is now empty)
second.push_back (2.1); // 2.1
first.merge(second, mycomparison);
cout << "first contains:";
for (list<double>::iterator it=first.begin(); it!=first.end(); ++it)
cout << ' ' << *it;
cout << '\n';
// first contains: 1.4 2.2 2.9 2.1 3.1 3.7 7.1
return 0;
}
但是经过我的尝试,好像可以不排序合并:
int main ()
{
list<double> first, second;
for (int i=1; i<=5; ++i) first.push_back(i);
for (int i=1; i<=5; ++i) second.push_back(i+10);
first.merge(second);
// (second is now empty)
cout << "first contains:";
for (list<double>::iterator it=first.begin(); it!=first.end(); ++it)
cout << ' ' << *it;
cout << '\n';
// first contains: 1 2 3 4 5 11 12 13 14 15
return 0;
}
6)rbegin() —— 对 list 进行逆向遍历
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
for (int i=1; i<=5; ++i) mylist.push_back(i);
cout << "mylist backwards:";
for (list<int>::reverse_iterator rit=mylist.rbegin(); rit!=mylist.rend(); ++rit)
cout << ' ' << *rit;
cout << '\n';
// mylist backwards: 5 4 3 2 1
return 0;
}
利用『逆向』这个特性,我们还可以用来实现逆向排序。STL 里的 sort 默认是按自然数顺序来排序的,要想实现从大到小排序的效果,我们有三种方法:
自己写比较函数
struct greater
{
template<class T>
bool operator()(T const &a, T const &b) const { return a > b; }
};
std::sort(numbers.begin(), numbers.end(), greater());
指定比较规则
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
使用 rbegin()
std::sort(numbers.rbegin(), numbers.rend()); // note: reverse iterators
7)remove()
#include <iostream>
#include <list>
using namespace std;
int main ()
{
int myints[]= {17,89,7,14};
list<int> mylist (myints,myints+4);
// 17 89 7 14
mylist.remove(89);
cout << "mylist contains:";
for (list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)
cout << ' ' << *it;
cout << '\n';
// mylist contains: 17 7 14
return 0;
}
它会删除所有匹配的值,也就是说
int myints[]= {17,89,7,89,14};
list<int> mylist (myints,myints+5);
// 17 89 7 89 14
mylist.remove(89);
// mylist contains: 17 7 14
如果你想像下面这样写的话,会出现死循环。
int main ()
{
int myints[]= {17,89,7,14};
list<int> mylist (myints,myints+4);
// 17 89 7 14
cout << "mylist contains:";
for (list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it) {
if (*it == 89)
mylist.remove(89);
cout << ' ' << *it;
}
cout << '\n';
// 死循环
return 0;
}
想要达到上述的效果,可以使用 erase() 函数。具体代码见那一节。
参考文献链接:
https://blog.csdn.net/weixin_41743247/article/details/90635931
https://blog.csdn.net/sinat_41104353/article/details/84900018