1. 有效的数独
https://leetcode-cn.com/problems/valid-sudoku/
思路:哈希表
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int row[9][10] = {0};// 哈希表存储每一行的每个数是否出现过,默认初始情况下,每一行每一个数都没有出现过
// 整个board有9行,第二维的维数10是为了让下标有9,和数独中的数字9对应。
int col[9][10] = {0};// 存储每一列的每个数是否出现过,默认初始情况下,每一列的每一个数都没有出现过
int box[9][10] = {0};// 存储每一个box的每个数是否出现过,默认初始情况下,在每个box中,每个数都没有出现过。整个board有9个box。
for(int i=0; i<9; i++){
for(int j = 0; j<9; j++){
// 遍历到第i行第j列的那个数,我们要判断这个数在其所在的行有没有出现过,
// 同时判断这个数在其所在的列有没有出现过
// 同时判断这个数在其所在的box中有没有出现过
if(board[i][j] == '.') continue;
int curNumber = board[i][j]-'0';
if(row[i][curNumber]) return false;
if(col[j][curNumber]) return false;
if(box[j/3 + (i/3)*3][curNumber]) return false;
row[i][curNumber] = 1;// 之前都没出现过,现在出现了,就给它置为1,下次再遇见就能够直接返回false了。
col[j][curNumber] = 1;
box[j/3 + (i/3)*3][curNumber] = 1;
}
}
return true;
}
};
2. 通配符匹配
https://leetcode-cn.com/problems/wildcard-matching/submissions/
思路:动态规划
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
dp[0][0] = true;
for (int i = 1; i <= n; ++i) {
if (p[i - 1] == '*') {
dp[0][i] = true;
}
else {
break;
}
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p[j - 1] == '*') {
dp[i][j] = dp[i][j - 1] | dp[i - 1][j];
}
else if (p[j - 1] == '?' || s[i - 1] == p[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
}
}
return dp[m][n];
}
};
3. 分数到小数
https://leetcode-cn.com/problems/fraction-to-recurring-decimal/submissions/
思路:
- 首先判断结果是否为负数,只需要判断分子与分母是否异号即可。然后,我们需要将分子分母都转化为正数,否则会影响后续结果。
- 计算整数部分,只需要分子除以分母下取整即可。计算小数部分时,需要考虑是否存在循环节。代码中,我们使用了一个哈希表记录每次做除法时所用的被除数及其上一次出现的位置。如果再次出现,就将上次出现位置与当前位置之间的部分加上括号即可。
class Solution {
using ll = long long;
public:
string fractionToDecimal(int numerator, int denominator) {
ll n = numerator, d = denominator;
string ret;
// 计算整数部分
// 判断负数
if(n * d < 0) ret += "-";
ll a = n / d;
if(a < 0) a *= -1;
ret += to_string(a);
if(n < 0) n*= -1;
if(d < 0) d*= -1;
// 计算小数部分
n %= d;
if(n == 0) {
// 无小数
return ret;
}
ret += ".";
// 连除
// 哈希表记录是否有数组第二次出现
unordered_map<int, int> st;
string t;
int index = 0;
while(n && !st.count(n)) {
st[n] = index++;
n *= 10;
t.push_back((char)(n / d + '0'));
n %= d;
}
if(n != 0) {
// 说明出现了循环,此时对循环部分 [st[n], index] 加括号
ret += t.substr(0, st[n]) + "(" + t.substr(st[n]) + ")";
} else {
ret += t;
}
return ret;
}
};
4. 轮转数组
https://leetcode-cn.com/problems/rotate-array/
思路一:使用额外的数组
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
vector<int> newArr(n);
for (int i = 0; i < n; ++i) {
newArr[(i + k) % n] = nums[i];
}
nums.assign(newArr.begin(), newArr.end());
}
};
时间复杂度: O(n),其中 nn 为数组的长度。
空间复杂度: O(n)。
思路二:数组翻转
class Solution {
public:
void reverse(vector<int>& nums, int start, int end) {
while (start < end) {
swap(nums[start], nums[end]);
start += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums, 0, nums.size() - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.size() - 1);
}
};
时间复杂度:O(n),其中 n 为数组的长度。每个元素被翻转两次,一共 n 个元素,因此总时间复杂度为 O(2n)=O(n)。
空间复杂度:O(1)。
5. 直线上最多的点
https://leetcode-cn.com/problems/max-points-on-a-line
思路:哈希表
用哈希表的方式存储斜率,统计最多数目的的斜率即可
class Solution {
public:
int maxPoints(vector<vector<int>>& points) {
int len = points.size();
// 点的数量不够
if(len < 3) {
return len;
}
int maxNum = 2;
// 遍历每两个点
for(int i = 0; i < len; i ++) {
unordered_map<double, int> count;
for(int j = 0; j < len; j ++) {
if(i != j) {
long long dx = points[i][0] - points[j][0];
long long dy = points[i][1] - points[j][1];
double gradient = dy * 1.0 / dx;
if(count.count(gradient)) {
count[gradient] ++;
} else {
count[gradient] = 2;
}
maxNum = max(maxNum, count[gradient]);
}
}
}
return maxNum;
}
};
6. 填充每个节点的下一个右侧节点指针
https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,’#’ 标志着每一层的结束。
思路:层序遍历
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr) {
return root;
}
// 初始化队列同时将第一层节点加入队列中,即根节点
queue<Node*> Q;
Q.push(root);
// 外层的 while 循环迭代的是层数
while (!Q.empty()) {
// 记录当前队列大小
int size = Q.size();
// 遍历这一层的所有节点
for(int i = 0; i < size; i++) {
// 从队首取出元素
Node* node = Q.front();
Q.pop();
// 连接
if (i < size - 1) {
node->next = Q.front();
}
// 拓展下一层节点
if (node->left != nullptr) {
Q.push(node->left);
}
if (node->right != nullptr) {
Q.push(node->right);
}
}
}
// 返回根节点
return root;
}
};
7. 基本计算器II
https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
思路:栈,乘除优先于加减
class Solution {
public:
int calculate(string s) {
vector<int> stk;
char preSign = '+';
int num = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
if (isdigit(s[i])) {
num = num * 10 + int(s[i] - '0');
}
if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1) {
switch (preSign) {
case '+':
stk.push_back(num);
break;
case '-':
stk.push_back(-num);
break;
case '*':
stk.back() *= num;
break;
default:
stk.back() /= num;
}
preSign = s[i];
num = 0;
}
}
return accumulate(stk.begin(), stk.end(), 0);
}
};
8. 删除链表中的节点
https://leetcode-cn.com/problems/delete-node-in-a-linked-list/
思路
和下一个节点交换
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};
9. 有效的字母异位词
https://leetcode-cn.com/problems/valid-anagram/
若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
思路:排序
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.length() != t.length()) {
return false;
}
sort(s.begin(), s.end());
sort(t.begin(), t.end());
return s == t;
}
};
思路二:哈希表
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.length() != t.length()) {
return false;
}
vector<int> table(26, 0);
for (auto& ch: s) {
table[ch - 'a']++;
}
for (auto& ch: t) {
table[ch - 'a']--;
if (table[ch - 'a'] < 0) {
return false;
}
}
return true;
}
};
时间复杂度:O(n),其中 n 为 s的长度。
空间复杂度:O(S),其中 S 为字符集大小,此处 S=26。
10. 奇偶链表
https://leetcode-cn.com/problems/odd-even-linked-list/
思路:分离节点后合并
class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
if (head == nullptr) {
return head;
}
ListNode* evenHead = head->next;
ListNode* odd = head;
ListNode* even = evenHead;
while (even != nullptr && even->next != nullptr) {
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
odd->next = evenHead;
return head;
}
};
时间复杂度:O(n),其中 n 是链表的节点数。需要遍历链表中的每个节点,并更新指针。
空间复杂度:O(1)。只需要维护有限的指针。
11. 递增的三元子序列
https://leetcode-cn.com/problems/increasing-triplet-subsequence/submissions/
思路:
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int one = INT_MAX,two = INT_MAX;
for(int three : nums){
if(three > two) return true;
else if(three <= one) one = three;
else two = three;
// if(three > one && three < two) two = three;
}
return false;
}
};
12. 矩阵中的最长递增路径
https://leetcode-cn.com/problems/longest-increasing-path-in-a-matrix/
思路:记忆化dfs
class Solution {
public:
static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int rows, columns;
int longestIncreasingPath(vector< vector<int> > &matrix) {
if (matrix.size() == 0 || matrix[0].size() == 0) {
return 0;
}
rows = matrix.size();
columns = matrix[0].size();
auto memo = vector< vector<int> > (rows, vector <int> (columns));
int ans = 0;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j) {
ans = max(ans, dfs(matrix, i, j, memo));
}
}
return ans;
}
int dfs(vector< vector<int> > &matrix, int row, int column, vector< vector<int> > &memo) {
if (memo[row][column] != 0) {
return memo[row][column];
}
++memo[row][column];
for (int i = 0; i < 4; ++i) {
int newRow = row + dirs[i][0], newColumn = column + dirs[i][1];
if (newRow >= 0 && newRow < rows && newColumn >= 0 && newColumn < columns && matrix[newRow][newColumn] > matrix[row][column]) {
memo[row][column] = max(memo[row][column], dfs(matrix, newRow, newColumn, memo) + 1);
}
}
return memo[row][column];
}
};
13. 反转字符串
https://leetcode-cn.com/problems/reverse-string/
思路:双指针
class Solution {
public:
void reverseString(vector<char>& s) {
int n = s.size();
for (int left = 0, right = n - 1; left < right; ++left, --right) {
swap(s[left], s[right]);
}
}
};
14. 至少有K个重复字符的最长子串
https://leetcode-cn.com/problems/longest-substring-with-at-least-k-repeating-characters/submissions/
思路:分治
class Solution {
public:
int dfs(const string& s, int l, int r, int k) {
vector<int> cnt(26, 0);
for (int i = l; i <= r; i++) {
cnt[s[i] - 'a']++;
}
char split = 0;
for (int i = 0; i < 26; i++) {
if (cnt[i] > 0 && cnt[i] < k) {
split = i + 'a';
break;
}
}
if (split == 0) {
return r - l + 1;
}
int i = l;
int ret = 0;
while (i <= r) {
while (i <= r && s[i] == split) {
i++;
}
if (i > r) {
break;
}
int start = i;
while (i <= r && s[i] != split) {
i++;
}
int length = dfs(s, start, i - 1, k);
ret = max(ret, length);
}
return ret;
}
int longestSubstring(string s, int k) {
int n = s.length();
return dfs(s, 0, n - 1, k);
}
};