某日二师兄参加XXX科技公司的C++工程师开发岗位第24面:

面试官:list用过吗?

二师兄:嗯,用过。

面试官:请讲一下list的实现原理。

二师兄:std::list被称为双向链表,和C中手写双向链表本质上没有大的区别。list对象中有两个指针,一个指向上一个节点(node),一个指向下一个节点(node)。

二师兄:与手写双向链表不同的是,list中有一个base node,此node并不存储数据,从C++11开始,此node中包含一个size_t类型的成员变量,用来记录list的长度。

二师兄:所以说从C++11开始,size()的时间复杂度是O(1),在此之前是O(N)

面试官:是每个node都包含一个记录长度的成员变量吗?

二师兄:不是,GCC中的实现只有在header node上记录了长度信息,其他node并没有记录。

  1. struct _List_node_base
  2. {
  3. _List_node_base* _M_next;
  4. _List_node_base* _M_prev;
  5. ...
  6. };
  7. struct _List_node_header : public _List_node_base
  8. {
  9. #if _GLIBCXX_USE_CXX11_ABI
  10. std::size_t _M_size;
  11. #endif
  12. ...
  13. };

面试官:添加和删除元素会导致迭代器失效吗?

二师兄:并不会,因为在任意位置添加和删除元素只需要改变prev/next指针指向的对象,而不需要移动元素的位置,所以不会导致迭代器失效。

面试官:listvector相比,有哪些优势?什么情况下使用list,什么情况下使用vector

二师兄:主要有2点优势:1.list在随机插入数据不会导致数据的搬移。2.list随机删除也不会导致数据搬移。所以在频繁的随机插入/删除的场景使用list,其他场景使用vector

面试官:你知道std::sort和list成员函数sort有什么区别吗?

二师兄:std::sort是STL算法的一部分。它排序的容器需要有随机访问迭代器,所以只能支持vectordequelist成员函数sort用于list排序,时间复杂度是O(N*logN)

面试官:forward_list了解吗?知道如何实现的吗?

二师兄:std::forward_list是C++11引入的新容器之一。它的底层是单向链表,引入它的主要目的是为了达到手写链表的性能。同时节省了部分内存空间。(只有一根指针)

面试官:listpop_front/pop_back的时候需要注意哪些问题?

二师兄:需要判断listsize()不能为0,如果list为空,pop_front/pop_back会导致coredump

面试官:你知道list的成员函数insertforward_list的成员函数的insert_after有什么区别?

二师兄:两者都可以向特定位置添加元素。不同的是insert把元素插入到当前迭代器前,而insert_after把元素插入到当前迭代器后。

面试官:以下代码的输出是什么?

  1. #include <iostream>
  2. #include <list>
  3. int main(int argc, char const *argv[])
  4. {
  5. std::list<int> li = {1,2,3,4,5,6};
  6. for(auto it = li.begin(); it!= li.end(); ++it)
  7. {
  8. if(0 == *it % 2) li.erase(it);
  9. }
  10. for(auto& i : li) std::cout << i << " ";
  11. std::cout << std::endl;
  12. }

二师兄:应该是1 3 5

面试官:遍历两个元素数目相同的vectorlist,哪个效率高?

二师兄:vectorlist的遍历效率都是O(N),效率应该是一样的。

面试官:好的,回去等通知吧。

让我们看以下二师兄今日的表现:

以下代码的输出是什么?

这里实际上会输出Segmentation fault,原因是因为当从listerase这个node,这个nodeprevnext指针被清空,而++it是通过当前的nodenext指针去找下一个node,解引用一个空指针,导致coredump

erase函数返回下一个有效迭代器,所以可以把if(0 == *it % 2) li.erase(it)修改为if(0 == *it % 2) it = li.erase(it)来解决这个问题。

遍历两个元素数目相同的vectorlist,哪个效率高?

这里二师兄回答的倒是没有毛病,但是没有考虑到缓存问题。实际上因为vector底层采用数组存储数据,所以它的空间局部性更好,对缓存更友好(Cache-friendly),所以遍历vector的效率要高于遍历list

最后多啰嗦一点,如果你没有特别的理由选择其他容器,使用vector是最好的选择。

二师兄今日的面试旅程结束了,感谢各位小伙伴的关注和点赞。为了保证面试质量,以后不一定能保证日更。文章中有任何技术性问题,请留言反馈,在此感谢!

关注我,带你21天“精通”C++!(狗头)

