算法记录 | Day2数组基础Ⅱ

 977-有序数组的平方

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

输入:nums = [-4,-1,0,3,10]

输出:[0,1,9,16,100]

解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]

解题思路

  • 该题直接暴力平方后排序,时间复杂度O(n+nlogn);双指针法,时间复杂度O(n)
  • 数组其实是有序的,只不过负数平方之后可能成为最大数了,暴力法没有充分利用这点
  • 数组平方的最大值就在数组的两端,不是最左边就是最右边,越往中间越小,因此可以考虑双指针

 暴力排序法

Class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 1.暴力排序 
        for i in range(0,len(nums)):
            nums[i] *= nums[i];
            nums.sort();
            return nums

 暴力排序法+列表推导法

Class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 2.暴力排序+列表推导
        for i in range(0,len(nums)):
            return sorted(x*x for x in nums)

注:列表推导法:结构是在一个中括号里包含一个表达式 + 一个for语句+ 0个 或 多个 for 或 if 语句;返回结果是一个新的列表;逻辑上等价于一个循环语句,只是形式上更加简洁。如:aList = [w.strip() for w in freshfruit]。

 双指针法

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 3.双指针
        i,j=0,len(nums)-1;
        loc=len(nums)-1;
        results=[0]*len(nums) # 需要提前定义列表,存放结果
        while i<=j:
            if (nums[i]**2<nums[j]**2):  # 左右边界进行对比,找出最大值
                results[loc]=nums[j]**2;
                j-=1; # 右指针往左移动
            else:
                results[loc]=nums[i]**2;
                i+=1; # 左指针往右移动
            loc-=1; # 存放结果的指针需要往前平移一位
        return results

难点

  • 双指针法变化较多,难以想到

 总结

因为这个数值排列一定是两边的绝对值相互比较,中间的值更小,可以用双指针。与昨天不同的是,27-移除元素双指针(快慢指针)是同一个方向的,977双指针是相向而行的。

209-长度最小的子数组

题目:给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]并返回其长度。如果不存在符合条件的子数组,返回 0

输入:target = 7, nums = [2,3,1,2,4,3]

输出:2

解释:子数组 [4,3]是该条件下的长度最小的子数组。

解题思路

  • 该题直接用双重循环,时间复杂度O(n^2),力扣会超时
  • 可以运用双指针的变种:滑动窗口法,时间复杂度O(n),通过不断的调节子序列的起始位置和终止位置,得出想要的结果
  • 在暴力解法中,是一个for循环为滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程;
  • 滑动窗口法中,用一个for循环实现,那么这个循环的索引,一定是表示 滑动窗口的终止位置。否则又回到暴力解法。

暴力法(超时)

暴力的解法就是两层for循环

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        #1. 暴力(不用把起始值单独拿出来算
        min_len = float("inf"); # 正无穷
        for i in range(0,len(nums)):
            cur_sum = 0; # 起始移动后需要清零
            for j in range (i,len(nums)):
                cur_sum += nums[j];
                if cur_sum >= target:
                    min_len = min(min_len,j-i+1);
                    break;
        return min_len if min_len!=float('inf') else 0;

双指针法(滑动窗口法)

一个for循环控制终止位置,再根据当前子序列和大小的情况,不断调节子序列的起始位置

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        #2. 双指针(滑动窗口法)
        i,j = 0,0;
        cur_sum = 0;
        min_len = float('inf')
        for j in range(0,len(nums)):
            cur_sum += nums[j]; # 当前的累加值。不用每回清零,渐少重复运算
            while cur_sum >= target: # 当前累加值大于目标值
                min_len = min(min_len,j-i+1)
                cur_sum -= nums[i]
                i += 1;
        return min_len if min_len!=float('inf') else 0

窗口的移动:

  • 窗口的起始位置 i 如何移动:如果当前窗口的值大于target了,窗口就要向前移动了(也就是该缩小了)
  • 窗口的结束位置 j 如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

难点

  • 题目在什么条件限制下,可以利用滑动窗口的解法
  • 关于时间复杂度:不要以为for里放一个while就以为是O(n^2), 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。

 总结

双指针法一般是减少循环层数,数组当中目前接6触到的感觉可分为2类:① 一个负责循环遍历,一个负责记录/更新(快慢指针法、滑动窗口法);② 相向的指针,职能相同,对称(977有序数组平方)。

 59-螺旋矩阵Ⅱ

题目:给你一个正整数 n ,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

输入:n = 3

输出:[[1,2,3],[8,9,4],[7,6,5]]

