目录
209. 长度最小的子数组
题目
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3]是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4] 输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
进阶:
- 如果你已经实现
O(n)
时间复杂度的解法, 请尝试设计一个O(n log(n))
时间复杂度的解法。
解题思路
算法:双指针,滑动窗口
1.初始化变量
定义两个指针 i 和 j ,用来确定字数组的起始和结束位置。定义sum用来计算当前子数组的和,len用来存储当前子数组的长度,result 用来存储最小的满足条件的子数组长度,初始化为一个很大的值(INT_MAX),这样找到的任何的有效长度都会小于这个初始值。
2.遍历数组
然后使用一个循环遍历数组,在每次循环过程中,将当前元素 nums[j] ,添加到 sum 中。
3.检查子数组和
如果 sum 大于或等于 target ,则进入内部循环,在内部循环中,首先计算当前子数组的长度len(即j-i+1)。然后,更新 result 为 result 和 len 中的较小值。接着,从 sum 中减去子数组起始位置的元素 nums[i] ,并将 i 向右移动一个位置(即缩小子数组的范围,看是否找到更小的满足条件的子数组)。
4.返回结果
如果在遍历整个数组后没有找到满足条件的子数组(即 result 仍然是 INT_MAX ),则返回 0 。否则,返回 result (即最小的满足条件的子数组的长度)
【补充】
- INT_MAX为 2^31-1 ,即 2147483647 ;
- INT_MIN为 -2^31 , 即 2147483648 ;
- 在使用INT_MAX和INT_MIN时,需要 #include<limits.h>;
代码实现
int minSubArrayLen(int target, int* nums, int numsSize) {
int i = 0, j, sum = 0, len = 0, result = INT_MAX;
// i 和 j 是双指针,用于确定子数组的起始和结束位置
// sum 用于计算当前子数组的和
// len 用于存储当前子数组的长度
// result
// 用于存储最小的满足条件的子数组长度,初始化为一个很大的值(INT_MAX)
{
for (j = 0; j < numsSize; j++) {
// 遍历数组,j 是当前考虑的元素的索引
sum += nums[j]; // 将当前元素加入sum中
while (sum >= target) {
// 当sum大于等于target时,进入循环
len = j - i + 1; // 计算当前子数组的长度
// 更新result为当前result和len中的较小值
result = result < len ? result : len;
sum -= nums[i]; // 从sum中减去子数组起始位置的元素
i++; // 将子数组的起始位置向右移动一个位置
}
}
// 如果在遍历过程中没有找到满足条件的子数组,则result仍然为INT_MAX
// 因此,如果result等于INT_MAX,则返回0;否则返回result
return result == INT_MAX ? 0 : result;
}
}
LCR 024. 反转链表
题目
给定单链表的头节点 head
,请反转链表,并返回反转后的链表的头节点。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
提示:
- 链表中节点的数目范围是
[0, 5000]
-5000 <= Node.val <= 5000
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
解题思路
法1:双指针、迭代
1.初始化指针
定义两个指针 current 和 pre ,分别用来指向链表的头结点 head 和 current 的前一个节点,初始化为 NULL 。
2.遍历链表
使用一个 while 循环遍历链表,直到 current 为 NULL。
3.改变节点指针方向
在每次循环中首先定义一个临时指针 temp ,用来指向 current 的下一个节点(因为需要移动current)。然后,将 current 的 next 指针指向前一个节点 pre ,这样就反转了 current 节点的指向。
4.移动指针
将 pre 更新为 current ,因为想让 pre 指向刚刚反转过的节点。将 current 更新为 temp ,因为想继续处理链表的下一个节点。
5.返回反转后的头结点
当 current 为 NULL 时,循环结束,此时 pre 将指向反转后链表的最后一个节点,也就是新链表的头结点。函数返回 pre ,即反转后的链表头结点。
在这个过程中,链表中的每个节点得到 next 指针都被反转,从而实现了链表的反转。
法2:递归
1.定义递归函数reverse
改递归函数接受两个参数:current(当前节点)和 pre(前一个节点),目的是将当前节点 current的 next 指针指向 pre ,然后递归处理剩余的链表。
2.递归处理
如果 current 为 NULL ,说明已经到达链表的末尾,返回 pre 作为新链表头结点。否则,将 current 的 next 指针保存到一个临时变量 temp 中,然后将 current 的 next 指针指向 pre。递归调用 reverse 函数,传入 temp 和 current 作为参数,继续处理剩余的链表。
3.调用递归函数reverse
在 reverseList 函数中,调用 reverse 函数,将头结点 head 和一个空指针 NULL 作为参数传递给reverse,这样,当递归到链表的末尾时,空指针将作为新链表的头结点的前一个节点,从而实现链表的反转。
代码实现
//迭代方式
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* current = head;
struct ListNode* pre = NULL;
while (current) {
struct ListNode* temp = current->next;
current->next=pre;
pre=current;
current=temp;
}
return pre;
}
//递归方式
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverse(struct ListNode* current, struct ListNode* pre) {
if (current == NULL) {
return pre;
}
struct ListNode* temp = current->next;
current->next = pre;
return reverse(temp, current);
}
struct ListNode* reverseList(struct ListNode* head) {
return reverse(head, NULL);
}