31. 下一个排列
- 先从后往前找到 k 使得
nums[k - 1] < nums[k] - 如果不存在这样的 k,说明这个数的排列已经是最大值,直接将数组逆转即可
- 如果存在这样的 k,则接着从后往前找到一个 t,使得
nums[t] <= nums[k - 1] - 交换
nuts[t]与nuts[k - 1],然后逆转该数组 k位之后的部分
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int k = nums.size() - 1;
while(k > 0 && nums[k - 1] >= nums[k]) k -- ;
if(k <= 0) {
reverse(nums.begin(), nums.end());
} else {
int t = k;
while(t < nums.size() && nums[t] > nums[k - 1]) t ++ ;
swap(nums[t - 1], nums[k - 1]);
reverse(nums.begin() + k, nums.end());
}
}
};
32. 最长有效括号
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> stk;
int res = 0;
for(int i = 0, st = -1; i < s.size(); i ++ ) {
if(s[i] == '(') stk.push(i);
else {
if(stk.size()) {
stk.pop();
if(stk.size()) {
res = max(res, i - stk.top());
} else {
res = max(res, i - st);
}
} else {
st = i;
}
}
}
return res;
}
};
33. 搜索旋转排序数组
使用二分搜索查找出数组中的最小值在哪里,再确定target值在数组的前一半还是后一半,在这一半区间继续使用二分搜索
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.empty()) return -1; //空返回
int l = 0, r = nums.size() - 1;
while(l < r) {
int mid = l + r + 1 >> 1;
if(nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
if(target >= nums[0]) l = 0;
else l = r + 1, r = nums.size() - 1;
while(l < r) {
int mid = l + r >> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] == target) return r;
return -1;
}
};
34. 在排序数组中查找元素的第一个和最后一个位置
- 二分查找第一个值大于等于
target的数字位置,若该位置的值不等于target,返回{-1, -1} - 二分查找第一个
target大于其他值的数字位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.empty()) return {-1, -1};
int l = 0, r = nums.size() - 1;
while(l < r)
{
int mid = l + r >> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] != target) return {-1, -1};
int L = l;
l = 0, r = nums.size() - 1;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(nums[mid] <= target) l = mid;
else r = mid - 1;
}
return {L, r};
}
};
35. 搜索插入位置
- 令
l = 0, r = n,这样可以将查找到的位置直接返回 - 二分查找值大于等于
target的第一个位置 - 返回
l
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, r = nums.size();
while(l < r)
{
int mid = l + r >> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
return l;
}
};
36. 有效的数独
- 使用三个数组记录行列和九宫格上中的数字存在情况
- 利用九位二进制数记录某一个位对应的数字是否出现过,如
010,000,000表示8 - 九宫格内的位置则从二维展开成一维
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
vector<int> row(9), col(9), squ(9); // 使用三个整型数组判重。
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.')
continue;
if (board[i][j] < '1' || board[i][j] > '9') return false;
int num = board[i][j] - '0';
// 以row[i] & (1 << num)为例,这是判断第i行中,num数字是否出现过。
// 即row[i]值的二进制表示中,第num位是否是1。
// 以下col和squ同理。
if ((row[i] & (1 << num)) ||
(col[j] & (1 << num)) ||
(squ[(i / 3) * 3 + (j / 3)] & (1 << num)))
return false;
row[i] |= (1 << num);
col[j] |= (1 << num);
squ[(i / 3) * 3 + (j / 3)] |= (1 << num);
}
return true;
}
};
37. 解数独
class Solution {
public:
bool row[9][9], col[9][9], cell[3][3][9];
void solveSudoku(vector<vector<char>>& board) {
memset(row, 0, sizeof row);
memset(col, 0, sizeof col);
memset(cell, 0, sizeof cell);
for(int i = 0; i < 9; i ++ )
for(int j = 0; j < 9; j ++ )
if(board[i][j] != '.') {
int t = board[i][j] - '1';
row[i][t] = col[j][t] = cell[i / 3][j / 3][t] = true;
}
dfs(board, 0, 0);
}
bool dfs(vector<vector<char>>& board, int x, int y) {
if(y == 9) x ++ , y = 0;
if(x == 9) return true;
if(board[x][y] != '.') return dfs(board, x, y + 1);
for(int i = 0; i < 9; i ++ )
if(!row[x][i] && !col[y][i] && !cell[x / 3][y / 3][i]) {
board[x][y] = '1' + i;
row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = true;
if(dfs(board, x, y + 1)) return true;
board[x][y] = '.';
row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = false;
}
return false;
}
};
38. 外观数列
- 1
- 11
- 21
- 1211
- 111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”
模拟从 2到n的顺序生成字符串,重复将连续的相同值进行基数,迭代下去即可
class Solution {
public:
string countAndSay(int n) {
string s = "1";
for(int i = 0; i < n - 1; i ++ ) {
string t;
for(int j = 0; j < s.size(); ) {
int k = j + 1;
while(k < s.size() && s[k] == s[j]) k ++ ;
t += to_string(k - j) + s[j];
j = k;
}
s = t;
}
return s;
}
};
39. 组合总和
暴力搜索即可
- 在每一层搜索中,枚举当前数字可以添加几次
- 边界条件是数组中的和为
target,或者当前层数比数组长度长
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> combinationSum(vector<int>& cs, int target) {
dfs(cs, 0, target);
return res;
}
void dfs(vector<int>& cs, int u, int target) {
if(target == 0) { //目前数组中的值已经等于target
res.push_back(path);
return;
}
if(u == cs.size()) return; //枚举完当前数组所有数字,返回
for(int i = 0; cs[u] * i <= target; i ++ ) {
//压入一个当前值,dfs搜索一次
dfs(cs, u + 1, target - cs[u] * i);
path.push_back(cs[u]);
}
//回溯
for(int i = 0; cs[u] * i <= target; i ++ ) {
path.pop_back();
}
}
};
40. 组合总和 II
- 与39相似,不同点在于每个数字只能使用一次,计算每个数字总共有几个,每次压入数组中不超过个数即可
- 使用
sort从小到大排序,可以实现剪枝的效果
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> combinationSum2(vector<int>& c, int target) {
sort(c.begin(), c.end()); //优化搜索顺序
dfs(c, 0, target);
return ans;
}
void dfs(vector<int>& c, int u, int target) {
if(target == 0) {
ans.push_back(path);
return;
}
if(u == c.size()) return;
int k = u + 1;
while(k < c.size() && c[k] == c[u]) k ++ ;
int cnt = k - u; //计算总共有几个当前值
for(int i = 0; c[u] * i <= target && i <= cnt; i ++ ) {
dfs(c, k, target - c[u] * i);
path.push_back(c[u]);
}
for(int i = 0; c[u] * i <= target && i <= cnt; i ++ ) {
path.pop_back();
}
}
};
本文精选了LeetCode上的多个经典算法题目,包括下一个排列、最长有效括号等,并提供了详细的解决方案。通过这些题目,读者可以深入理解算法设计的思路与技巧。

被折叠的 条评论
为什么被折叠?