解题思路

  • 本题属于模拟过程,不涉及算法
  • 求解本题要坚持循环不变量原则(二分法也),解决拐角的处理
  • 这里边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就要反复调节,很可能在循环中迷失方向。
  • 一圈下来,我们要画四条边,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0]*n for _ in range(n)];
        count = 1;
        for offset in range (1,n//2+1):  # 每循环一层偏移量加1;n为偶数循环n//2次,因为一次少2宽度
            for j in range(offset-1,n-offset): # 从左至右,左闭右开
                nums[offset-1][j] = count;
                count += 1;
            for i in range(offset-1,n-offset): # 从上至下,左闭右开
                nums[i][n-offset] = count;
                count += 1;
            for j in range(n-offset,offset-1,-1): # 从右至左,左闭右开
                nums[n-offset][j] = count;
                count += 1;
            for i in range(n-offset,offset-1,-1): # 从下至上,左闭右开
                nums[i][offset-1] = count;
                count += 1;
        if n%2 != 0: # 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
            nums[n//2][n//2] = count;
        return nums;

时间复杂度 O(n^2):模拟遍历二维矩阵的时间;空间复杂度 O(1)

难点

  • 找到循环不变量,确定边处理的统一原则,解决拐角问题
  • 循环次数:n为偶数循环n//2次,因为一次少2宽度/高度;n为奇数,需要单独给矩阵最中间的位置赋值

 总结

循环不变量原则是写程序中的重要原则。平时可能感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实真正解决题目的代码都是简洁的,或者有原则性的。

代码随想录算法训练营Day36 力扣1049.最后一块石头的重量Ⅱ【medium】 力扣474.一和零【meidum】 阅读详情

相关推荐

Day24 LeetCode】贪心Ⅱ

这题第一想法是使用动态规划做,每天有两个状态,持有股票和非持有股票,每次计算这两个状态下的最优值。贪心的做法就是 只要当前股票值会在明天上升,则在当前进行购买,在明天进行售卖获取利润。因为要求只能持有一只股票,即使price[i]到price[j]之间股票一直在涨,亦可将利润划分成pricej−priceipricej−pricej−1])pricej−1−pricej−2])...pricei1−pri。

H2020fighting的博客 925

day2-数组(有序数组的平方、长度最小的子数组、螺旋矩阵Ⅱ)

看到题目的第一想法:录入数组和需要满足的条件s后使用双指针,初始化一个长度变量为1,然后遍历该数组寻找满足该条件的子数组。看到题目的第一想法:录入数组后直接遍历数组,对每个元素进行平方。看完代码随想录的想法:自己看题目时漏掉了“连续”两个字,导致解题思路错误,但是双指针的方法是对的。实现过程中遇到的困难:一开始没注意循环不变量的问题,后来统一规定了左闭右开就好了。看完代码随想录的想法:双指针法更加简便快捷,且清晰易懂,比暴力算法简单。看完代码随想录的想法:将矩阵四条边分开循环就好,同时还要注意循环的圈数。

Wendyfwd的博客 428

算法记录 | Day2数组基础(长度最小子数组、螺旋矩阵、区间和以及开发商购买土地)

【代码】算法记录 | Day2数组基础(长度最小子数组、螺旋矩阵、区间和以及开发商购买土地)

qq_57274088的博客 318

代码随想录算法训练营Day32 | 122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组

训练营第32天:122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组

jiabao0520的博客 1357

Day2>>209、长度最小的子数组 + 59、螺旋矩阵Ⅱ

刚看这道题,完全没有任何思路,就直接去看讲解视频了。看完视频后思路还算清晰,尝试自己独立写了一遍,但还是由于太久没有手撕过算法题,中间还是被一些很基础的语法知识给卡住了。还是要多加练习,不能眼高手低。时间原因先不写解题思路了,等周末再练习一遍再写。刚开始看这道题基本上没什么可实现的思路,只是在想遍历数组,然后累加去找满足要求的子数组,但是不知道怎么实现。为了节省时间,还是先看了讲解视频。看完视频后,思路就清晰多了。附加题留到周末再补了,睡太晚实在是顶不住,晚上九点多的时候在公司都想直接趴下睡了。

weixin_46660114的博客 272

Day2 C++算法题—LCR019.验证回文串Ⅱ&LCR072.x的平方根

C++算法题分享—验证回文串Ⅱ&x的平方根

T_X_Parallel的博客 762

Day2 有序数组的平方、长度最小的子数组、螺旋矩阵Ⅱ

977.有序数组的平方、209.长度最小的子数组、904.水果成篮、76.最小覆盖子串、59.螺旋矩阵Ⅱ、54.螺旋矩阵