C++面试八股文:std::vector和std::list,如何选择?的更多相关文章

  1. 实战c++中的string系列--std:vector 和std:string相互转换(vector to stringstream)

    string.vector 互转 string 转 vector vector  vcBuf;string        stBuf("Hello DaMao!!!");----- ...

  2. std::vector与std::list效能对比(基于c++11)

    测试对象类型不同,数量级不同时,表现具有差异: 测试数据对象为std::function时: test: times(1000)vector push_back time 469 usvector e ...

  3. 编程杂谈——std::vector与List<T>的性能比较

    昨天在比较完C++中std::vector的两个方法的性能差异并留下记录后--编程杂谈--使用emplace_back取代push_back,今日尝试在C#中测试对应功能的性能. C#中对应std:: ...

  4. Item 21: 比起直接使用new优先使用std::make_unique和std::make_shared

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 让我们先从std::make_unique和std::make_s ...

  5. 使用std::map和std::list存放数据,消耗内存比实际数据大得多

    使用std::map和std::list存放数据,消耗内存比实际数据大得多 场景:项目中需要存储一个结构,如下程序段中TEST_DATA_STRU,结构占24B.但是使用代码中的std::list&l ...

  6. c++转载系列 std::vector模板库用法介绍

    来源:http://blog.csdn.net/phoebin/article/details/3864590 介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作 ...

  7. C++ 中的std::vector介绍(转)

    vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vec ...

  8. std::vector介绍

    vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vec ...

  9. std::vector<Channel2*> m_allChannels;容器,以及如何根据channelid的意义

    std::vector<Channel2*> m_allChannels;容器,以及如何根据channelid的意义 这个容器保存了所有客户端连接的channel Channel2* Li ...

  10. std::vector数据复制

    std::vector<boost::shared_ptr <ITEM> > srcItemList;  // 数据源 std::vector<ITEM>  des ...

随机推荐

  1. 在Kubernetes(k8s)中部署 jenkins

    在Kubernetes(k8s)中部署 jenkins YAML配置文件 由于jenkins需要持久化存储,通过nfs动态供给pvc存储卷. 可以参考我之前的文档:https://cloud.tenc ...

  2. [数据库/MySQL]数据库备份与升级:MySQL Percona(RPM) 5.7.24-27 升级到 5.7.31-34

    1 数据库升级方式:RPM包方式升级 [亲测有效] 环境 OS: CENTOS 7 DB: MYSQL 5.7.24-27 1.1 数据库备份 备份以防止升级失败 备份数据库的2个主要方法: 1)用M ...

  3. 从k8s 的声明式API 到 GPT的 提示语

    命令式 命令式有时也称为指令式,命令式的场景下,计算机只会机械的完成指定的命令操作,执行的结果就取决于执行的命令是否正确.GPT 之前的人工智能就是这种典型的命令式,通过不断的炼丹,告诉计算机要怎么做 ...

  4. 【Note】贪心

    感谢 $ \text{orzws/chy} $ 倾情授课. 目录 -1. 证明方式 0. 朴素贪心 AT2557 [ARC073C] Ball Coloring P2587 [ZJOI2008]泡泡堂 ...

  5. Python常见面试题016. 请实现如下功能|谈谈你对闭包的理解

    016. 请实现如下功能|谈谈你对闭包的理解 摘自<流畅的python> 第七章 函数装饰器和闭包 实现一个函数(可以不是函数)avg,计算不断增加的系列值的平均值,效果如下 def av ...

  6. SQL Case条件判断语句

    问题描述:在表中取到一些值做出判断,配合监控监测一些表中的数据.使用select case when if 来做条件查询判断 CASE 表达式遍历条件并在满足第一个条件时返回一个值(类似于 if-th ...

  7. 百度松果菁英班--oj赛(第二次)

    目录 一.小码哥剪绳子 二.咖啡品鉴师小码哥 三.均分糖果 四.持盾 五.活动安排 六.甜品供应 七.斐波那契数列的组合 八.小码哥的布阵指挥 九.活动分组 十.外卖递送 一.小码哥剪绳子 题目:马上 ...

  8. Go语言:两种常见的并发模型

    Go语言:两种常见的并发模型 在并发编程中,须要精确地控制对共享资源的访问,Go语言将共享的值通过通道传递 并发版"Hello World" 使用goroutine来打印" ...

  9. .NET Web入门到高级路线(新版本)

    .NET Web入门到高级路线 C# 基础语法 .NET Core 基础知识 ASP.NET Core基础知识概述 Blazor ASP.NET Core 官方文档 ORM FreeSql Entit ...

  10. 图像I、P、B帧介绍

    I.p.b 帧 I帧:帧内编码帧 :尽可能去除图像空间冗余信息来压缩传输数据量的帧内编码图像:P帧:前向预测编码帧: 通过充分将低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像,也叫 ...