61. 旋转链表
分析
注意 k >= n, k %= n
code
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (!head) return NULL;
auto dummy = new ListNode(-1);
dummy->next = head;
int n = 0;
auto b = dummy;
// 先找出末尾的节点
for (auto p = head; p; p = p->next) {
b = p;
n ++;
}
if (k >= n) k %= n;
if (k == 0) return head;
k = n - k;
auto p = dummy;
for (int i = 0; i < k; i ++ ) p = p->next; // 找出位移后头节点的前一个节点
auto a = p->next; // 4
p->next = NULL;
b->next = dummy->next;
dummy->next = a;
return dummy->next;
}
};
62. 不同路径
分析
dp
code
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> f(m + 1, vector<int>(n + 1));
for (int i = 0; i < m; i ++ )
for (int j = 0; j < n; j ++ ){
if (!i && !j) f[i][j] = 1;
else if (!i) f[i][j] += f[i][j - 1];
else if (!j) f[i][j] += f[i - 1][j];
else f[i][j] += f[i - 1][j] + f[i][j - 1];
}
return f[m - 1][n - 1];
}
};
63. 不同路径 II
分析
同上题
code
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& o) {
int n = o.size(), m = o[0].size();
if (o[0][0] || o[n - 1][m - 1]) return 0;
vector<vector<int>> f(n + 1, vector<int>(m + 1));
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ ){
if (!i && !j) f[i][j] = 1;
else if (!i && !o[0][j - 1]) f[i][j] += f[i][j - 1];
else if (!j && !o[i - 1][0]) f[i][j] += f[i - 1][j];
else {
if (i && !o[i - 1][j]) f[i][j] += f[i - 1][j];
if (j && !o[i][j - 1]) f[i][j] += f[i][j - 1];
}
}
return f[n - 1][m - 1];
}
};
code(优化)
将每层判断都需要用到o的信息, 将o提到循环外
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& o) {
int n = o.size(), m = o[0].size();
if (o[0][0] || o[n - 1][m - 1]) return 0;
vector<vector<int>> f(n + 1, vector<int>(m + 1));
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ ){
if (!o[i][j]){
if (!i && !j) f[i][j] = 1;
else if (!i) f[i][j] += f[i][j - 1];
else if (!j) f[i][j] += f[i - 1][j];
else f[i][j] += f[i - 1][j] + f[i][j - 1];
}
}
return f[n - 1][m - 1];
}
};
64. 最小路径和
分析
思路同机器人那道题
code
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<int>> f(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ ){
if (i == 1) f[i][j] += f[i][j - 1] + grid[i - 1][j - 1];
else if (j == 1) f[i][j] += f[i - 1][j] + grid[i - 1][j - 1];
else f[i][j] += min(f[i - 1][j], f[i][j - 1]) + grid[i - 1][j - 1];
}
return f[n][m];
}
};
65. 有效数字
分析
DiamondZ大佬的题解
这里用A[.[B]][e|EC]
或者.B[e|EC]
表示,其中A和C都是整数(可以有正负号也可以没有),B是无符号整数。
[]代表出现0次或1次,e|E代表指数符号,C是带符号整数。
code
class Solution {
public:
bool isNumber(string s) {
int i = 0;
while (i < s.size() && s[i] == ' ') i ++;
bool numberic = scanInteger(s, i);
if (s[i] == '.') numberic = scanUnsignedInteger(s, ++ i) || numberic;
// scan函数一定要在前, numberic || scanUnsignedInter 会因为numbeic = true, 而不执行scan函数, "3." 答案错误
if (s[i] == 'e' || s[i] == 'E') numberic = scanInteger(s, ++ i) && numberic;
while (i < s.size() && s[i] == ' ') i ++;
return numberic && i == s.size();
}
bool scanInteger(string&s, int& pos){
if (s[pos] == '+' || s[pos] == '-') pos ++;
return scanUnsignedInteget(s, pos);
}
bool scanUnsignedInteger(string& s, int &pos){
int p = pos;
while (pos < s.size() && s[pos] >= '0' && s[pos] <= '9') pos ++;
return pos > p;
}
};
66. 加一
分析
因为原数组是 高位在前, 低位在后, 不方便进位.
- 翻转
- 高精度加法
2.1 for(auto &x : digits) - 翻转结算的结果
code
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
reverse(digits.begin(), digits.end());
int t = 1;
vector<int> res;
for (auto &x : digits){
t += x;
res.push_back(t % 10);
t /= 10;
}
if (t) res.push_back(t);
reverse(res.begin(), res.end());
return res;
}
};
67. 二进制求和
分析
原理同上一题
步骤:
- 翻转原字符串
- 高精度加法
- 翻转计算的结果
code
class Solution {
public:
string addBinary(string a, string b) {
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
int t = 0;
string res;
for (int i = 0; i < a.size() || i < b.size(); i ++ ){
if (i < a.size()) t += a[i] - '0';
if (i < b.size()) t += b[i] - '0';
res.push_back(t % 2 + '0');
t /= 2;
}
if (t) res.push_back(t + '0');
reverse(res.begin(), res.end());
return res;
}
};
68. 文本左右对齐
分析
一行一行处理,每次先求出这一行最多可以放多少个单词,然后分三种情况处理:
- 如果是最后一行,则只实现左对齐:每个单词之间插入一个空格,行尾插入若干空格,使这一行的总长度是 maxWidth;
- 如果这一行只有一个单词,则直接在行尾补上空格;
- 其他情况,则需计算总共要填补多少空格,然后按题意均分在单词之间;
注意 循环变量i扫尾工作
因为循环内带j
变量, j
表示最长下一个需要处理的字符串, 因此下一步i
需要跳到j - 1
的位置, i = j - 1
code
class Solution {
public:
vector<string> fullJustify(vector<string>& words, int maxWidth) {
vector<string> res;
for (int i = 0; i < words.size(); i ++ ){
int j = i + 1;
int len = words[i].size();
while (j < words.size() && len + 1 + words[j].size() <= maxWidth)
len += 1 + words[j ++].size();
string line;
if (j == words.size() || j == i + 1){
line += words[i];
for (int k = i + 1; k < j; k ++ ) line += ' ' + words[k];
while (line.size() < maxWidth) line += ' ';
}
else {
int cnt = j - i - 1, r = maxWidth - len + cnt; // cnt 表示间隔数, r表示空格数
line += words[i]; // 第一个单词前不用补空格, 直接加
int k = 0;
// r % cnt 表示 多余了空格数, 需要平均分给前面几个
// r / cnt 表示 每个间隔需要分配多少空格数
// cnt 表示总共有多少个间隔需要分配空格
while (k < r % cnt) line += string(r / cnt + 1, ' ') + words[i + k + 1], k ++ ;
while (k < cnt) line += string(r / cnt, ' ') + words[i + k + 1], k ++;
}
res.push_back(line);
i = j - 1;
}
return res;
}
};
69. x 的平方根
分析
二分
code
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x;
while (l < r){
int mid = (long long)l + r + 1 >> 1;
if (mid <= x / mid) l = mid;
else r = mid - 1;
}
return l;
}
};
70. 爬楼梯
dp
code
class Solution {
public:
int climbStairs(int n) {
vector<int> f(n + 1);
f[0] = 1, f[1] = 1;
for (int i = 2; i <= n; i ++ )
f[i] += f[i - 1] + f[i - 2];
return f[n];
}
};