weixin_45502666的博客 1033

算法训练 day29 组合总和、组合总和Ⅱ、分割回文串】

整体比较简单。本题要求不能有重复元素,其实有两层含义。第一:每个元素不能重复选取它自己;第二:在满足第一个重复条件下,最终结果不能出现重复的组合比如:[1,2,5]与[2,1,5]。第一个比较容易实现,每次递归传递下标值时自加1即可,第二个需要额外进行判断,这就体现出排序的好处了。排好序后,如果当前下标不为传递过来的index值且该下标代表的值与index代表的值相同,则应该跳过该值,因为该值已经在前面使用过了。

m0_67540113的博客 756

Day4 C++算法题—11.盛最多水的容器&47.全排列Ⅱ

Day4 C++算法题分享及解析—11.盛最多水的容器&47.全排列Ⅱ

T_X_Parallel的博客 1082

代码随想录算法训练营Day39

力扣213.打家劫舍Ⅱ【medium】 力扣543.二叉树的直径【easy】 力扣124.二叉树种的最大路径和【hard】 力扣337.打家劫舍Ⅲ【medium】

lololo的博客 1577

LeetCode刷题】Day08 字符串基础Ⅱ(KMP算法)

KMP算法难难难

Elinor_的博客 695

练题100天——DAY27:两个数组交集Ⅱ+第三大的数

今天只有两道题呢,也很简单,难度★吧,能做出来。今天是有点懈怠了,但是还是撑着做了两道题,不是吗,不要太苛责自己,状态好的时候多写点,状态差的时候少写点,只要写了就不算原地踏步。

2301_81099859的博客 892

代码随想录算法训练营Day02-数组 | LC209长度最小的子数组、LC059螺旋矩阵Ⅱ、区间和、开发商购买土地 | Java代码实现

第二天打卡,今天也没看视频,写两行看一眼提示,不能完全自己写出来,但代码做出了进一步优化。今天的博客写的比较简单后两题使用ACM风格的输入输出,快读快写忘了,Scanner类占大部分运行时间,用快读快写速度会提高很多。

m0_72781813的博客 1150

代码随想录打卡|day06

题目描述:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i!思路:使用单个for循环确定数字nums[i],随后将i+1到nums.length作为一个新的范围并使用双指针来找到和nums[i]的和为0的nums[left]和nums[right]。若nums[j-2] = nums[j-1] = nums[j] ,那么nums[i]和这三个值会产生重复的结果,所以此处进行判断,若产生重复则直接进入下一个循环。如果可以,返回 true;

m0_51794588的博客 895

代码随想录算法训练营Day02|997.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵Ⅱ

997.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵Ⅱ

weixin_50956954的博客 421

算法训练营day2

这个算法利用了双指针的思想,因为输入数组是有序的,最大的平方数要么是最左边元素的平方,要么是最右边元素的平方。因此,从结果数组的末尾开始填充元素,依次比较两个指针指向元素的绝对值大小,将较大的元素的平方放入结果数组的末尾,可以保证结果数组也是有序的。这种方法使用滑动窗口(双指针)的思想,通过不断调整左右指针来改变窗口的大小,同时计算窗口内元素的和,从而找到满足条件的最小子数组。这种实现方式通过不断更新边界变量,按照螺旋顺序依次填充矩阵元素,最终实现了螺旋矩阵的生成。

czk0411的博客 206

代码随想录算法训练营Day8

【代码】day8代码随想录训练营。

lololo的博客 737

DAY30||491.非递减子序列 |46.全排列 |47.全排列Ⅱ

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。去重逻辑是1.本层重复元素不使用,纵向的树枝可以使用重复元素,但和path末尾相比要去掉非递增的元素。(这些在逻辑里去重,终止条件只要取所有节点即可)本题可以用set去重,(只记录本层重复使用的元素)。x写个上题(使用used去重)不一样的写法先。。优化可以用数组做哈希表。

2301_79865280的博客 1003

力扣(合并区间)

给定一组可能重叠或相邻的整数区间,合并所有重叠或端点相接的区间,返回不重叠的最小区间列表。例如输入 [[1,3],[2,6],[8,10],[15,18]],输出 [[1,6],[8,10],[15,18]]。核心是先按左端点升序排序,再单次线性扫描完成合并,避免暴力两两比较。

weixin_52811458的博客 290
上一篇: 算法记录 | Day1数组基础
下一篇: 算法记录 | Day3链表基础
qq_50844226
博客等级 码龄6年 0粉丝 · 3原创
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值