1. 全 O(1) 的数据结构
Design a data structure to store the strings’ count with the ability to return the strings with minimum and maximum counts.
Implement the AllOne
class:
AllOne()
Initializes the object of the data structure.inc(String key)
Increments the count of the stringkey
by1
. Ifkey
does not exist in the data structure, insert it with count1
.dec(String key)
Decrements the count of the stringkey
by1
. If the count ofkey
is0
after the decrement, remove it from the data structure. It is guaranteed thatkey
exists in the data structure before the decrement.getMaxKey()
Returns one of the keys with the maximal count. If no element exists, return an empty string""
.getMinKey()
Returns one of the keys with the minimum count. If no element exists, return an empty string""
.
Note that each function must run in O(1)
average time complexity.
Example 1
Input
["AllOne", "inc", "inc", "getMaxKey", "getMinKey", "inc", "getMaxKey", "getMinKey"]
[[], ["hello"], ["hello"], [], [], ["leet"], [], []]
Output
[null, null, null, "hello", "hello", null, "hello", "leet"]
Explanation
AllOne allOne = new AllOne();
allOne.inc("hello");
allOne.inc("hello");
allOne.getMaxKey(); // return "hello"
allOne.getMinKey(); // return "hello"
allOne.inc("leet");
allOne.getMaxKey(); // return "hello"
allOne.getMinKey(); // return "leet"
Constraints:
1 <= key.length <= 10
key
consists of lowercase English letters.- It is guaranteed that for each call to
dec
,key
is existing in the data structure. - At most
5 * 10^4
calls will be made toinc
,dec
,getMaxKey
, andgetMinKey
.
代码 [双向链表/哈希]
struct Node {
string s;
int val;
Node *prev, *next;
Node(string s, int val) : s(s), val(val), prev(nullptr), next(nullptr) {}
};
class AllOne {
private:
unordered_map<string, Node *> mp;
Node *dummyHead, *dummyTail;
inline void insertNode(Node *node, Node *nodePrev, Node *nodeNext) {
node->next = nodeNext, node->prev = nodePrev;
node->prev->next = node;
node->next->prev = node;
}
inline void deleteNode(Node *node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
inline void moveNode(Node *node, Node *nodePrev, Node *nodeNext) {
if (node == nodePrev || node == nodeNext) return;
deleteNode(node);
insertNode(node, nodePrev, nodeNext);
}
public:
AllOne() {
dummyHead = new Node("", 0);
dummyTail = new Node("", INT_MAX);
dummyHead->next = dummyTail;
dummyTail->prev = dummyHead;
}
void inc(string key) {
if (!mp.count(key)) {
auto node = new Node(key, 1);
insertNode(node, dummyHead, dummyHead->next);
mp[key] = node;
} else {
auto node = mp[key];
++node->val;
auto nodeNext = node;
while (nodeNext->next->val < node->val) nodeNext = nodeNext->next;
moveNode(node, nodeNext, nodeNext->next);
}
}
void dec(string key) {
if (!mp.count(key)) return;
auto node = mp[key];
--node->val;
if (node->val == 0) {
deleteNode(node);
mp.erase(key);
return;
}
auto nodeNext = node;
while (nodeNext->prev->val > node->val) nodeNext = nodeNext->prev;
moveNode(node, nodeNext->prev, nodeNext);
}
string getMaxKey() { return dummyTail->prev->s; }
string getMinKey() { return dummyHead->next->s; }
};
2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
Example 1
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
Example 2
输入:l1 = [0], l2 = [0]
输出:[0]
提示:
- 每个链表中的节点数在范围
[1, 100]
内 0 <= Node.val <= 9
- 题目数据保证列表表示的数字不含前导零
代码
class Solution {
public:
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
ListNode *head = new ListNode();
ListNode *node = head;
int inc = 0, tmp;
while (l1 || l2) {
tmp = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + inc;
node = node->next = new ListNode(tmp % 10);
inc = tmp / 10;
if (l1) l1 = l1->next;
if (l2) l2 = l2->next;
}
if (inc) node->next = new ListNode(1);
return head->next;
}
};
3. 无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
Example 1
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
Example 2
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
提示:
0 <= s.length <= 5 * 10^4
s
由英文字母、数字、符号和空格组成
代码 [滑动窗口]
class Solution {
public:
int lengthOfLongestSubstring(string s) {
bool check[128];
memset(check, 0, sizeof check);
int result = 0;
for (int i = 0, j = 0; j < s.size(); ++j) {
while (check[s[j]]) check[s[i++]] = false;
check[s[j]] = true;
result = max(result, j - i + 1);
}
return result;
}
};
4. 寻找两个正序数组的中位数
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n))
。
Example 1
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
Example 2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
Constraints:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-10^6 <= nums1[i], nums2[i] <= 10^6
代码 [官方题解,二分]
class Solution {
public:
double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {
if (nums1.size() > nums2.size()) {
return findMedianSortedArrays(nums2, nums1);
}
int m = nums1.size();
int n = nums2.size();
int left = 0, right = m;
// median1:前一部分的最大值
// median2:后一部分的最小值
int median1 = 0, median2 = 0;
while (left <= right) {
// 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
// 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
// nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
int nums_im1 = (i == 0 ? INT_MIN : nums1[i - 1]);
int nums_i = (i == m ? INT_MAX : nums1[i]);
int nums_jm1 = (j == 0 ? INT_MIN : nums2[j - 1]);
int nums_j = (j == n ? INT_MAX : nums2[j]);
if (nums_im1 <= nums_j) {
median1 = max(nums_im1, nums_jm1);
median2 = min(nums_i, nums_j);
left = i + 1;
} else {
right = i - 1;
}
}
return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
}
};
5. 盛最多水的容器
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
**说明:**你不能倾斜容器。
Example 1
输入:[1,8,6,2,5,4,8,3,7]
输出:49
Example 2
输入:height = [1,1]
输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 10^4
代码 [双指针]
class Solution {
public:
int maxArea(vector<int> &height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while (l < r) {
int area = min(height[l], height[r]) * (r - l);
ans = max(ans, area);
height[l] < height[r] ? ++l : --r;
}
return ans;
}
};
6. 颜色分类
给定一个包含红色、白色和蓝色、共 n
个元素的数组 nums
,**原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
Example 1
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
Example 2
输入:nums = [2,0,1]
输出:[0,1,2]
提示:
n == nums.length
1 <= n <= 300
nums[i]
为0
、1
或2
代码 [双指针]
class Solution {
public:
void sortColors(vector<int> &nums) {
for (int i = 0, j = 0; j < nums.size(); ++j) {
if (nums[j] == 0) swap(nums[i++], nums[j]);
}
for (int i = nums.size() - 1, j = nums.size() - 1; j >= 0; --j) {
if (nums[j] == 2) swap(nums[i--], nums[j]);
}
}
};
7. 最大矩形
给定一个仅包含 0
和 1
、大小为 rows x cols
的二维二进制矩阵,找出只包含 1
的最大矩形,并返回其面积。
Example 1
输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:6
Example 2
输入:matrix = []
输出:0
提示:
rows == matrix.length
cols == matrix[0].length
1 <= row, cols <= 200
matrix[i][j]
为'0'
或'1'
代码 [单调栈]
class Solution {
private:
int largestRectangleArea(const vector<int> &heights) {
stack<int> st;
vector<int> minArgRight(heights.size(), heights.size());
vector<int> minArgLeft(heights.size(), -1);
for (int i = heights.size() - 1; i >= 0; --i) {
while (!st.empty() && heights[st.top()] >= heights[i]) st.pop();
if (!st.empty()) {
minArgRight[i] = st.top();
}
st.push(i);
}
while (!st.empty()) st.pop();
for (int i = 0; i < heights.size(); ++i) {
while (!st.empty() && heights[st.top()] >= heights[i]) st.pop();
if (!st.empty()) {
minArgLeft[i] = st.top();
}
st.push(i);
}
int maxArea = 0;
for (int i = 0, tmpArea; i < heights.size(); ++i) {
tmpArea = heights[i] * (minArgRight[i] - minArgLeft[i] - 1);
if (tmpArea > maxArea) maxArea = tmpArea;
}
return maxArea;
}
public:
int maximalRectangle(vector<vector<char>> &matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> mat(m, vector<int>(n, 0));
for (int j = 0; j < matrix[0].size(); ++j) mat[0][j] = matrix[0][j] == '1';
for (int i = 1; i < matrix.size(); ++i) {
for (int j = 0; j < matrix[0].size(); ++j) {
if (matrix[i][j] == '1') mat[i][j] = 1 + mat[i - 1][j];
}
}
int result = 0;
for (auto &h :mat) result = max(result, largestRectangleArea(h));
return result;
}
};
8. 爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
Example 1
输入:n = 2
输出:2
Example 2
输入:n = 3
输出:3
提示:
1 <= n <= 45
代码 [DP,斐波那契数]
class Solution {
public:
int climbStairs(int n) {
int f = 0, g = 1;
while (n--) {
g += f;
f = g - f;
}
return g;
}
};
9. 爬楼梯
给定一个包含非负整数的 *m* x *n*
网格 grid
,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
**说明:**每次只能向下或者向右移动一步。
Example 1
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
Example 2
输入:grid = [[1,2,3],[4,5,6]]
输出:12
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 100
代码
class Solution {
public:
int minPathSum(vector<vector<int>> &grid) {
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[0].size(); ++j) {
if (i == 0 && j == 0) continue;
grid[i][j] += min(i > 0 ? grid[i - 1][j] : 4000000, j > 0 ? grid[i][j - 1] : 4000000);
}
}
return grid.back().back();
}
};
9. 三数之和
给你一个包含 n
个整数的数组 nums
,判断 nums
中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0
且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
Example 1
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
Example 2
输入:nums = []
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 10^5
代码 [双指针]
class Solution {
public:
vector<vector<int>> threeSum(vector<int> &nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int i = 0; i + 2 < nums.size(); ++i) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
int j = i + 1, k = nums.size() - 1;
while (j < k) {
if (nums[i] + nums[j] + nums[k] == 0) {
result.push_back({nums[i], nums[j], nums[k]});
while (++j < nums.size() && nums[j] == nums[j - 1]) continue;
while (--k > i && nums[k] == nums[k + 1]) continue;
} else if (nums[i] + nums[j] + nums[k] < 0) {
while (++j < nums.size() && nums[j] == nums[j - 1]) continue;
} else {
while (--k > i && nums[k] == nums[k + 1]) continue;
}
}
}
return result;
}
};
10. 括号生成
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
Example 1
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
Example 2
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
代码 [栈,回溯]
class Solution {
private:
vector<string> result;
string path;
void dfs(int st, int left, int right) {
if (left == 0 && right == 0) {
result.emplace_back(path);
return;
}
if (left > 0) {
path.push_back('(');
dfs(st + 1, left - 1, right);
path.pop_back();
}
if (st > 0 && right > 0) {
path.push_back(')');
dfs(st - 1, left, right - 1);
path.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
result.clear();
path.clear();
dfs(0, n, n);
return result;
}
};
11. 不同路径
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
Example 1
输入:m = 3, n = 7
输出:28
Example 2
输入:m = 3, n = 2
输出:3
提示:
1 <= m, n <= 100
- 题目数据保证答案小于等于
2 * 10^9
代码 [动态规划]
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m, vector<int>(n, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
dp[i][j] = i == 0 || j == 0 ? 1 : dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
};
12. 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
Example 1
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
Example 2
输入: strs = [""]
输出: [[""]]
提示:
1 <= strs.length <= 10^4
0 <= strs[i].length <= 100
strs[i]
仅包含小写字母
代码 [哈希]
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string> &strs) {
map<string, vector<string>> mp;
for (auto &s : strs) {
string key = string(26, '0');
for (auto c : s) ++key[c - 'a'];
mp[key].emplace_back(s);
}
vector<vector<string>> res;
for (const auto &e : mp) res.emplace_back(e.second);
return res;
}
};
13. 环形链表
Given head
, the head of a linked list, determine if the linked list has a cycle in it.
There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next
pointer. Internally, pos
is used to denote the index of the node that tail’s next
pointer is connected to. Note that pos
is not passed as a parameter.
Return true
if there is a cycle in the linked list. Otherwise, return false
.
Example 1
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).
Example 2
Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where the tail connects to the 0th node.
提示:
- The number of the nodes in the list is in the range
[0, 104]
. -105 <= Node.val <= 105
pos
is-1
or a valid index in the linked-list.
代码
class Solution {
public:
bool hasCycle(ListNode *head) {
auto i = head, j = head;
while (j && j->next) {
i = i->next, j = j->next->next;
if (i == j) return true;
}
return false;
}
};
14. 二叉树展开为链表
给你二叉树的根结点 root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
Example 1
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
Example 2
输入:root = []
输出:[]
提示:
- 树中结点数在范围
[0, 2000]
内 -100 <= Node.val <= 100
代码
class Solution {
public:
void flatten(TreeNode *root) {
if (!root) return;
stack<TreeNode *> st;
TreeNode *head = new TreeNode();
TreeNode *prev = head, *cur;
st.push(root);
while (!st.empty()) {
cur = st.top();
st.pop();
if (cur->right) st.push(cur->right);
if (cur->left) st.push(cur->left);
cur->left = nullptr;
prev->right = cur;
prev = cur;
}
root = head->right;
}
};
15. 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历,inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
Example 1
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
Example 2
输入: preorder = [-1], inorder = [-1]
输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder
和inorder
均 无重复 元素inorder
均出现在preorder
preorder
保证 为二叉树的前序遍历序列inorder
保证 为二叉树的中序遍历序列
代码
class Solution {
private:
vector<int> preorder, inorder;
TreeNode *dfs(int iPre, int jPre, int iIn, int jIn) {
if (iPre == jPre) return nullptr;
TreeNode *node = new TreeNode(preorder[iPre]);
if (iPre + 1 == jPre) return node;
int miIn = find(inorder.begin() + iIn, inorder.begin() + jIn, preorder[iPre]) - inorder.begin();
int nLeft = miIn - iIn, nRight = jIn - miIn - 1;
node->left = dfs(iPre + 1, iPre + 1 + nLeft, iIn, miIn);
node->right = dfs(iPre + 1 + nLeft, jPre, miIn + 1, jIn);
return node;
}
public:
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
this->preorder = move(preorder), this->inorder = move(inorder);
return dfs(0, this->preorder.size(), 0, this->inorder.size());
}
};
16. 单词搜索
给定一个 m x n
二维字符网格 board
和一个字符串单词 word
。如果 word
存在于网格中,返回 true
;否则,返回 false
。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
Example 1
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
Example 2
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true
提示:
m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board
和word
仅由大小写英文字母组成
代码
const vector<pair<int, int>> DIR{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
class Solution {
private:
vector<vector<char>> board;
string word;
int m, n;
bool visited[6][6]; // 回溯时将board[x][y]修改为'#'可不开辟visited
bool dfs(int x, int y, int i) {
if (i == word.size()) return true;
if (x < 0 || y < 0 || x >= m || y >= n || visited[x][y] || board[x][y] != word[i]) return false;
visited[x][y] = true;
for (auto&[dx, dy]:DIR) {
if (dfs(x + dx, y + dy, i + 1)) return true;
}
visited[x][y] = false;
return false;
}
public:
bool exist(vector<vector<char>> &board, string &word) {
memset(visited, 0, sizeof visited);
m = board.size(), n = board[0].size();
this->board = move(board), this->word = move(word);
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (dfs(i, j, 0)) return true;
}
}
return false;
}
};
17. 排序链表
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
Example 1
输入:head = [4,2,1,3]
输出:[1,2,3,4]
Example 2
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
提示:
- 链表中节点的数目在范围
[0, 5 * 10^4]
内 -10^5 <= Node.val <= 10^5
代码
class Solution {
private:
ListNode *merge(ListNode *head1, ListNode *head2) {
ListNode *dummyHead = new ListNode;
ListNode *node = dummyHead;
while (head1 && head2) {
if (head1->val > head2->val) swap(head1, head2);
node = node->next = head1;
head1 = head1->next;
}
node->next = head1 ? head1 : head2;
return dummyHead->next;
}
ListNode *sortList(ListNode *head, ListNode *tail) {
if (head == nullptr) return head;
if (head->next == tail) {
head->next = nullptr;
return head;
}
auto slow = head, fast = head;
while (fast != tail) {
slow = slow->next, fast = fast->next;
if (fast != tail) fast = fast->next;
}
return merge(sortList(head, slow), sortList(slow, tail));
}
public:
ListNode *sortList(ListNode *head) { return sortList(head, nullptr); }
};
17. 寻找重复数
给定一个包含 n + 1
个整数的数组 nums
,其数字都在 [1, n]
范围内(包括 1
和 n
),可知至少存在一个重复的整数。
假设 nums
只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums
且只用常量级 O(1)
的额外空间。
Example 1
输入:nums = [1,3,4,2,2]
输出:2
Example 2
输入:nums = [3,1,3,4,2]
输出:3
提示:
1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums
中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
代码 [快慢指针]
class Solution {
public:
int findDuplicate(vector<int> &nums) {
int slow = 0, fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
};