马上进入2020找实习冲刺阶段,我决定以天为单位,记录每天做的LeetCode习题,方便后期整理。
C++文档神器推荐:cppreference.chm
📅 更新打卡:2020 - week2 - 3/13
-
😄 简单题目(典型题目)
-
🤢 中等难题(题目本身不是很难,但是并不一定写得出来)
- 838 push-dominoes (TODO)
- 932 beautiful-array(TODO)
- 334 increasing-triplet-subsequences (TODO)
- 838 push-dominoes (TODO)
- 984 string-without-aaa-or-bbb (TODO)
- 959 regions-cut-by-slashes (并查集,TODO)
- 96-unique-binary-search-trees (二叉搜索树,TODO)
- 96-unique-binary-search-trees (二叉搜索树,TODO)
- 399-evaluate-division (并查集,TODO)
- 79-word-search (回溯,TODO)
- 236-lowest-common-ancestor-of-a-binary-tree (二叉树递归,TODO)
-
😡 困难题目(值得一看)
- 52-n-queens-ii (TODO)
- 154. 寻找旋转排序数组中的最小值 II
数组
⭐⭐⭐⭐⭐
(这是另一个每日刷题项目,有空的时候我也会更新)
总结整理剑指Offer中部分经典题目
- 面试题24. 反转链表(同LeetCode206)
链表
- 面试题03. 数组中重复的数字
哈希
- 面试题12. 矩阵中的路径(同LeetCode79)
DFS
- 面试题59 - I. 滑动窗口的最大值(同LeetCode239)
滑动窗口
- 面试题59-ii-队列的最大值
滑动窗口
- 面试题60. n个骰子的点数
DP
- 面试题52. 两个链表的第一个公共节点
快慢指针
- 面试题56 - I. 数组中数字出现的次数
位运算
- 面试题57. 和为s的两个数字
对撞指针
- 面试题14- I. 剪绳子
DP
- 面试题53 - I. 在排序数组中查找数字 I
二分
🕑 - 面试题62. 圆圈中最后剩下的数字
约瑟夫环
贪心
- 面试题53 - II. 0~n-1中缺失的数字
二分
🕑
并查集是一种树形数据结构,用于处理一些非连通子图的合并以及查询问题,主要使用Union
以及find
两个方法定义了该数据结构的相关操作:
- Find:确定给定元素属于哪个子集,可以用于确定两个元素是否属于同一个子集;
- Union:将两个子集合并成同一个集合。
并查集核心代码:
class UnionSet(object):
def __init__(self, n, init_list = None):
if init_list:
self.parent = init_list
else:
self.parent = [i for i in range(n)]
def __str__(self):
return str(self.parent)
# 不带路径压缩的find函数
def find(self, num):
if self.parent[num] == num:
return self.parent[num]
return self.find(self.parent[num])
# 带路径压缩的find函数:在执行find函数的时候完成路径压缩
def find(self, num):
root = num
while root != self.parent[root]:
root = self.parent[root]
while num != root:
self.parent[num], num = root, self.parent[num]
return root
def union(self, num1, num2):
self.parent[self.find(num1)] = self.find(num2)
def count(self):
return len([1 for i, num in enumerate(self.parent) if num == i])
并查集经典题目:
- 959 regions-cut-by-slashes (TODO)
- 399-evaluate-division (TODO)
检查数x是否为2的幂:
x > 0 and x & (x - 1) == 0
将最右边一位1置0:
num = num & (num - 1)
层序遍历:
python
def levelOrder(root):
if not root:
return []
ans = []
queue = [root]
while queue:
count = len(queue)
cur_line = []
while count:
cur = queue.pop(0)
cur_line.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
count -= 1
ans.append(cur_line)
return ans
cpp
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
if (root == NULL) return ret;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int counter = que.size();
vector<int> cur_row;
while (counter--) {
TreeNode* cur = que.front();
que.pop();
cur_row.push_back(cur->val);
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
ret.push_back(cur_row);
}
return ret;
}
前序遍历非递归写法:
python
def preOrder(root):
if not root:
return []
ans = []
stack = [root]
while stack:
cur = stack.pop()
ans.append(cur.val)
# 这里注意了,是先访问右节点
if cur.right:
stack.append(cur.right)
if cur.left:
stack.append(cur.left)
return ans
Cpp
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == NULL) return ret;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
st.pop();
ret.push_back(cur->val);
if (cur->right) st.push(cur->right);
if (cur->left) st.push(cur->left);
}
return ret;
}
中序遍历的非递归写法:
*中序遍历递归写法将左右子节点递归的顺序反过来就能得到逆序。
python
def inOrder(root):
ans = []
stack = []
cur = root
while cur or stack:
if cur:
stack.append(cur.left)
cur = cur.left
else:
cur = stack.pop()
ans.append(cur.val)
cur = cur.right
return ans
cpp
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur || !st.empty()) {
if (cur) {
st.push(cur);
cur = cur->left;
}
else {
cur = st.top();
st.pop();
ret.push_back(cur->val);
cur = cur->right;
}
}
return ret;
}
后序遍历的非递归写法:
python
# 从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中。因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。
def postOrder(root):
if not root:
return []
ans = []
stack = [root]
while stack:
cur = stack.pop()
ans.append(cur.val)
if cur.left:
stack.append(cur.left)
if cur.right:
stack.append(cur.right)
return ans[::-1]
cpp
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == NULL) return ret;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
st.pop();
ret.push_back(cur->val);
if (cur->left) st.push(cur->left);
if (cur->right) st.push(cur->right);
}
reverse(ret.begin(), ret.end());
return ret;
}
冒泡排序:
void bubbleSort(vector<int>& vec) {
int n = vec.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (vec[j + 1] < vec[j]) swap(vec[j + 1], vec[j]);
}
}
}
插入排序:
void insertSort(vector<int>& vec) {
for (int i = 1; i < vec.size(); i++) {
int cur = vec[i];
for (int j = i - 1; j >= 0; j--) {
if (vec[j] > vec[j + 1])
swap(vec[j + 1], vec[j]);
else break;
}
}
}
选择排序:
void selectSort(vector<int>& vec) {
for (int i = 0; i < vec.size(); i++) {
int temp_min = vec[i], temp_min_idx = i;
for (int j = i + 1; j < vec.size(); j++) {
if (vec[j] < temp_min) {
temp_min = vec[j];
temp_min_idx = j;
}
}
swap(vec[i], vec[temp_min_idx]);
}
}
快速排序:
// 版本1:快慢指针,从左往右遍历
void quick_sort1(vector<int>& vec, int left, int right) {
if (left >= right) return;
int ptr = left, pivot = vec[ptr];
for (int i = left + 1; i <= right; i++) {
if (vec[i] < pivot) {
ptr ++;
swap(vec[ptr], vec[i]);
}
}
swap(vec[left], vec[ptr]);
quick_sort1(vec, left, ptr - 1);
quick_sort1(vec, ptr + 1, right);
}
// 版本2:前后指针,最经典的方法
void quick_sort2(vector<int>& vec, int left, int right) {
if (left >= right) return;
int pivot = vec[left];
int l = left, r = right;
while (left < right) {
while (left < right && vec[right] >= pivot) right --;
while (left < right && vec[left] <= pivot) left ++;
swap(vec[left], vec[right]);
}
swap(vec[left], vec[l]);
quick_sort2(vec, l, left - 1);
quick_sort2(vec, left + 1, r);
}
void quickSort(vector<int>& vec) {
quick_sort2(vec, 0, vec.size() - 1);
}
归并排序:
void merge_sort(vector<int>& vec, int left, int right) {
if (right <= left) return;
int mid = left + (right - left) / 2;
merge_sort(vec, left, mid);
merge_sort(vec, mid + 1, right);
int ptr1 = left, ptr2 = mid + 1;
vector<int> temp;
while (ptr1 <= mid && ptr2 <= right) {
if (vec[ptr1] <= vec[ptr2]) {
temp.push_back(vec[ptr1]);
ptr1 ++;
}
else {
temp.push_back(vec[ptr2]);
ptr2 ++;
}
}
while (ptr1 <= mid) temp.push_back(vec[ptr1++]);
while (ptr2 <= right) temp.push_back(vec[ptr2++]);
for (int i = 0; i < temp.size(); i++) {
vec[left + i] = temp[i];
}
}
void mergeSort(vector<int>& vec) {
merge_sort(vec, 0, vec.size() - 1);
}
BFS解题模板:
DFS解题模板:
最小公倍数辗转相除法:
int gcd(int a, int b) {
if (a < b) { a = a ^ b; b = a ^ b; a = a ^ b; }
return b == 0 ? a : gcd(b, a % b);
}
最大公约数求法:
int lcm(int a, int b) {
return a * b / gcd(a, b);
}
前缀树在字符串查找、输入框自动补全等应用场所常常使用。
class Trie {
private:
// 定义前缀树结构体,有两个成员变量
// isEnd:表示是否存在从根节点到当前节点的字符串
// children:表示当前节点的26个子节点
struct TrieNode {
TrieNode() : isEnd(false), children(26, nullptr) {}
~TrieNode() {
for (auto child : children)
if (child) delete child;
}
bool isEnd;
vector<TrieNode*> children;
};
// 定义根节点
TrieNode *root;
public:
/** Initialize your data structure here. */
Trie() : root(new TrieNode()) {}
/** Inserts a word into the trie. */
void insert(string word) {
TrieNode* cur = root;
for (auto ch : word) {
if (cur->children[ch - 'a'] == nullptr) {
cur->children[ch - 'a'] = new TrieNode();
}
cur = cur->children[ch - 'a'];
}
cur->isEnd = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
TrieNode* cur = root;
for (auto ch : word) {
if (cur->children[ch - 'a'] == nullptr) return false;
cur = cur->children[ch - 'a'];
}
return cur->isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
TrieNode* cur = root;
for (auto ch : prefix) {
if (cur->children[ch - 'a'] == nullptr) return false;
cur = cur->children[ch - 'a'];
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
算法类:
-
分治、二分
-
排序算法:快速排序、归并排序、计数排序
- 冒泡排序
- 快速排序
- 归并排序
-
搜索算法:回溯、递归、深度优先遍历,广度优先遍历,二叉搜索树等
- DFS
- BFS
-
*图论:最短路径、最小生成树
-
动态规划:背包问题、最长子序列
-
位运算
- 异或:相同为0,不同为1
- 136. 只出现一次的数字
- 与
- 左移/右移
- 综合
-
贪心
- [面试题62. 圆圈中最后剩下的数字](./offer/62. 圆圈中最后剩下的数字.md)
-
数学/逻辑/规律:
数据结构类:
-
数组
- 快慢指针
- 对撞指针
- 滑动窗口
-
链表
- 链表反转
-
栈与队列
-
哈希表
-
堆:最大堆 / 最小堆
- 优先队列
-
二叉树
- 递归
- 遍历
- 二叉树的层次遍历:102. 二叉树的层序遍历
- 二叉树的先序遍历:144. 二叉树的前序遍历
- 二叉树的中序遍历:
- 二叉树的后序遍历:145. 二叉树的后序遍历
- 二叉树的垂序遍历:987. 二叉树的垂序遍历
-
图:最近公共祖先、并查集
- DFS
- BFS
- 并查集
-
字符串:前缀树(字典树) / 后缀树
系列问题
- 股票问题:
- Top 100 Linked Questions [0%]
- Top Interview Questions [0%]