1.unordered_set和set,unordered_map和map的区别
set
和 map
是 C++ STL 中的两种关联容器,而 unordered_set
和 unordered_map
是 C++11 新增的基于哈希表的关联容器。它们之间的主要区别在于底层的数据结构和操作复杂度。
-
set
和unordered_set
:set
是一个基于红黑树实现的有序集合,其中的元素按照一定的排序规则进行排列。unordered_set
是基于哈希表实现的无序集合,元素没有特定的顺序,但插入、查找和删除操作的平均时间复杂度为 O(1)。- 因此,如果对集合中的元素有顺序要求,可以使用
set
;如果不需要顺序,并且对查找性能有较高要求,可以选择unordered_set
。
-
map
和unordered_map
:map
是一个基于红黑树实现的有序键值对容器,其中的键值对按照键的大小进行排序。unordered_map
是基于哈希表实现的无序键值对容器,键值对没有特定的顺序,但插入、查找和删除操作的平均时间复杂度为 O(1)。- 类似地,如果对键值对有顺序要求,可以使用
map
;如果不需要顺序,并且对查找性能有较高要求,可以选择unordered_map
。
总的来说,unordered_set
和 unordered_map
在插入、查找和删除操作的平均时间复杂度上具有更好的性能,但不保证元素的顺序;而 set
和 map
则提供了有序的元素访问。在选择使用时,可以根据实际需求和性能要求来进行选择。
2.哈希表和红黑树
哈希表和红黑树是两种常见的数据结构,用于实现集合、映射等关联容器。它们在实际应用中具有不同的特点和适用场景。
-
哈希表(Hash Table):
- 哈希表是一种通过哈希函数将键映射到数组索引的数据结构。它的特点是可以实现快速的插入、查找和删除操作,时间复杂度接近 O(1)。
- 哈希表的核心是哈希函数,它能够将键映射到数组的索引位置。理想情况下,哈希函数能够将键均匀地映射到数组中,避免产生冲突。
- 哈希表在实际应用中广泛使用,例如 C++ STL 中的
unordered_set
和unordered_map
就是基于哈希表实现的。
-
红黑树(Red-Black Tree):
- 红黑树是一种自平衡的二叉搜索树,它保持了良好的平衡性质,因此能够保证插入、删除和搜索操作的最坏情况时间复杂度为 O(log n)。
- 红黑树通过维护节点的颜色和执行旋转操作来保持平衡,确保树的高度保持在一个较小的范围内。
- 红黑树在实际应用中常被用作关联容器的底层实现,例如 C++ STL 中的
set
和map
就是基于红黑树实现的。
总的来说,哈希表适合于需要快速插入、查找和删除操作的场景,并且不要求元素有序;而红黑树适合于有序元素访问,并且需要保证插入、删除和搜索操作的稳定性和较低时间复杂度的场景。在实际使用中,根据具体应用的需求和特点,选择合适的数据结构来实现相应的功能。
3.B+树
B+树是一种常见的平衡树数据结构,被广泛应用于数据库和文件系统中。它相对于B树有一些特殊的设计,使得在磁盘上进行范围查询等操作时效率更高。
下面是关于B+树的详细介绍:
-
结构:
- B+树由根节点、内部节点和叶子节点组成,其中内部节点用于索引和导航,叶子节点存储实际数据。
- 所有叶子节点通过指针连接成一个有序链表,方便范围查询和遍历。
-
特点:
- 所有数据条目都存储在叶子节点中,内部节点只包含索引信息,这样可以使得范围查询更加高效。
- 叶子节点之间通过指针连接,形成有序的双向链表,方便范围查询和顺序遍历。
- 内部节点的子节点数与索引数相同,提高了空间利用率。
-
操作:
- 查找:从根节点开始,根据键值进行比较,沿着内部节点逐层搜索,直到找到对应的叶子节点。
- 插入:首先按照查找操作找到要插入的位置,然后插入到叶子节点中,并保持树的平衡。
- 删除:类似于插入操作,找到要删除的数据所在的叶子节点并删除,同时保持树的平衡。
-
优点:
- B+树在磁盘存储和范围查询方面具有优势,因为每个节点存储的数据更多,减少了磁盘I/O次数。
- 叶子节点之间有序连接,可以快速进行范围查询和遍历操作。
总的来说,B+树是一种高效的平衡树数据结构,特别适合在数据库和文件系统中应用,能够提高范围查询和顺序访问的效率,并且保持了良好的平衡性质和空间利用率。
4.虚函数和纯虚函数的区别
在C++中,虚函数和纯虚函数是面向对象编程中重要的概念,它们在面向对象设计中起着关键作用。下面是它们的区别:
-
虚函数(Virtual Function):
- 虚函数是在基类中声明为虚函数的成员函数。当派生类继承并重写了这个虚函数时,在运行时将根据对象的实际类型来调用相应的函数。
- 基类中的虚函数可以被派生类覆盖,即在派生类中重新定义同名的虚函数,这样在调用时会根据对象的实际类型来选择调用哪个版本的函数。
-
纯虚函数(Pure Virtual Function):
- 纯虚函数是在基类中声明但没有实现的虚函数,通常在声明时使用“= 0”来表示。它要求任何继承自该基类的派生类都必须实现这个纯虚函数。
- 含有纯虚函数的类称为抽象类,不能被实例化,只能作为基类来派生新的类。派生类必须实现基类中的纯虚函数,否则派生类也会变成抽象类。
总的来说,虚函数是在基类中声明为虚函数的成员函数,可以被派生类覆盖;而纯虚函数是没有实现的虚函数,要求派生类实现,并且含有纯虚函数的类是抽象类,不能被实例化。这两种函数在多态性和面向对象设计中起着非常重要的作用。
5.多态是如何实现的
在C++中,多态性通过虚函数来实现。当基类中的成员函数被声明为虚函数时,派生类可以覆盖这些虚函数,并在运行时根据对象的实际类型来调用相应的函数。这种机制称为动态多态性。
6.深拷贝和浅拷贝
下面是C++中深拷贝和浅拷贝的区别:
-
浅拷贝(Shallow Copy):
- 浅拷贝是指将一个对象的值复制到另一个对象,包括对象中的基本数据类型和指针。如果对象中有指针指向堆内存,浅拷贝只会复制指针的地址,而不会复制指针指向的具体内容。
- 当进行浅拷贝时,两个对象会共享同一块内存空间,如果其中一个对象修改了堆内存中的数据,另一个对象也会受到影响。
-
深拷贝(Deep Copy):
- 深拷贝是指将一个对象的值以及其指向的动态分配的内存复制到另一个对象,包括对象中的基本数据类型和指针指向的动态内存。
- 当进行深拷贝时,会创建一个新的内存空间,并将原对象中的数据复制到新的内存空间中,这样两个对象之间互不影响。
在C++中,如果类中包含指向动态分配内存的指针,通常需要实现深拷贝构造函数和赋值运算符重载函数,以确保在对象复制时不会出现内存访问问题。深拷贝会创建新的内存空间,从而避免了浅拷贝可能引起的指针悬挂和内存泄漏问题。
7.sort快排源码
快速排序的算法思想是分治思想,具体可以通过三步来实现;
- 确定分界点x:
常用的取值分界点是: q[l] q[(l+r)/2] q[r]; - 调整区间(核心步骤):
使分界点以左区间全部<=x,分界点以右区间全部>=x; - 递归左右区间;
void quick_sort(int q[],int l,int r)
{if(l>=r)return;int x=q[l],i=l-1,j=r+1;while(i<j){do i++;while(q[i]<x);do j--;while(q[j]>x);if(i<j)swap(q[i],q[j]);}quick_sort(q,l,j);quick_sort(q,j+1,r);
}
快速排序是一种常见的排序算法,其基本思想是通过选择一个基准值,将数组分割成小于基准值和大于基准值的两部分,然后对这两部分分别进行递归排序,最终完成整个数组的排序。
下面是代码中的快速排序的实现流程:
- 选择基准值x为数组q的第一个元素q[l]。
- 设置两个指针i和j,分别指向数组的左边界l和右边界r。
- 在while循环中,不断移动指针i和j,使得i指向第一个大于等于基准值x的元素,j指向第一个小于等于基准值x的元素。
- 如果i < j,则交换q[i]和q[j]的值,将大于基准值的元素放到右边,小于基准值的元素放到左边。
- 经过上述操作后,数组被基准值x分成两部分,[l, j]区间内的元素都小于等于x,(j, r]区间内的元素都大于x。
- 对左右两部分分别递归调用quick_sort函数,对左半部分排序quick_sort(q, l, j),对右半部分排序quick_sort(q, j+1, r)。
快速排序的时间复杂度为O(nlogn),其中n为数组的长度。它是一种高效的排序算法,在平均情况下表现较好。