c++面试知识点总结

STL容器篇

C++面试基础中不可避免会问到STL容器相关知识点,STL容器包含两大类:序列式容器 & 关联容器
序列式容器:元素都可序(ordered),但是未必有序(sorted),通过元素的顺序来访问,包含vector、list、dequeue、stack、queue、priority-queue。
关联容器:数据以key-value的形式组成,可通过key值访问元素,包含RB-tree、map、set、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap;

萃取

关于萃取可参考下面文章:
https://yosef-gao.github.io/2016/10/05/cpp-traits/

Vector

vector可以看作式一种动态数组,普通的数组申明时需要固定大小但是vector不需要。向vector中添加元素时会存在“重新分配空间、元素移动、释放原空间”等步骤,
1.在当前capacity不足以存放新元素时,需要将vector扩张至当前空间的2倍大小,若还不够则继续扩张;
2.上述的扩张过程对于大对象在移动过程中会很损耗性能;

List

list vs vector
1、list 相较于vector的特点是可以很方便的删除节点和插入节点,删除节点和插入节点的都不会造成原节点的失效,在某些情况下插入和删除节点的效率相较于vector要高;
2、因为list的每个节点只保存前一节点(pre)或者后一节点(next)的指针,需要读取或者查找list节点时,需以其中一个节点为起始遍历比较,所以查找和读取节点不如vector方便。

list迭代器的操作:递增时指向下一节点,递减时指向上一节点,取值时是节点的数据值,成员取用的是节点的成员。

deque & stack & queue

deque:双向开口的连续空间,可以在头尾两端分别做元素的插入和删除操作,
没有容量的限制,随时可以新增一段新的空间链接起来,但是迭代器很复杂。每个节点至少保存三种数据结构:中控节点map、起始节点start、终止节点end,数据节点(数据节点的个数不少于8个或者是num+2个,num为已经存在的需要的数据节点,其中多余的两个是buffer)。dequeue允许以常数时间内对头端进行元素的插入和移除操作。
stack:是一种先进后出的数据结构,底层以deque实现,或者以list实现也可;
queue:是一种先进先出数据结构,可以dequeue或者list为底层容器;

Priority Queue

Priority Queue的底层实现是heap(堆)。
heap:有最大堆(优先级最高的在堆顶)和最小堆(优先级最低的在堆顶),下面介绍的堆均默认为最大堆。
性质:
(1)heap是一颗完全二叉树,除叶子节点外,每一层均是满的;
(2)叶子节点从左至右不得有空隙;
(3)所有子树的根节点均大于叶子节点;
heap有关的数据结构:
(1)一颗完全二叉树
(2)一个长度为n(树元素的个数)数组arr
假设当前节点在数组arr的index为i,则左子节点arr的index为2i,右子节点的index为2i+1;
heap中插入元素:将待插入的节点插入至树的最右子节点,然后一次上溯调整树,使其满足上述三点性质;
pop-heap:首先删除最顶上元素,为了满足complete binary tree的条件,必须割舍最下层右边的叶子节点。并将其值重新安插至max-heap(因此有必要重新调整heap结构)

仿函数

仿函数:顾名思义,就是其行为像函数一样的对象;
一般的应用场景:仿函数可以理解为针对一种算法思路或者是策略的封装,一般要将这类函数当做算法的参数使用,需要将封装好的函数作为函数指针传入调用函数,但是函数指针并不支持模板化;

在面向对象程序设计过程中,将“操作”和数据结构解藕是比较高端的设计方式(反正我等菜鸟还在领悟中);例如将指定范围内的所有元素都相加有两种实现过程,一种是将所有元素从头至尾都加上,一种是设计仿函数实现“两两相加”;肯定第二种方式更高端塞。
废话少说,上代码如下:编译环境g++ (GCC) 4.8.2,此代码可能在其他环境不行,因为没有加类型萃取

#include <iostream>

template<class T>
struct plus {
    T operator()(const T& x, const T& y) { return x + y;}
};

