数据结构
- 一维:
- 基础:数组 vector(string),链表 linked list
- 高级:栈 stack,队列 queue,双端队列 deque,集合 set, 映射 map, 优先队列 priority_queue
- 二维:
- 基础:树 tree, 图 graph
- 高级: 二叉搜索树 binary search tree(red-black tree, AVL),堆heap,并查集 disjoint set, 字典树 Trie
- 特殊:
- 位运算 Bitwise, 布隆过滤器 BloomFilter
- LRU Cache
时间复杂度
算法
- if-else, switch -> branch
- for, while loop -> Iteration
- 递归 Recursion (Divide & Conquer, Backtrace)
- 搜索 Search:深度优先搜索 Depth first search,广度优先搜索 Breadth first search,A*
- 动态规划 Dynamic Programming
- 二分查找 Binary Search
- 贪心 Greedy
- 数学 Math, 几何 Geometry
模板
- 递归
void recursion(level, param1, param2, ...)
{
//recursion terminator
if(level > MAX_LEVEL)
{
process_result();
return;
}
//process logic in current level
process(level, data...);
//drill down
recursion(level + 1, p1, ...);
//reverse the current level status if needed
}
- 分治
void divide_conquer(problem, param1, param2, ...):
// recursion terminator
if problem is None:
print_result
return
// prepare data
data = prepare_data(problem)
subproblems = split_problem(problem, data)
// conquer subproblems
subresult1 = self.divide_conquer(subproblems[0], p1, ...)
subresult2 = self.divide_conquer(subproblems[1], p1, ...)
subresult3 = self.divide_conquer(subproblems[2], p1, ...)
…
// process and generate the final result
result = process_result(subresult1, subresult2, subresult3, …)
// revert the current level states
- DFS
void dfs(Node* node, unordered_set<Node*>& visited)
{
if(visited.count(node) > 0)
return ;
visited.insert(node);
for(auto &child: node->children)
{
if(visited.count(child) < 1)
dfs(child, visited);
}
}
- BFS
def bfs(graph, start, end)
{
visited = set();
queue = [];
queue.append([start])
while queue:
node = queue.pop()
visited.add(node)
process(node)
nodes = generate_related_nodes(node)
queue.push(nodes)
}
- 归并排序
void mergeSort(vector<int>& nums, int left, int right)
{
if(left > right)
return;
int mid = ((left + right) >> 1);
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
merge(nums, left, mid, right);
}
void merge(vector<int>& nums, int left, int mid, int right)
{
vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while(i <= mid && j <= right)
temp[k++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
while(i <= mid) temp[k++] = nums[i++]; //如果左侧数组没用完
while(j <= right) temp[k++] = nums[j++]; //如果右侧数组没用完
for(int p = 0; p < temp.size(); p++)
nums[left + p] = temp[p];
}
- 快速排序
void quickSort(vector<int>& nums, int begin, int end)
{
if(begin > end)
return ;
int pivot = partition(nums, begin, end);
quickSort(nums, begin, pivot - 1);
quickSort(nums, pivot + 1, end);
}
int partition(vector<int>& nums, int begin, int end)
{
int pivot = end, counter = begin;
for(int i = begin, i < end; i++)
{
if(nums[i] < nums[pivot])
swap(nums[counter++], nums[i]);
}
swap(nums[counter], nums[pivot]);
return counter;
}
- 堆排序
void heapSort(vector<int>& nums)
{
priority_queue<int, vector<int>, greater<int>> pq;
for(int i = 0; i < nums.size(); i++)
pq.push(nums[i]);
for(int i = 0; i < nums.size(); i++)
nums[i] = pq.pop();
}
学习要点
- 基本功是区别业余和职业选手的根本。 深厚功底来自于–过遍数
- 最大的误区: 只做一遍
- 五毒神掌
- 刻意练习–练习缺陷弱点地方、不舒服、枯燥,但是这些都是成长的基础
- 反馈–看题解、看国际版的高票回答
经典习题
需要多次练习彻底掌握的习题:
爬楼梯、硬币兑换
括号生成、括号匹配、直方图最大面积、滑动窗口
二叉树遍历、分层输出树、判断二叉排序树
股票买卖、偷房子、字符串编辑距离、最长上升子序列、最长公共子序列
异位词(判断和归类)、回文串(最大回文串)、regex和通配符匹配
高级数据结构(Trie、BloomFilter、LRU cache)
五毒神掌
第一遍: 不要死磕,要看代码学习(一定要看国际版的高票回答)
第二遍:自己写
第三遍:24小时后
第四遍:一周后
第五遍:面试前
面试技巧
- Clarfication:明确题目意思、边界、数据规模
- Possible solutions:穷尽所有可能的解法
- compare time 、space
- optimal solution
- Coding:代码简洁、高性能、美感
N皇后问题 - Test cases
- N皇后位运算解法
class Solution {
public:
int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return this->res;
}
void dfs(int n, int row, int col, int ld, int rd) {
if (row >= n) { res++; return; }
// 将所有能放置 Q 的位置由 0 变成 1,以便进行后续的位遍历
int bits = ~(col | ld | rd) & ((1 << n) - 1);
while (bits > 0) {
int pick = bits & -bits; // 注: x & -x
dfs(n, row + 1, col | pick, (ld | pick) << 1, (rd | pick) >> 1);
bits &= bits - 1; // 注: x & (x - 1)
}
}
private:
int res = 0;
};