数据结构与算法:数组相关力扣题:27.移除元素、977.有序数组的平方、209.长度最小的子数组、59. 螺旋矩阵 II

news/2025/3/29 1:43:35/

27.移除元素

思路1:遇到数值符合的元素,两两交换从而后移一位

因为要原地操作,所以一开始的思路就是遍历数组,遇到数值符合的元素,将其和后面的元素两两交换从而实现后移一位。

python">class Solution:def removeElement(self, nums: List[int], val: int) -> int:equal_nums = 0 # 数值等于val的元素个数for i in range(len(nums)-1):if nums[i] == val:tmp = nums[i]nums[i] = nums[i+1]nums[i+1] = tmpequal_nums += 1if nums[len(nums)-1] == val:equal_nums += 1return len(nums)-equal_nums

这个代码的问题就是,如果在数组中遇到重复的数值等于val的元素,这个两两交换且后移一位就会失效。

思路2:双指针,快指针遍历数组,慢指针指向非val元素结束的位置

所以想到一个快指针和一个慢指针。快指针遍历数组元素,当遇到不等于val的元素时,就将该元素复制到慢指针的位置,然后慢指针前进一步。这样最后慢指针的位置就是所有非val元素的长度,同时数组的前k个都是非val的。

python">class Solution:def removeElement(self, nums: List[int], val: int) -> int:fast = slow = 0# 快指针遍历数组元素,慢指针指向非val元素结束的位置。while fast < len(nums):if nums[fast] != val:nums[slow] = nums[fast]slow += 1fast += 1return slow

效率:0ms,击败100.00%

977. 有序数组的平方

思路1:O(nlogn):使用库函数sort

其实直接使用库函数效率也不低啊hhh

python">class Solution:def sortedSquares(self, nums: List[int]) -> List[int]:new_arr = []for num in nums:new_arr.append(num * num)new_arr.sort()return new_arr

效率:7ms,击败88.81%

因为sort()的时间复杂度是O(n log n),所以整体时间复杂度是 O(n) + O(n log n) = O(n log n)

思路2:O(n):双指针

因为原数组存在一个特性,就是本身就是非递减顺序排列的。
那么平方后的新数组的最大值一定存在在原数组的两端(要么最左端最小值,要么最左端最大值)。而从原数组两端使用双指针向中间遍历,则就是新数组的最大值逐渐递减遍历。所以我们倒序填充新数组。
所以代码如下:

python">class Solution:def sortedSquares(self, nums: List[int]) -> List[int]:n = len(nums)left = 0right = n-1new_arr = [0] * n# 倒序填充新数组for i in range(n-1, -1, -1):if abs(nums[left]) > abs(nums[right]):new_arr[i] = nums[left] * nums[left]left += 1else:new_arr[i] = nums[right] * nums[right]right -= 1return new_arr

效率:3ms,击败98.13%

209.长度最小的子数组

思路1:暴力解法

