思考问题的方法
1、举几个简单的例子,理解问题,模拟操作过程。
2、用图形表示抽象的数据结构,分析链表、二叉树等。
3、分解成若干子问题,采用递归的思路,包括分治法和动态规划。
写程序的过程
先想几个测试用例,确保思维是正确的,没有遗漏的点。
函数的入口对所有参数进行检验。
写完代码之后脑补测试用例运行流程。
交给面试官。
具体的问题
求解最长XX区间:窗口思想。一个back指针指向区间最后一个元素,每次递增,当发现不满足时向后移动指像区间首个元素的front指针。参考
最优化问题:贪心/动态规划
动态规划:写从前往后递推的状态转移方程。如果只用到前一个元素,可将保存的状态压缩一维。
贪心:每个最优子结构只做一个操作。“每个贪心算法之下,几乎总有一个更繁琐的动态规划算法”(算导)。
用递归写程序简单且不容易出错
设计模式:观察者模式、单例模式
二维数组:指针数组实现 或 vector实现 ( vector<vector<int>> f(m, vector<int>(n)); )
ps:int a[n][n]; 这种VLAs (variable length arrays)是C99标准,不是C++标准,GCC可支持,VS不支持。
C++ STL容器常用操作
容器/操作 | 添加 | 删除 | 查找 | 清空 | 其他 |
vector | push_back insert | pop_back erase |
| clear | resize max_size |
stack | push | pop | top |
|
|
queue | push | pop | front back |
|
|
priority_queue | push O(logN) | pop O(logN) | top O(1) |
| 建堆O(N) 默认大根堆 |
set | insert O(logN) emplace/_hint | erase O(logN) | find O(logN) 二分四兄弟 | clear | 建红黑树O(NlogN) |
map | [ ] insert(pair/it) emplace(k, v) emplace_hint | erase(key/ it) | [ ] find 二分四兄弟 | clear | [ ]key不存在时插入 |
unordered_set | insert O(1) | erase O(1) | find O(1) count | clear |
|
unordered_map | [ ] insert | erase | find count | clear |
|
list | push_front push_back insert | pop_front pop_back erase | front back | clear |
|
二分四兄弟: count, lower_bound, upper_bound, equal_range
empty, size每个容器都有
set, map, unordered_set, unordered_map均有multi版本,可保存重复的key。multimap没有[]操作。
distance(it1, it2)用来计算两个iterator之间元素的个数。
void advance (InputIterator& it, Distance n) iterator前进n步。
排序算法比较
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 原理 |
直接插入排序 | O(n^2) | O(n) 元素从后往前比 | O(n^2) | O(1) | [有序][无序] 每次取无序区第一个元素,插入有序区合适位置 |
希尔排序 | O(nlogn) | O(n^1.3) | O(n^2) | O(1) |
|
冒泡排序 | O(n^2) | O(n) 有剪枝,记录了上一轮最后交换的位置 | O(n^2) | O(1) | [无序][有序] 每次指针从第一个元素开始,后面的数比它小就互换,直到遍历到有序区 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) 每次partition剩余元素都在同一边 | O(logn)~O(n) 递归深度 | [<pivot]pivot[>pivot] 分治
|
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | [有序][无序] 每次从无序区中选出最小的元素放在区首,从而有序区+1 |
堆排序 | O(nlogn) 建堆O(n) 每次sift O(logn) | O(nlogn)
| O(nlogn) | O(1) | [无序区max[其它]][有序] 每次交换大根堆根节点和无序区最后一个元素,从而有序区+1
|
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) 每次都合并到另一个数组 | [有序1][有序2] 分治,两个有序数组合并只要O(n)时间 |
归并排序:
//归并递归实现
void merge(vector<int>& num, vector<int>& temp, int begin, int mid, int end) {
int p1 = begin, p2 = mid, p3 = begin;
while (p1 < mid && p2 < end) {
if (num[p1] < num[p2])
temp[p3++] = num[p1++];
else
temp[p3++] = num[p2++];
}
while (p1 < mid) {
temp[p3++] = num[p1++];
}
while (p2 < end) {
temp[p3++] = num[p2++];
}
}
void mergeSort(vector<int>& num, vector<int>& temp, int begin, int end) {
if (begin == end - 1)
return;
int mid = (end - begin) >> 1 + begin;
mergeSort(num, temp, begin, mid);
mergeSort(num, temp, mid, end);
merge(num, temp, begin, mid, end);
for (int i = begin; i < end; i++)
num[i] = temp[i];
}
快速排序:
//其他partition实现也可以
int partition(vector<int>& num, int start, int end) {
int pivot = num[start], i = start, j = end - 1;
while (i < j) {
while (i < j && pivot <= num[j])
j--;
if (i < j) {
num[i] = num[j];
i++;
}
while (i < j && num[i] < pivot)
i++;
if (i < j) {
num[j] = num[i];
j--;
}
}
num[i] = pivot;
return i;
}
void quickSort(vector<int>& num, int start, int end) {
if (start >= end - 1)
return;
int pivotIndex = partition(num, start, end);
quickSort(num, start, pivotIndex);
quickSort(num, pivotIndex + 1, end);
}
二分查找
//递增数组中找target下标
int binary_search(vector<int> num, int target) {
int begin = 0, end = num.size() - 1;
while (begin <= end) {
int mid = (begin + end) >> 1;
if (num[mid] > target)
end = mid - 1;
else if (num[mid] < target)
begin = mid + 1;
else
return mid;
}
return -1;
}
还有四种二分查找,第一个大于,小于,大于等于和小于等于T的位置,请见我的博文。
向面试官提问
我挑选的问题:
1、介绍一下您所在的team,以及您目前在做什么工作?
2、Google上海主要是做广告相关的开发吗?还有哪些业务吗?
3、在Google程序员的职业发展?
<转>问面试官的问题:
"我"会问的一些:(可能我已经知道了答案但我想听听面试官的看法或者了解团队的前景):
- 团队多大规模?
- 开发周期是怎样的? 会使用瀑布流/极限编程/敏捷开发么?
- 经常会为 deadline 加班么? 或者是有弹性的?
- 团队里怎么做技术选型?
- 每周平均开多少次会?
- 你觉得工作环境有助于员工集中精力吗?
- 目前正在做什么工作?
- 喜欢这些事情吗?
- 工作期限是怎么样的?