算法第二天

209 长度最小的子数组

力扣链接:[209.长度最小的子数组](209. 长度最小的子数组 - 力扣(LeetCode))

【题目描述】

给定一个含有 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_O。因此就想着寻找其他的方法来解决,在查阅一些资料后发现了一个重要的操作数组手段——滑动窗口。

滑动窗口

这也是一个双指针的思想,先让右指针向后滑动,每移动一个长度就更新窗口内的数据,同时当动态窗口内的数据满足我们的需要时右指针就停止滑动,此时再让左指针向后移动,直到窗口内的元素已经不能满足需求时,左指针停止移动。重复上述操作直到右指针遍历完整个数组。

在上述对滑动窗口的分析可以看出,滑动窗口的思想并没有很复杂,重要的是对动态窗口的更新和维护,也就是新旧元素的加入、移出。

对于本题来说,就是保持窗口内数值总和要大于等于target,在此基础上对左右指针进行移动,同时对子数组的长度进行更新,在最后返回。

【代码】

暴力破解

public class Solution {
    public int MinSubArrayLen(int target, int[] nums) {
        int sum = 0;
        int subLength = 0;
        int minLength = int.MaxValue;
        for(int i = 0;i < nums.Length;i++)
        {
            sum = 0;
            for(int j = i;j < nums.Length;j++)
            {
                sum += nums[j];
                if(sum >= target)
                {
                    subLength = j - i + 1;
                    minLength = minLength < subLength ? minLength : subLength;
                    break;
                }
            }
        }
        minLength = minLength == int.MaxValue ? 0 : minLength;
        return minLength;
    }
}

思路比较简单清晰,但是测试的结果往往很骨感()。不出意外的超时。

滑动窗口

public class Solution {
    public int MinSubArrayLen(int target, int[] nums) {
        int result = int.MaxValue;
        int i = 0;
        int sum = 0;
        int subLength = 0;
        for (int j = 0;j< nums.Length;j++)
        {
            sum += nums[j];
            while(sum >= target) 
            {
                subLength = j - i + 1;
                result = result < subLength? result : subLength;
                sum -= nums[i++];
            }
        }
        return result == int.MaxValue ? 0 : result;
    }
}

在结尾return的时候使用一个三元运算符,用于判断传入的数组中是否存在满足条件的子数组。

善用三元运算符可以提高代码的可读性以及增加代码在执行过程中的效率。


977 有序数组的平方

力扣链接:[977.有序数组的平方](977. 有序数组的平方 - 力扣(LeetCode))

【题目描述】

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

【示例】

示例1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

【思路】

初看这一题,想法比较简单,先对整个数组元素进行平方操作,然后调用sort(快排)进行排序,因此整体算法的时间复杂度就取决于快排O(nlgn)。

对于我个人第一遍写代码时没有想到对他优化的方法,然后又看到了熟悉的双指针。卡尔大神的讲解分析还是比较简单易懂,题目中给了提示——非递减。可以观察到数组在平方后,负数元素有可能会成为最大值,那么就能得出数组元素在平方后的最大值只可能在数组的两端,而不是在中间。这个时候我们就可以使用双指针了,同时判断最左侧和最右侧元素的平方值,然后放入一个新的数组中,之后左侧后移或者右侧前移,重复上述操作直到每个元素都被遍历。

【代码】

双指针法:

public class Solution {
    public int[] SortedSquares(int[] nums) {
        int size = nums.Length-1;
        int[] result = new int[size+1];
        for(int i = 0, j = nums.Length-1;i <= j;)
        {
            if(nums[i] * nums[i] < nums[j] * nums[j])
            {
                result[size--] = nums[j] * nums[j];
                j--;
            }
            else 
            {
                result[size--] = nums[i] * nums[i];
                i++;
            }
        }
        return result;
    }
}

思想在上面已经说过了这里就不在赘述了。

同时还看到一个C#的LINQ骚操作,上代码:

public class Solution {
    public int[] SortedSquares(int[] nums) {
        return nums.Select(x => x * x).OrderBy(x => x).ToArray();
    }
}

首先使用Select将原数组的每个元素进行平方,返回一个包含平方之后结果的新序列,再调用Orderby方法对新序列中的元素进行升序排列,最后通过ToArray方法将排序后的结果打包成一个新的整型数组返回。

由于本人对于C#的学习还不够深,短期应该还想不出这种解法(),希望和大家共同学习。

posted @ 2023-06-10 15:48  asjdqi  阅读(10)  评论(0)    收藏  举报