977.有序数组的平方(py)

双指针法思想:27.移除元素(py)-CSDN博客

题目

        给你一个按 非递减顺序  排序的整数数组 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]

提示:

        1 <= nums.length <= 104

        -104 <= nums[i] <= 104

        nums 已按 非递减顺序 排序

进阶:

        请你设计时间复杂度为 O(n) 的算法解决本问题

题解

        暴力排序:每个数平方之后,排个序。

        双指针法:数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

        此时可以考虑双指针法了,i指向起始位置,j指向终止位置。

        定义一个新数组result,和A数组一样的长度,让k指向result数组终止位置(倒序填充)。

        如果A[i] * A[i] < A[j] * A[j] 那么result[k--] = A[j] * A[j];

        如果A[i] * A[i] >= A[j] * A[j] 那么result[k--] = A[i] * A[i]; 。

Pycharm

#(版本一)双指针法
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l, r, i = 0, len(nums)-1, len(nums)-1
        res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果
        while l <= r:
            if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值
                res[i] = nums[r] ** 2
                r -= 1 # 右指针往左移动
            else:
                res[i] = nums[l] ** 2
                l += 1 # 左指针往右移动
            i -= 1 # 存放结果的指针需要往前平移一位
        return res
#时间复杂度:O (n) 空间复杂度:O (n)

注:res = [float('inf')] * len(nums) 这一行的作用是 预先初始化一个长度与输入数组相同的列表,并用无穷大(float('inf'))填充。这是因为双指针法需要从后向前填充结果数组,而 Python 中列表的长度是固定的,无法直接通过索引赋值(如 res[i] = ...)来添加元素,除非列表已经有足够的长度。 更常见的写法:

res = [None] * len(nums)  # 使用 None 作为占位符

详细解释:
        ①为什么需要初始化列表?
        Python 的列表不像数组那样有固定长度,直接通过索引赋值(如 res[3] = 10)会引发 IndexError,除非列表已经至少有 4 个元素。因此,需要先创建一个足够长的列表
        ②为什么用 float('inf') 填充?
        这里填充的值实际上无关紧要,因为后续双指针遍历时会覆盖每个位置的值。使用无穷大只是一种占位符,强调这些值会被替换。实际上,使用 None、0 或其他任意值都可以。

#(版本二)暴力排序法
from typing import List
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        for i in range(len(nums)):
            nums[i] *= nums[i]
        nums.sort()
        return nums
#时间复杂度:O (n log n) 空间复杂度:O (n)

注:在 Python 中,nums.sort() 是列表(list)对象的一个原地排序方法,用于将列表中的元素按照升序排列。 

功能解释:

        ①直接修改原列表
        nums.sort() 会直接修改原列表 nums 的元素顺序,使其变为有序,不会返回新列表(返回值为 None)。

        ②默认升序排列
        对于数字,从小到大排序;对于字符串,按字典序排序。

        ③可选参数

        reverse=True:降序排列。reverse=False:升序排列。

        key=函数:自定义排序规则(例如按绝对值排序)。

        nums = [5, 3, 1, 4, 2]

        nums.sort(reverse=True)  

        print(nums)  # 输出: [5, 4, 3, 2, 1]

#(版本三)暴力排序法+列表推导法
from typing import List
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted(x*x for x in nums)
#(双指针优化版本) 三步优化
 class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        """
        整体思想:有序数组的绝对值最大值永远在两头,比较两头,平方大的插到新数组的最后
        优   化:1. 优化所有元素为非正或非负的情况
                2. 头尾平方的大小比较直接将头尾相加与0进行比较即可
                3. 新的平方排序数组的插入索引可以用倒序插入实现(针对for循环,while循环不适用)
        """
 
        # 特殊情况, 元素都非负(优化1)
        if nums[0] >= 0:
            return [num ** 2 for num in nums]  # 按顺序平方即可
        # 最后一个非正,全负有序的
        if nums[-1] <= 0:
            return [x ** 2 for x in nums[::-1]]  # 倒序平方后的数组
        
        # 一般情况, 有正有负
        i = 0  # 原数组头索引
        j = len(nums) - 1  # 原数组尾部索引
        new_nums = [0] * len(nums)  # 新建一个等长数组用于保存排序后的结果
        # end_index = len(nums) - 1  # 新的排序数组(是新数组)尾插索引, 每次需要减一(优化3优化了)

        for end_index in range(len(nums)-1, -1, -1): # (优化3,倒序,不用单独创建变量)
            # if nums[i] ** 2 >= nums[j] ** 2:
            if nums[i] + nums[j] <= 0:  # (优化2)
                new_nums[end_index] = nums[i] ** 2
                i += 1
                # end_index -= 1  (优化3)
            else:
                new_nums[end_index] = nums[j] ** 2
                j -= 1
                # end_index -= 1  (优化3)
        return new_nums

