【C++】list的介绍和使用(下)

简介: 【C++】list的介绍和使用(下)

Operations


remove


remove 相当于 find + erase,如果想要删除的元素不错在,也不会报错。


void TestList6()
{
  list<int> lt;
  lt.push_back(1);
  lt.push_back(2);
  lt.push_back(3);
  lt.push_back(3);
  lt.push_back(4);
  lt.remove(3);
  for (auto e : lt)
  {
    cout << e << " ";
  }
  cout << endl;
}

3889944bde784ac7905738f95401e958.png

sort


void TestList7()
{
  list<int> lt;
  lt.push_back(10);
  lt.push_back(2);
  lt.push_back(5);
  lt.push_back(3);
  lt.push_back(4);
  lt.push_back(4);
  lt.push_back(6);
  lt.push_back(4);
  lt.push_back(0);
  lt.sort();
  for (auto e : lt)
  {
    cout << e << " ";
  }
  cout << endl;
}

dd3e64304dea47849f3045f948a30ffc.png


算法库不是用排序函数吗,为什么 list 还要自己实现一个 sort 函数呢?这就要知道迭代器的分类了。


迭代器的分类
1.单向 只能++
2.双向 可以++和--
3.随机 可以++ -- + -


算法库中的 sort 函数是快速排序,需要支持随机访问和指针相减,否则三数取中无法支持。而 list 中的节点地址是随机的,无法通过指针相减来得到元素的个数。所以,需要为 list 单独设计一个 sort 函数。注:list 的 sort 函数是用归并排序实现的。

b0ab767c6f764907b18e3965fbbf76e5.png


排序性能的比较


vecor 和 list 的比较


// N个数据需要排序,vector+ 算法sort  list+ sort
void Test()
{
  srand(time(0));
  const int N = 100000;
  vector<int> v;
  v.reserve(N);
  list<int> lt1;
  list<int> lt2;
  for (int i = 0; i < N; ++i)
  {
    auto e = rand();
    v.push_back(e);
    //lt1.push_back(e);
    lt2.push_back(e);
  }
  int begin1 = clock();
  sort(v.begin(), v.end());
  int end1 = clock();
  int begin2 = clock();
  lt2.sort();
  int end2 = clock();
  printf("vector sort:%d\n", end1 - begin1);
  printf("list sort:%d\n", end2 - begin2);
}


先用 vector 排序后,再将数据拷贝回 list 中


// N个数据需要排序,vector+ 算法sort  list+ sort
void Test()
{
  srand(time(0));
  const int N = 1000000;
  vector<int> v;
  v.reserve(N);
  list<int> lt1;
  list<int> lt2;
  for (int i = 0; i < N; ++i)
  {
    auto e = rand();
    //v.push_back(e);
    lt1.push_back(e);
    lt2.push_back(e);
  }
  // 拷贝到vector排序,排完以后再拷贝回来
  int begin1 = clock();
  for (auto e : lt1)
  {
    v.push_back(e);
  }
  sort(v.begin(), v.end());
  size_t i = 0;
  for (auto& e : lt1)
  {
    e = v[i++];
  }
  int end1 = clock();
  int begin2 = clock();
  lt2.sort();
  int end2 = clock();
  printf("vector sort:%d\n", end1 - begin1);
  printf("list sort:%d\n", end2 - begin2);
}


list 的排序总体来说还是比 vector 的排序要慢得,所以个人不太推荐使用 list 的进行排序。


unique


unique 是一个去重函数,该函数需要在排完序后才能够使用。


void TestList8()
{
  list<int> lt;
  lt.push_back(10);
  lt.push_back(2);
  lt.push_back(5);
  lt.push_back(3);
  lt.push_back(4);
  lt.push_back(4);
  lt.push_back(6);
  lt.push_back(4);
  lt.push_back(0);
  // 先排序,才能去重
  lt.sort();
  lt.unique();
  //sort(lt.begin(), lt.end());
  for (auto e : lt)
  {
    cout << e << " ";
  }
  cout << endl;
}

6182f40eda1144a09e876405dbd8c6ef.png


splice


splice 可以将链表中的节点转移到另一个链表中。


void TestList9()
{
  list<int> lt1;
  lt1.push_back(1);
  lt1.push_back(1);
  lt1.push_back(1);
  list<int> lt2;
  lt2.push_back(2);
  lt2.push_back(3);
  lt2.push_back(4);
  lt2.push_back(5);
  lt1.splice(lt1.end(), lt2);
  for (auto e : lt1)
  {
    cout << e << " ";
  }
  cout << endl;
  cout << lt1.size() << endl;
  cout << lt2.size() << endl;
}

ea57959b524c4c21b7918154c2eba556.png


👉vector 和 list 的对比👈


      vector        list
--------------------------------------------      
底层结构    动态顺序表,一段    带哨兵位头节点
      连续的空间     的双向循环链表
--------------------------------------------
随机访问    支持随机访问,访    不支持随机访问,
      问某个元素效率O(1) 访问某个元素效率O(N)
--------------------------------------------
插入和删除    任意位置插入和删除    任意位置插入和删除
      效率低,需要搬移元素, 效率高,不需搬移元
      时间复杂度为O(N), 素,时间复杂度为O(1)
      插入时有可能需要增容
      增容:开辟新空间,拷
      贝元素,释放旧空间,
      导致效率更低
--------------------------------------------
空间利用率   底层为连续空间,不容  底层节点动态开辟,
       易造成内存碎片,空间 小节点容易造成内
       利用率高,缓存利用率 存碎片,空间利用率
       高              低,缓存利用率低
--------------------------------------------
迭代器失效   在插入元素时,要给所  插入元素不会导致迭
       有的迭代器重新赋值, 代器失效,删除元素
       因为插入元素有可能    时,只会导致当前迭
       会导致重新扩容,致使 代器失效,不会影响
       原来迭代器失效,删除 其他迭代器
       时,当前迭代器需要重
       新赋值否则也会失效
--------------------------------------------  
使用场景    需要高效存储,支持随    大量插入和删除操作,
       机访问,不关心插入删 不关心随机访问
       除效率     


👉总结👈


本篇博客主要介绍了 list 的常用的函数接口,难度不是很大。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️










相关文章
|
12天前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
27 7
|
20天前
|
存储 编译器 C++
C++ initializer_list&&类型推导
在 C++ 中,`initializer_list` 提供了一种方便的方式来初始化容器和传递参数,而右值引用则是实现高效资源管理和移动语义的关键特性。尽管在实际应用中 `initializer_list&&` 并不常见,但理解其类型推导和使用方式有助于深入掌握现代 C++ 的高级特性。
16 4
|
2月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
67 2
|
2月前
|
存储 算法 C++
【C++打怪之路Lv10】-- list
【C++打怪之路Lv10】-- list
23 1
|
2月前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
71 5
|
2月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
84 2
|
2月前
|
C++
【C++】C++ STL 探索:List使用与背后底层逻辑(三)
【C++】C++ STL 探索:List使用与背后底层逻辑
|
2月前
|
C++
【C++】C++ STL 探索:List使用与背后底层逻辑(二)
【C++】C++ STL 探索:List使用与背后底层逻辑
|
2月前
|
存储 编译器 C++
【C++】C++ STL 探索:List使用与背后底层逻辑(一)
【C++】C++ STL 探索:List使用与背后底层逻辑
|
2月前
|
存储 缓存 C++
C++番外篇——list与vector的比较
C++番外篇——list与vector的比较
28 0