template<class T>
struct minus {
    T operator()(const T&x, const T&y) {return x -y;}
};

int main() {
    std::cout << plus<int>()(3, 4) << std::endl;  //不是plus<int>(3, 4)哈
    std::cout << minus<int>()(6,7) << std::endl;
    return 0;
}

输出:
7
-1

STL中常见的仿函数使用方式如下

algorithm(first, last, ..., functionObj)
{
	...
	functionobj();
	...
}

算法泛型化的过程

算法泛型化的过程可以理解为抽象化的过程,具体方法论:
(1)将操作对象游标化,即关注元素移动而不是操作对象的容器类型,相当于iterator的产生原因;
(2)将具体的“操作”(算法的过程)抽象化,例如上述的仿函数;

int* find(int* arrayHead, int arraySize, const int& value) {
    int i = 0;
    for (; i < arraySize; i++) {
        if (arrayHead[i] == value) {
            break;
        }
    }
    return &(arrayHead[i]);
}

以上代码对容器的依赖性比较强(例如,必须计算出容器的size),同时对于最后一个元素的判断不好处理,事实上stl的遵循“前闭后开”的原则,可以使用指针指向最后一个元素的next元素(end),但是无法针对end提取值,因为end指向的是最后一个元素的下一个节点,可能会为null,故改进find算法如下:

int* find(int* start, int* end, const int& value) {
    while (start != end) {
        if (*start == value) {
            break;
        }
        ++start;
    }
    return start;
}

关联式容器

在这里插入图片描述
上图通过内缩表示各个容器的包含关系,例如vector底层实现是内建类型array,heap内部包含容器vector及树。。。。。
本节重点剖析关联式容器,介绍关联容器之前势必需涉猎一点树的前世今生。

1、二叉搜索树-set/map/multiset/multimap

    二叉树:即每个节点只有左右子节点;
     二叉搜索树:所有左子树节点的值小于根节点的值;所有右子树的值大于根节点的值;
     插入节点时,可从根节点开始,遇到键值大的就向左,遇到键值大的就向右边;
     删除节点时,若当前节点只有一个节点,则删除当前节点,并利用其子节点替代它原来的位置;若当前节点还有其他节点,则用右子树最小的节点替代当前节点;
  2、平衡二叉搜索树
   所谓的平衡是指没有任何一个节点过深。
   **AVL tree**:任何节点左右子树高度相差最多1,插入元素时,为了满足平横性,借助单旋(节点外侧插入元素)或者双旋(节点内侧插入元素)调整树的平衡性。
   **RB-tree**:不仅是一个二叉搜索树,还必须满足如下条件:
   (1)每个节点不是红色就是黑色;
   (2)根节点必须是黑色;
   (3)不能有两个连续的红色节点;
   (4)任一节点至NULL的任何路径,所含黑色节点数必须相同;
RB-tree插入和删除元素的时间复杂度都是logN,且与vector不同,基于 RB-tree的容器——set/map/multiset/multimap等插入和删除之前的迭代器均不会失效,因为iterator是一个指向内存的指针,RB-tree插入和删除时原节点的存储位置并没有变化,变化的只是节点的right或者left指针,故iterator不会失效。

  
   set与multiset  map与multimap 的区别在于set和map插入元素时调用的接口是insert_unique() ,而multiset和multimap插入元素时调用的接口是insert_equal()。

hashtable

一种插入、删除、搜寻等操作具有“常数平均时间”的表现的容器。
当有不同的元素经过hash函数hash后映射到相同的位置,即所谓的”碰撞“,解决碰撞的方法:线性探测、二次探测、开链等。其中开链是重点,所谓的开链是在每一个表格元素中维护一个list;hash function为我们分配一个list,然后我们在那个list身上执行元素的插入、搜寻、删除等操作。
底层以hashtable数据结构实现的容器有hash_set/hash_map/hast_multimap/hash_multiset

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值