昨天面试了一家公式,面试上来问我,使用过哪些STL容器,我说了一下,然后又问从最简单的开始说。
面试官:说说使用vector是需要注意什么?
我:注意什么......。迭代器失效问题。
面试官:你是看面经的吧
我:我没有看面经,平时就刷题用用这些容器,使用时需要注意什么,使用时需要注意什么(我连说两遍),平时就是用,没注意到有什么。
面试官:好吧,有看过STL源码剖析吗?
我:内心:我刷过侯捷老师的视频,但是书没看过,看的比较久了,基本上已经忘了。于是脱口而出,没看过。
面试官:好吧,你有了解什么性能优化的工具吗
我:我注意到贵公司的笔试题中有类似的问题,我就只知道valgrind,,可以检查内存泄漏,但是没用过。
面试官:好吧,那你做完笔试,有没有了解过呢?
我:没有。
面试官呵呵一笑:那你了解图吗?
我:图我就知道有向图、无向图,图的算法:迪杰斯特拉、克鲁斯卡尔(没记错的话)
面试官:图的存储呢?
我:邻接表,邻接矩阵
面试官:那什么时候用邻接表、邻接矩阵呢?
我:秋招这么久,没有面试官问过我关于图的算法,这几个算法是我之前看的。现在有点忘了。
面试官:(很惊讶),竟然没有人问过你图,好吧。你有参与过或者做过什么开源的项目吗?
我:项目没有做过,看过levelDB源码中的切片和内存分配部分。
面试官:好吧,那我们先做一道在线编程,我看看你的编程习惯,你用你熟悉的环境。
我:我熟练的打开VS,面试官把题发了过来。
给定一个正整数集合S。该集合中的元素很多(大于100万),而且该集合满足以下条件:
条件A: 集合中的所有元素之和能被正整数P=5整除。?
1.请你设计并实现一个程序,求出S的一个最小子集(最小的定义是集合元素个数最少)T,使得T也满足条件A. ?
示例:当P=5的时候,
如果
Case1: 输入 S = {1, 2, 3, 4, 5}, 则输出 T = {5} (元素个数为1)
Case2: 输入 S = {1,2,3,4}, 则输出 T = {1, 4} 或 T = {2, 3}
Case3: ?输入 S = {1,2,6,7,12,17}, 则输出 T = {1,2,7} 或 T = {6,12,17}
Case4: ?输入 S = {1,2,6,7,11, 12,13, 14, 17, 22}, 则输出 T = {1,14} 或 T = {2, 13}?...?
注意:任意输出一个最小子集即可,不要求输出所有的最小子集。
我最开始想的是暴力,思考了有3-5min,发现不行,后来想到可以用回溯求出所有子集,然后再根据子集去找,写了半天,没写出来怎么求子集(真是蠢,平时都可以的),后面面试官说了他的思路,先按下不提,我这里先把自己的思路再写写。
(1)首先求子集;
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 求最小子集的和被5整除
vector<vector<int>> res; // 存放所有子集
vector<int> path; // 存放子集
void helper(vector<int>& nums, int index) {
res.push_back(path);
// 终止条件
if (index >= nums.size()) {
return;
}
for (int i = index; i < nums.size(); i++) {
path.push_back(nums[i]);
helper(nums, i + 1);
path.pop_back();
}
}
int main(int argc, char** argv) {
res.clear();
path.clear();
vector<int> nums = { 1,2,3,4,5 };
helper(nums, 0);
for (int i = 0; i < res.size(); i++) {
for (int j = 0; j < res[i].size(); j++) {
cout << res[i][j];
}
cout << endl;
}
system("pause");
return 0;
}
测试
(2)对子集进行判断;
path中存储的是某个子集,res中存储的是所有子集,需要对所有子集进行遍历;需要求的是最小的子集,那么先对子集path按照个数排序。
// 我这里贴是核心代码
static bool cmp(vector<int>& a, vector<int>& b) {
return a.size() < b.size();
}
sort(res.begin(), res.end(), cmp);
for (int i = 0; i < res.size(); i++) {
for (int j = 0; j < res[i].size(); j++) {
cout << res[i][j];
}
cout << endl;
}
测试
(3)对子集求和,找出满足条件的最小子集
子集已经排序了,直接遍历找到第一个满足的就行
// 贴的核心代码
// 求和函数
int add(vector<int>& nums) {
int sum = 0;
for (auto num : nums) {
sum += num;
}
return sum;
}
for (int i = 0; i < res.size(); i++) {
// 去掉空集
if (res[i].size() == 0) {
continue;
}
if (add(res[i]) % 5 == 0) {
for (auto num:res[i]) {
cout << num << "\t";
}
break;
}
}
测试
换一组数据
S = {1,2,6,7,11, 12,13, 14, 17, 22}
这里可以再按照起始大排序(我真是会叠buff)
6和14是满足的,
but,使用了回溯,整个程序复杂度就提上来了,也总算写出了(苦笑)
【注:上述代码在面试过程中并没有写出来,今天早上复盘的时候才写出来的】
贴上整体代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/*
给定一个正整数集合S。该集合中的元素很多(大于100万),而且该集合满足以下条件:
条件A: 集合中的所有元素之和能被正整数P=5整除。?
1.请你设计并实现一个程序,求出S的一个最小子集(最小的定义是集合元素个数最少)T,使得T也满足条件A. ?
示例:当P=5的时候,
如果
Case1: 输入 S = {1, 2, 3, 4, 5}, 则输出 T = {5} (元素个数为1)
Case2: 输入 S = {1,2,3,4}, 则输出 T = {1, 4} 或 T = {2, 3}
Case3: ?输入 S = {1,2,6,7,12,17}, 则输出 T = {1,2,7} 或 T = {6,12,17}
Case4: ?输入 S = {1,2,6,7,11, 12,13, 14, 17, 22}, 则输出 T = {1,14} 或 T = {2, 13}?...?
注意:任意输出一个最小子集即可,不要求输出所有的最小子集。
*/
// 求最小子集的和被5整除
vector<vector<int>> res; // 存放所有子集
vector<int> path; // 存放子集
void helper(vector<int>& nums, int index) {
res.push_back(path);
// 终止条件
if (index >= nums.size()) {
return;
}
for (int i = index; i < nums.size(); i++) {
path.push_back(nums[i]);
helper(nums, i + 1);
path.pop_back();
}
}
// 排序
static bool cmp(vector<int>& a, vector<int>& b) {
return a.size() < b.size();
}
// 求和函数
int add(vector<int>& nums) {
int sum = 0;
for (auto num : nums) {
sum += num;
}
return sum;
}
int main(int argc, char** argv) {
res.clear();
path.clear();
vector<int> nums = { 1,2,6,7,11, 12,13, 14, 17, 22 };
helper(nums, 0);
for (int i = 0; i < res.size(); i++) {
for (int j = 0; j < res[i].size(); j++) {
cout << res[i][j];
}
cout << endl;
}
cout << "************************" << endl;
sort(res.begin(), res.end(), cmp);
for (int i = 0; i < res.size(); i++) {
for (int j = 0; j < res[i].size(); j++) {
cout << res[i][j];
}
cout << endl;
}
cout << "************************" << endl;
for (int i = 0; i < res.size(); i++) {
// 去掉空集
if (res[i].size() == 0) {
continue;
}
if (add(res[i]) % 5 == 0) {
for (auto num:res[i]) {
cout << num << "\t";
}
break;
}
}
system("pause");
return 0;
}
话接上回,我用回溯没有写出来,说了一下我的思路,面试官和我说了他的思路
【可以先预处理一波,所有数字先对5取余,得到的数字是在0-4范围内,然后再用5个桶,类似桶排序,把数字存起来,再进行判断(取余、存储我理解了,那么怎么和原数据进行对应的)】,我没有理解面试官后面的意思(苦笑),也没有顺着他的思路写出来。
面试官:你这个回溯的复杂度是多少,你了解代码的复杂度吗,
我:代码的复杂度分为时间复杂度和空间复杂度,回溯的复杂度,我还不会分析(尴尬)。
后面就聊聊我的基本情况,今年的大环境。面试官人很好,是大佬级别的。
总结:还是自己菜,有一些基本概念没理解,讲不出来,算法能力也还需要精进。
#算法引流:##秋招求助#