python">class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:"""暴力解法,外层i循环遍历数组的开始位置,内层j循环遍历结束位置。"""n = len(nums)min_length = n+1for i in range(n):for j in range(i,n):sums = sum(nums[i:j+1])if sums >= target:# 一旦遇到总和满足条件了,就说明当前位置最小长度的子数组找到了min_length =  min(min_length, j-i+1)break return min_length if min_length != (n+1) else 0

时间复杂度为O(n^2),会超出时间限制。

思路2:维护前缀和

我们首先考虑一下维护前缀和的思路。
因为当i固定时,j从i开始逐步增加,每次计算sum(nums[i:j+1]),这其实是可以用前缀和或者累加的方式来优化的。比如,内层循环可以维护一个当前的和,每次j增加的时候,加上nums[j],而不是每次都重新计算整个子数组的和。这样可以把内层循环的时间复杂度从O(n)降到O(1),虽然整体还是O(n^2)

python">class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:"""暴力解法,外层i循环遍历数组的开始位置,内层j循环遍历结束位置。"""n = len(nums)min_length = n+1for i in range(n):current_sum = 0for j in range(i,n):current_sum += nums[j]if current_sum >= target:# 一旦遇到总和满足条件了,就说明当前位置开始,最小长度的子数组找到了min_length =  min(min_length, j-i+1)break return min_length if min_length != (n+1) else 0

还是会超时,而这里又有两层循环,所以我们想到双指针来优化它。

思路3:双指针滑动窗口法

python">class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:n = len(nums)min_length = n+1left = 0current_sum = 0for right in range(n):current_sum += nums[right]  # 扩展右边界# 当总和满足条件时,尝试缩小左边界while current_sum >= target:min_length = min(min_length, right - left + 1)current_sum -= nums[left]left += 1  # 左边界右移return min_length if min_length != (n+1) else 0

为什么这里我们的for循环要遍历终止位置?而不是起始位置?
因为如果是起始位置的话,我们要找到答案,还是需要维护一个起始位置向后的遍历,那和暴力解法其实没有区别了。因为每个元素可能被访问不止两次

譬如对于nums[3],起始位置j=0时,终止位置i指针向右遍历时会访问到它一次。然后起始位置j=1时,终止位置i指针向右遍历时会访问到它第二次。起始位置j=2时,终止位置i指针向右遍历时会访问到它第三次。

但是如果是终止位置的话,每个元素最多被访问到两次。

譬如对于nums[3],第一次访问到他时是终止位置j向右拓展时j=3时。然后j就会停止不动了,左边界右移也不会访问到它。之后j=4以及j继续遍历时,因为左边界只会不断右移,所以也只可能访问它1次。

滑动窗口的维护:

  • 右指针 right:遍历数组,逐步扩展窗口的右边界,累加元素和。
  • 左指针 left:当窗口内元素和 current_sum >= target 时,尝试右移左边界,缩小窗口长度,同时更新最小长度。

时间复杂度 O(n):每个元素最多被访问两次(被 left 和 right 各处理一次)。

效率:11ms,击败76.45%

59. 螺旋矩阵 II

这道题最重要的知识点就是,每转一圈会少2行,所以对于n个数字,n为偶数的话会转n/2圈,n为奇数的话会转n/2圈并多出最后一个数字。

python">class Solution:def generateMatrix(self, n: int) -> List[List[int]]:matrix = [[0] * n for _ in range(n)]# 每转一圈会少2行。所以对于n个数字,n为偶数的话会转n/2圈,n为奇数的话会转n/2圈并多出最后一个数字。# 对每条边实行左闭右开,start_x和start_y代表每一圈左上角的起始位置start_x = start_y = 0offset = 1# offset控制每圈边界的缩小count = 1for _ in range(n//2):# 处理上边for j in range(start_y, n-offset):matrix[start_x][j] = countcount += 1# 处理右边for i in range(start_x, n-offset):matrix[i][n-offset] = countcount += 1# 处理下边for j in range(n-offset, start_y, -1):matrix[n-offset][j] = countcount += 1# 处理左边for i in range(n-offset, start_x, -1):matrix[i][start_y] = countcount +=1offset += 1start_x += 1start_y += 1if n%2:matrix[n//2][n//2] = countreturn matrix

效率:0ms,击败100.00%


http://www.ppmy.cn/news/1583328.html

相关文章

C语言入门教程100讲(4)输入输出

文章目录 1. 什么是输入输出&#xff1f;2. 标准输入输出函数2.1 printf 函数2.2 scanf 函数 3. 格式化占位符4. 示例代码代码解析&#xff1a;输出结果&#xff1a; 5. 常见问题问题 1&#xff1a;scanf 中的 & 是什么作用&#xff1f;问题 2&#xff1a;printf 和 scanf …

嵌入式Linux驱动开发基础知识(一)

嵌入式Linux驱动开发基础知识点精要 一、开发环境搭建 交叉编译工具链 安装&#xff1a;arm-linux-gnueabihf-gcc验证&#xff1a;arm-linux-gnueabihf-gcc -v 内核编译与设备树 编译内核&#xff1a;make Image dtbs设备树文件&#xff1a;.dts&#xff08;源码&#xff09; …

JVM 学习前置知识

JVM 学习前置知识 Java 开发环境层次结构解析 下图展示了 Java 开发环境的层级关系及其核心组件&#xff0c;从底层操作系统到上层开发工具&#xff0c;逐步构建完整的开发与运行环境&#xff1a; 1. 操作系统&#xff08;Windows, MacOS, Linux, Solaris&#xff09; 作用&…

在本地Windows机器加载大模型并生成内容

本篇演示在本地机器下载和加载大模型并获取AI产生的内容。简单起见&#xff0c;使用的大模型是Qwen2.5-0.5B-Instruct&#xff0c;整个模型的所有文件不到1G。 Qwen2.5-0.5B-Instruct 是阿里巴巴云 QWen 团队基于 Transformer 架构开发的轻量级指令调优语言模型&#xff0c;专…

无人设备遥控器之调度自动化技术篇

一、技术原理 信息采集与处理&#xff1a; 通过传感器、仪表等设备采集无人设备的各种数据&#xff0c;如位置、速度、状态等。 将采集到的数据传输到调度自动化系统中进行处理和分析&#xff0c;以获取设备的实时状态。 系统建模与优化&#xff1a; 调度自动化系统会根据…

Java后端API限流秘籍:高并发的防护伞与实战指南

目录导航 📜 🛡️ 为什么需要API限流?🧠 主流限流算法大解析👩‍💻 阿里巴巴的限流实践📏 四大黄金定律🤼 限流策略组合拳🏆 限流场景实战💻 技术实现方案🌟 最佳实践分享📈 结语与展望📚 推荐阅读 1. 🛡️ 为什么需要API限流? 在高并发环境中,未…

Mac上安装和配置adb学习总结

1、安装&#xff1a; 命令行安装 brew install android-platform-tools 2、adb 的工作原理 adb 提供对 Unix shell&#xff08;可用来在设备上运行各种命令&#xff09;的访问权限。它是一种客户端-服务器程序&#xff0c;包括以下三个组件&#xff1a; 客户端&#xff1a;用…

C# SerialPort 使用详解

总目录 前言 在工业控制、物联网、嵌入式开发等领域&#xff0c;串口通信&#xff08;Serial Port Communication&#xff09;是连接串行设备&#xff08;如条码扫描器、GPS接收器等&#xff09;与计算机的重要手段。C# 提供了内置的 SerialPort 类&#xff0c;简化了串口开发…