运行结果

进程已结束,退出代码为 0

LeetCode

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        a,b,c=0,len(nums)-1,len(nums)-1
        res=[None]*len(nums)
        while a<=c:
            if nums[a]**2<nums[c]**2:
                res[b]=nums[c]**2
                c-=1
            else:
                res[b]=nums[a]**2
                a+=1
            b-=1
        return res

链接

977. 有序数组的平方 - 力扣(LeetCode)

双指针法经典题目 | LeetCode:977.有序数组的平
方_哔哩哔哩_bilibili

时间:2025.6.9

不爱学习的大学生

<think>好的,我现在需要帮助用户解决如何用Python有序数组中移除重复项的问题。首先,我需要回忆一下相关的算法和方法。根据用户提供的引用,特别是引用[3]中的代码示例,看起来这是一个经典的“双指针”问题。用户提到的数组是非严格递增的,也就是可能存在重复元素,但整体是递增的。 首先,我需要确认问题要求:原地删除重复项,返回新长度,并且保证元素的相对顺序一致。这意味着不能使用额外的数组空间,只能在原数组上修改,同时后面的元素可以不管,只要前k个元素是不重复的。 接下来,我需要思考双指针法的具体实现。通常,快指针遍历数组,慢指针指向下一个唯一元素的位置。当快指针发现当前元素与前一个不同时,就将该元素复制到慢指针的位置,然后慢指针前移。这种方法的时间复杂度是O(n),空间复杂度是O(1),符合题目要求。 然后,我需要检查用户提供的引用内容。引用[3]中的代码确实使用了双指针方法,其中k从1开始,遍历数组时比较当前元素和前一个元素是否相同,不同的话就赋值到k的位置,并递增k。这应该是正确的实现方式。 另外,用户还提到引用[5]中的方法,即使用列表的remove方法,但这种方法可能效率不高,因为每次remove操作都需要移动后面的元素,导致时间复杂度为O(),不适合处理大数据量。因此,双指针法是更优的选择。 还需要考虑边界情况,比如空数组的情况,这时候应该直接返回0。在代码中需要先处理这种情况。比如,在引用[3]的代码中,首先检查if not nums,返回0,这是正确的。 现在,我需要将这个过程用中文清晰地解释出来,包括步骤分解和代码示例。同时,根据用户的要求,生成相关问题,比如时间复杂度、优化方法、其他数据结构的应用等。 最后,确保所有数学表达式和公式使用正确的格式,比如变量使用$...$,如$O(n)$,而独立公式如时间复杂度公式可能需要使用$$...$$,但在这个问题中可能用不上,但如果有的话需要正确应用。 总结一下,我的回答应包括: 1. 算法原理:双指针法的工作原理。 2. 步骤分解:详细说明每个步骤的作用。 3. 代码示例:引用[3]中的代码,并添加注释。 4. 复杂度分析:时间和空间复杂度。 5. 注意事项:如原地操作、边界情况处理。 6. 相关问题:生成与题目相关的扩展问题。 现在需要确保回答结构清晰,符合用户的要求,同时引用正确的参考文献,比如引用[3]中的代码,引用[4]中提到的原地修改数组的方法等。</think>### 如何用Python有序数组中移除重复元素? #### 算法原理 对于非严格递增的有序数组,可以通过**双指针法**高效移除重复元素。核心思想是: 1. **快指针**遍历数组,检测重复元素 2. **慢指针**标记唯一元素的存储位置 时间复杂度为$O(n)$,空间复杂度为$O(1)$[^3][^4] #### 实现步骤分解 1. **边界处理**:空数组直接返回0 2. **初始化指针**:慢指针从索引1开始(首个元素必定唯一) 3. **遍历数组**:比较当前元素与前一个元素 4. **唯一性判断**:若不同则将元素存入慢指针位置,慢指针前移 #### 代码实现 ```python def removeDuplicates(nums): if not nums: return 0 k = 1 # 唯一元素存放位置指针 for i in range(1, len(nums)): if nums[i] != nums[i-1]: # 发现新唯一元素 nums[k] = nums[i] # 覆盖存储 k += 1 return k ``` *注:该实现满足原地修改要求,返回唯一元素数量* #### 复杂度分析 | 指标 | 数值 | 说明 | |------------|--------|--------------------------| | 时间复杂度 | $O(n)$ | 单次遍历完成去重 | | 空间复杂度 | $O(1)$ | 仅使用两个指针变量[^4] | #### 注意事项 1. 必须保证数组是**非严格递增**的,否则算法失效 2. 数组修改是**原地覆盖**,原始数据会被部分保留在前k位 3. 不能使用`remove()`方法,其时间复杂度为$O(n^2)$[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值