面试题总结(四) -- STL与算法篇
文章目录
- [面试题总结(四) -- STL与算法篇](#面试题总结(四) -- STL与算法篇)
-
-
- [<1> 请列举 C++ STL 中常用的容器(如 vector、list、map 等)及其特点。](#<1> 请列举 C++ STL 中常用的容器(如 vector、list、map 等)及其特点。)
- [<2> 如何在 C++ 中使用 STL 算法(如排序、查找等)?](#<2> 如何在 C++ 中使用 STL 算法(如排序、查找等)?)
- [<3> 解释 STL 迭代器的概念和作用。](#<3> 解释 STL 迭代器的概念和作用。)
- [<4> C++ 中 map 和 unordered_map 的区别是什么?](#<4> C++ 中 map 和 unordered_map 的区别是什么?)
- [<5> 谈谈 STL 中容器适配器(stack、queue、priority_queue)的使用。](#<5> 谈谈 STL 中容器适配器(stack、queue、priority_queue)的使用。)
- [<6> 如何自定义 C++ STL 容器的比较函数?](#<6> 如何自定义 C++ STL 容器的比较函数?)
- [<7> 描述 C++ 中算法的复杂度分析(时间复杂度和空间复杂度)。](#<7> 描述 C++ 中算法的复杂度分析(时间复杂度和空间复杂度)。)
- [<8> 举例说明在 C++ 中如何使用 STL 进行数据的批量处理。](#<8> 举例说明在 C++ 中如何使用 STL 进行数据的批量处理。)
- [<9> 解释 C++ 中函数对象(functor)在 STL 中的应用。](#<9> 解释 C++ 中函数对象(functor)在 STL 中的应用。)
- [<10> 如何解决 C++ 中 STL 容器的迭代器失效问题?](#<10> 如何解决 C++ 中 STL 容器的迭代器失效问题?)
-
<1> 请列举 C++ STL 中常用的容器(如 vector、list、map 等)及其特点。
vector (向量):
- 特点: 动态数组,内存连续存储,支持随机访问,在尾部添加和删除元素效率高,在中间插入和删除元素效率低。
- 理由: 连续存储使得随机访问速度快,但中间插入删除需要移动大量元素。
list (链表):
- 特点: 双向链表,非连续存储,在任意位置插入和删除元素效率高,不支持随机访问。
- 理由: 链表结构决定了插入删除操作只需修改指针,无需移动元素,但随机访问需要遍历。
map (映射):
- 特点: 基于红黑树实现的键值对数据结构,按键有序存储,查找、插入和删除的平均时间复杂度为 O(log n)。
- 理由: 红黑树的特性保证了元素的有序性和较好的查找效率。
unordered_map (无序映射):
- 特点: 基于哈希表实现,查找、插入和删除的平均时间复杂度为 O(1),元素无序。
- 理由: 哈希表的特性使得查找速度快,但不保证元素顺序。
<2> 如何在 C++ 中使用 STL 算法(如排序、查找等)?
排序:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 3};
std::sort(numbers.begin(), numbers.end());
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
理由 : std::sort
函数接受两个迭代器指定排序范围,对范围内的元素进行排序。
查找:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 3};
auto it = std::find(numbers.begin(), numbers.end(), 8);
if (it != numbers.end()) {
std::cout << "找到了 8" << std::endl;
} else {
std::cout << "未找到 8" << std::endl;
}
return 0;
}
理由 : std::find
函数返回指向找到元素的迭代器,如果未找到则返回结束迭代器。
<3> 解释 STL 迭代器的概念和作用。
概念: 迭代器是一种用于遍历容器中元素的工具。
作用: 1.提供统一的访问方式,使得不同容器的遍历操作具有相似性;2.解耦算法和容器的具体实现,算法只需通过迭代器操作元素,无需关心容器的内部结构。
<4> C++ 中 map 和 unordered_map 的区别是什么?
存储结构:
map
基于红黑树,元素按键有序存储。unordered_map
基于哈希表,元素无序存储。
查找效率:
- 平均情况下,
unordered_map
的查找、插入和删除操作通常更快,时间复杂度接近 O(1)。 map
的查找、插入和删除操作的平均时间复杂度为 O(log n)。
空间占用 : unordered_map
通常需要更多的空间来存储哈希表的相关信息。
迭代顺序:
map
按照键的升序迭代。unordered_map
的迭代顺序是不确定的。
<5> 谈谈 STL 中容器适配器(stack、queue、priority_queue)的使用。
stack (栈):
cpp
#include <iostream>
#include <stack>
int main() {
std::stack<int> myStack;
myStack.push(1);
myStack.push(2);
myStack.push(3);
std::cout << "栈顶元素: " << myStack.top() << std::endl;
myStack.pop();
std::cout << "栈顶元素: " << myStack.top() << std::endl;
return 0;
}
理由 : push
用于入栈,top
获取栈顶元素,pop
弹出栈顶元素。
queue (队列):
cpp
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
myQueue.push(1);
myQueue.push(2);
myQueue.push(3);
std::cout << "队头元素: " << myQueue.front() << std::endl;
myQueue.pop();
std::cout << "队头元素: " << myQueue.front() << std::endl;
return 0;
}
理由 : push
用于入队,front
获取队头元素,pop
弹出队头元素。
priority_queue (优先队列):
cpp
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> myPriorityQueue;
myPriorityQueue.push(1);
myPriorityQueue.push(3);
myPriorityQueue.push(2);
std::cout << "队头元素: " << myPriorityQueue.top() << std::endl;
myPriorityQueue.pop();
std::cout << "队头元素: " << myPriorityQueue.top() << std::endl;
return 0;
}
理由 : push
用于入队,top
获取队头元素,pop
弹出队头元素。
<6> 如何自定义 C++ STL 容器的比较函数?
对于 map
或 set
等有序容器:
cpp
struct CustomComparator {
bool operator()(const int& a, const int& b) {
return a % 2 < b % 2; // 按照奇数偶数比较
}
};
std::map<int, int, CustomComparator> myMap;
对于排序算法:
cpp
bool customSort(int a, int b) {
return a > b; // 降序排序
}
std::vector<int> numbers = {5, 2, 8, 1, 3};
std::sort(numbers.begin(), numbers.end(), customSort);
<7> 描述 C++ 中算法的复杂度分析(时间复杂度和空间复杂度)。
时间复杂度: 表示算法执行所需的时间与输入规模之间的关系。
常见的时间复杂度有:O(1)(常数时间)、O(log n)(对数时间)、O(n)(线性时间)、O(n log n)、O(n^2) 等。
空间复杂度: 表示算法执行所需的额外空间与输入规模之间的关系。
例如,对于 std::sort
函数,其平均时间复杂度为 O(n log n),空间复杂度为 O(log n)。
<8> 举例说明在 C++ 中如何使用 STL 进行数据的批量处理。
假设要对一个整数 vector
中的所有元素进行平方操作:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
void square(int& num) {
num *= num;
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::for_each(numbers.begin(), numbers.end(), square);
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
<9> 解释 C++ 中函数对象(functor)在 STL 中的应用。
函数对象可以用于传递给 STL 算法作为操作函数。
例如,在 std::sort
中使用自定义的函数对象来定义排序规则:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
struct DescendingComparator {
bool operator()(int a, int b) {
return a > b;
}
};
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 3};
std::sort(numbers.begin(), numbers.end(), DescendingComparator());
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
<10> 如何解决 C++ 中 STL 容器的迭代器失效问题?
- 对于
vector
和deque
:
- 在插入或删除元素时,如果导致容器重新分配内存,可能会使迭代器失效。
- 解决方法:在插入或删除操作后,重新获取迭代器。
- 对于
list
:
插入和删除操作不会使迭代器失效,只会使指向被删除元素的迭代器失效。
- 对于
map
和set
等关联容器:
-
插入操作不会使迭代器失效,但删除操作会使指向被删除元素的迭代器失效。
-
解决方法:在删除操作前,先保存需要的迭代器,或者使用返回的新迭代器。
例如,对于 vector
:
cpp
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto it = numbers.begin() + 2;
numbers.insert(numbers.begin() + 2, 6); // 插入可能导致迭代器失效
it = numbers.begin() + 3; // 重新获取迭代器
std::cout << *it << std::endl;
return 0;
}