github: https://github.com/careercup/CtCI-6th-Edition
my-coding: https://git.coding.net/daydaygo/CTCI.git
date: 2016-03-22 19:45
里面有一张面试流程图, 只能用 吓人 和 专业 来形容了: 1年以上的面试准备 + 扩充人脉 + 自己的项目展示 + 找一个友人模拟面试
请准备纸质简历: 我之前的做法是展示自己的blog, 当然是有 秀
的成分在里面, 不过纸质简历方便面试官写写画画.
十大常见错误
- 只在计算机上面coding(我感觉这项没有错, 好的工具更重要)
- 不做行为面试题
- 不模拟面试
- 试图死记硬背答案
- 不大声说出自己的解题思路
- 过于仓促
- 代码不够严谨
- 不做测试
- 修正错误漫不经心
- 轻言放弃
受期待的面试者
- 对技术充满热情
- 关注扩展性&存储限制(互联网行业)
- 谈论产品: 喜欢什么? 有什么值得改进?
- 热衷创造
- 系统设计(虽然我总是听到 系统架构 不是一天就练成的)
简历
- 长度适中, 亮点优先级
- 工作经历: 要点 + 亮点
- 项目经历
- 编程语言 + 工具
行测
项目相关(浓缩成关键字): 1. 最难的部分; 2. 有什么收获; 3. 最有意思的部分; 4. 最难解的bug; 5. 最享受的过程; 6. 与团队成员的冲突
应对: 1. 力求具体, 切忌自大; 2. 省略细枝末节; 3. 条理清晰(主题现行/SAR)
- 你有哪些缺点: 正视不足, 重点突出怎么克服的
- 项目中最难的问题: 不要泛泛而谈, 这样会显得这个项目没有什么难度
- 问面试官的问题: 1. 真实的问题; 2. 有见地的问题; 3. 富有激情
技术面试
注重 数据结构 + 算法
解决面试题:
- 大部分时候都不能马上解决问题
- 提问, 消除歧义
- 设计算法
- 伪码
- 使用数据结构, 保持结构清晰
- 测试: 一般 / 极端情况 / 用户错误
算法题解法:
- 举例
- 模式匹配(找相似的例子)
- 简化推广法(打 单词 当作 单个字符处理, hash 的原型)
- 简单构造(递归)
- 数据结构头脑风暴(把能想到的数据结构都过一遍试试)
好代码: 正确 / 简洁 / 高效 / 易读 / 可维护
- 多用数据结构
- 适量重用代码
- 模块化
- 灵活/健壮
- 错误检查: 仔细验证输入 / 边界
其他
薪资考量: 工资 + 奖励 + 年终奖(时薪会是一个比较好的考量)
职业发展: 1. 增加履历; 2. 增长知识; 3. 升迁(技术/管理); 4. 是否处于上升期
幸福指数: 1. 产品; 2. 团队; 3. 工作时长; 4. 企业文化
录用谈判: 1. 理直气壮; 2. 手头还有其他选择; 3. 提出具体要价; 4. 比预期稍高; 5. 不要只盯着薪水(待遇是多方面, 薪水是其中的大头而已); 6. 选择合适的方法(电话 / 邮件)
入职须知: 1. 制定时间表; 2. 人际网络; 3. 寻求经理帮助
面试题
array & string
散列表
Hashtable / HashMap
: 数组 -> 链表 -> 二叉查找树动态数组
ArrayList
: 存满时动态扩容(一般为2倍)StringBuffer
: 处理字符串拼接, 申请一个可以容纳所有字符串的数组判断字符串的所有字符是否相同: 确定编码( ascll 还是 unicode); ascll -> 256 个字符; 26个字母 -> int 变量的每位来存储
c / c++
void reverse(char* str)
判断2个字符串重新排列后能否相等: 使用辅助数组计数
替换字符串中所有空格为
%20
: 遍历出空格的数量, 算出新字符串的长度, 从字符串尾部开始操作aabcccccaaa -> a2b1c5a3
实现字符串压缩:
i = 0; j =1;
str2 = [];
while(str[i]){
while(str[i] == str[i+j]) j++;
str2[] = str[i];
str2[] = j;
j = 1;
}
return strlen(str) > strlen(str2) ? str2 : str;
- N x N 的矩阵旋转90度: 顺时针
a[i][j] -> a[j][n-i-1]
; 逆时针:a[i][j] -> a[n-j-1][i]
- M x N 的矩阵中, 含0的行和列全部清零: 遍历, 使用2个辅助数组, 存储 行/列 中0的位置
- 调用一次 subString() 判断 s2 是不是 s1 旋转( s-> xy -> yx )得到: subString(s2, s1s1)
链表(LinkedList)
注意是单向链表还是双向链表
创建链表:
appendToTail()
, 在结尾添加节点删除单向链表中的节点:
prev.next = n.next
即可, 注意 检查空指针 + 表头/表尾 指针快行指针: 同时2个指针遍历链表, 其中一个步速为2倍或固定长度, 如
a1->a2..an->b1->b2..bn => a1->b1..an->bn
递归, 但是至少要 O(n)空间, 所有的递归都可以转为迭代
移除未排序链表中的重复节点: 使用 Hashtable, 遍历一次; 使用2个指针, 相当于2个for循环
[算法]单向链表中倒数第k个节点: 2个指针, 一个先行k步(k大于链表长度的情况也不用担心了), 之后2个指针一起走
删除一个节点(你只能访问这个节点): 使用下个节点替换当前节点即可
定值x将链表分为2部分: 使用2个链表来存储, 然后合并
链表实现加法, 和数组实现加法类似
有环链表: slow 步长为1, fast 步长为2; 当 slow 步行 k 到环起点, 环 size 为 m, 此时 fast 在 m-k%m 处; 所以 fast/slow 在 m-k%m 处相遇; 再取一个 指针从 head 开始, 就可以在 环起点 相遇
检查回文: 反转链表, 只需要比较前半部分; 栈; 递归, 每次 length-1, 终止条件 length<2
栈和队列
栈 Stack
队列 Queue
- 一个数组实现3个栈: 固定分配, 只允许每个栈使用固定的数组空间; 不固定分配, 需要考虑数组回绕
- 栈中添加一个 min(): 节点增加 min 值; 使用辅助栈存储 min
- 栈满时新建栈 + popAt(int index): 使用数组栈来维护栈的数量, pop() / push() / empty() 等方法都要考虑是否需要维护这个 数组栈
- 用栈实现汉诺塔:
An = 2*An-1 + 1
- 2个栈实现一个队列
- 升序栈: 一次弹出原栈的元素, 和新栈进行比较, 弹出新栈的元素到原栈, 直到找到元素适合的位置
- 维护 queueDog + queueCat
树&图
潜在问题: 二叉树&二叉查找树; 平衡&不平衡(需要衡量算法的 最好/最坏 情况); 完满&完整
二叉树遍历: 前/中/后/层次
树的平衡: 红黑树 / 平衡二叉树
单词查找数(trie): 每个节点存储字符, 每条路径表示一个单词
图的遍历: DFS / BFS
判断是否为平衡二叉树: 从根节点开始递归判断; 保存每个树的高度
判断有向图2点之间是否存在路径: dfs / bfs 都可
将有序数组构建成高度最小的二叉查找数: 依次使用数组中间节点作为根节点, 构建左右子树
无序数组构建二叉查找数: 依次插入元素到现有二叉树, O(N*logN)
// 构建子树, 返回子树根节点
TreeNode createMinimalBST(int arr[], int start, int end){
if(end < start) return NULL;
int mid = (start+end)>>1;
TreeNode n = new TreeNode(arr[mid]);
n.left = createMinimalBST(arr, start, mid-1);
n.right = createMinimalBST(arr, mid+1, end);
return n;
}
// 使用
createMinimalBST(arr,0,n-1);
给定二叉树, 创建某一深度上的所有节点的链表
方法一: 前序遍历, 遍历过程中传递当前层数, 并把节点加入相应链表中
方法二: 层次遍历检查一个树是否为 二叉查找树
二叉查找树:根节点 >= 所有左子树节点, <= 所有右子树节点
最大最小值: 中序遍历结果的时候, 不断更新最大最小值, 如果不符合条件直接停止遍历找出指定节点的中序后继
问题很简单, 但是边界条件很多: null; 右子树为null; 右子树的最左边节点找出二叉树中某2个结点的第一个公共祖先
递归访问当前节点的子树, 看看是否包含 2 个结点判断 T2(数据量比较小) 是不是 T1 的子树(数据很大)
遍历 T1, 当出现节点和 T2 根节点相同时, 进行比较树中结点和为固定值的路径
位操作
掩码清零
小数的十进制和二进制: 依次乘 2 然后和 1 对比; 依次减去 1/2, 1/4, 1/8
n&(n-1)
: 清除 n 中最右边的 1
n&(n-1)==0
: n 是 2^k
a^b
- 一个正整数二进制表示中 1 的个数相同且大小最接近的 2 个数
- 交换奇数和偶数位: 掩码 -> 光 奇数位/偶数位 -> 移位 + 相加
- 通过二进制找出 0-n 中缺少的一个数
智力题
- 找出20瓶药中份量不同的一瓶: 一瓶有很多粒药
- 88 的棋盘切去2个对角, 是否还能使用 12 的方块覆盖(否, 使用黑白相间证明)
- 5升水壶+3升水壶 -> 4升水? 数学定理: 2个数字互质, 则可以获得这 1-2个数和之间的任意数
- 每个人只能看到其他人的状态, 状态a的人必须离开, 如果有 n 个人状态 a, 第几天离开(n 个人 -> n 天, 使用数学推导)
- 2个鸡蛋, 100层楼, 找出鸡蛋扔下不会碎掉的层数, 并使最差情况下的次数最少(围绕 最差情况, 平衡2个鸡蛋扔的负载)
- 100个关上的柜子, 第i次改变第n*i柜子的状态, 还有几个柜子开着(数 -> 因子 -> 平方和)
数学&概率
- 比较1投命中 & 3投>=2中 的概率
- n边行n个蚂蚁相撞的概率(相撞 = 1 - 不相撞)
- 2条线是否会相交(线 = 线段 + 射线 + 直线; 二元一次方程)
- 加法 实现 减法/乘法/除法
- 一条直线评分2个正方形(过正方形中心的直线就可以平分正方形)
- 找出平面经过点数最多的直线(使用 斜率+截距 来表示一条直线, 遍历所有点, 返回出现次数最多的直线)
- 找出只有 3,5,7 因子的数中第k个数
维护3个队列, 每次判断新插入数, 然后放入相应的队列
对象对象设计
标准52张扑克牌
呼叫中心: call + 三级处理 员工/主管/经理
音乐点唱机: 点唱机(是否收费) + CD(可能是不同媒介) + 歌曲 + 艺术家 + 播放列表 + 显示屏
停车场: 不同车 + 不同停车位(多层/多排/可停不同车)
在线图书阅读系统: one time one user one book + 会员资格和书籍延期 + 图书搜索 + 书籍阅读
拼图程序: 拼图 = 边 + 3种状态 + 位置(绝对/相对); 判断相邻拼图是否可以拼在一起
聊天服务器: 在线离线状态 + 请求(发送/接受/拒绝) + 更新状态 + 私聊/群聊 + 发送消息
确定用户在线: 定时询问; 冲突信息; 负载(多台机器); DOS 攻击黑白棋设计
内存文件系统
散列表, 使用 链表处理冲突
递归&动态规划
位操作: 使用开发中有遇到, 使用 php 做 socket client, 和 c++ server 通信, 包括 编码 和 数据类型
测试: 测试转开发, 温水煮青蛙; 准备核心测试问题
PM: 处理模糊问题; 以客户为中心(态度 + 技术); 多层次交流; 热爱技术; 团队/领能力
技术主管/项目经理: 团队/领能力; 把握轻重缓急; 沟通; '把事情做好'的能力
创业公司: 个性契合; 技术能力(快速上手); 以往经验
经验积累: 多承担编程工作; 善用闲暇时光
人脉:广度 + 深度
面试非易事, 姿态很重要.
之前感觉自己能力很足, 去面试的时候把面试官给呛了, 结果可想而知. 能力只是是否能够胜任的前提条件, 最重要的还是与人相处与人合作与人沟通的能力.完美的解决问题并不是关键, 关键是要比其他面试者回答得更好.