2023年8月——每日一题
1、8月6日 24. 两两交换链表中的节点
思路:直接模拟
使用虚拟头结点,初始时cur指向虚拟头结点,然后执行三步骤,具体见代码
C++代码
/**
* 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* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur && cur->next && cur->next->next) {
ListNode* node1 = cur->next;
ListNode* node2 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = node1;
cur->next->next->next = node2;
cur = cur->next->next;
}
return dummyHead->next;
}
};
2、8月7日 344. 反转字符串
思路:双指针
C++代码
class Solution {
public:
void reverseString(vector<char>& s) {
int first = 0, last = s.size() - 1;
while (first < last) {
char temp = s[first];
s[first++] = s[last];
s[last--] = temp;
}
}
};
3、8月8日 1749. 任意子数组和的绝对值的最大值
思路:贪心
有两种情况可以得到和的绝对值最大值,一种情况是正和的最大值,另一种情况是负和的最小值。
(同7月20日每一题类似:918. 环形子数组的最大和)
- 情况一:通过
i
遍历nums
(可视为数组开头),count1
记录数组和(可以视为末尾),当count1<0
时,抛弃前面元素,count1
置为0,从i + 1
开始重新计算。 - 情况二:通过
i
遍历nums
(可视为数组开头),count2
记录数组和(可以视为末尾),当count2>0
时,抛弃前面元素,count1
置为0,从i + 1
开始重新计算。
最终返回正和最大值和负的负和最小值中的较大值。
C++代码
class Solution {
public:
int maxAbsoluteSum(vector<int>& nums) {
int res1 = INT_MIN;
int res2 = INT_MAX;
int count1 = 0;
int count2 = 0;
for (int i = 0; i < nums.size(); i++) {
count1 += nums[i];
res1 = max(res1, count1);
if (count1 < 0) count1 = 0;
count2 += nums[i];
res2 = min(res2, count2);
if (count2 > 0) count2 = 0;
}
return max(res1, -res2);
}
};
4、8月9日 1281. 整数的各位积和之差
思路:直接模拟
提取出n
的各个位的值,并求积和和,最后求差
C++代码
class Solution {
public:
int subtractProductAndSum(int n) {
int sum1 = 0, sum2 = 1;
while (n > 0) {
int temp = n % 10;
sum1 += temp;
sum2 *= temp;
n /= 10;
}
return sum2 - sum1;
}
};
5、8月10日 1289. 下降路径最小和 II
思路:动态规划
动态规划四步走:确定dp数组的下标及含义、确定递推公式、dp数组初始化、确定遍历顺序。
(和7月13日 931. 下降路径最小和类似)
- dp数组的下标及含义:
dp[i][j]
表示以grid[i][j]
为结尾的下降路径最小和 - 确定递推公式:
dp[i][j] = grid[i][j] + min(dp[i - 1][k])
,其中k不等于j - dp数组初始化:
dp[0][j] = grid[0][j]
- **确定遍历顺序:**从左到右,从上到下。
最后返回dp[dp.size() - 1][j]
的最小值即可。
C++代码
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& grid) {
vector<vector<int>> dp(grid.size(), vector<int>(grid[0].size()));
for (int j = 0; j < dp[0].size(); j++) dp[0][j] = grid[0][j];
for (int i = 1; i < dp.size(); i++) {
for (int j = 0; j < dp[0].size(); j++) {
int temp = INT_MAX;
for (int k = 0; k < dp[0].size(); k++) {
if (j != k) temp = min(temp, dp[i - 1][k]);
}
dp[i][j] = grid[i][j] + temp;
}
}
int res = INT_MAX;
for (int j = 0; j < grid[0].size(); j++) res = min(res, dp[dp.size() - 1][j]);
return res;
}
};
6、8月11日 1572. 矩阵对角线元素的和
思路:直接模拟
判断数组大小是偶数*偶数
还是奇数*奇数
,如果是偶数的话,直接相加两个对角线的元素,否则的话还需要减去中间元素值。
C++代码
class Solution {
public:
int diagonalSum(vector<vector<int>>& mat) {
int n = mat.size();
int res = 0;
for (int i = 0, j = n - 1; i < n; i++, j--) {
res += mat[i][i];
res += mat[i][j];
}
if (n % 2 == 0) return res;
else return res - mat[n / 2][n / 2];
}
};
7、8月12日 23. 合并 K 个升序链表
思路:遍历合并
- **合并两个有序链表的思路:**当
list1
和list2
都不是空链表时,判断list1和list2哪一个链表的头节点的值更小,将较小值的节点添加到结果里,当一个节点被添加到结果里之后,将对应链表中的节点向后移一位。最后只有一个链表不为空,并将其添加到结果里。 - 遍历合并:遍历
lists
,逐步合并两个链表。
C++代码
/**
* 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummyHead = new ListNode(0);
ListNode* pre = dummyHead;
while (list1 != nullptr && list2 != nullptr) {
if (list1->val <= list2->val) {
pre->next = list1;
list1 = list1->next;
} else {
pre->next = list2;
list2 = list2->next;
}
pre = pre->next;
}
pre->next = list1 == nullptr ? list2 : list1;
return dummyHead->next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* temp = nullptr;
for (int i = 0; i < lists.size(); i++) {
temp = mergeTwoLists(temp, lists[i]);
}
return temp;
}
};
8、8月13日 88. 合并两个有序数组
思路:三指针
从后向前遍历,比较nums1
和nums2
的元素值,选择最大的即可。最后nums2
元素如果有剩余则覆盖nums1
中元素即可,nums1
的元素有剩余不用进行任何操作。
C++代码
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int index1 = m - 1, index2 = n - 1, index = nums1.size() - 1;
while (index1 >= 0 && index2 >= 0) {
nums1[index--] = nums1[index1] > nums2[index2] ? nums1[index1--] : nums2[index2--];
}
while (index2 >= 0) nums1[index--] = nums2[index2--];
}
};