leetcode分类刷题:基于数组的双指针(二、有序数组的排序)

1、对于数组上的双指针问题,主要是指两个变量在数组上移动或遍历的问题,之前整理的“二分查找”系列的算法题目,就是双指针解决数组上的索引相向移动。
2、双指针算法的关键在于:初始化双指针值和双指针值分别更新的条件,保持对以上两点的清晰认知,就能准确掌握这类题目。
3、本次博客总结的“有序数组的排序”题目,该类题目 需要双指针初值总是分别指向最大或最小值处,以便通过O(N)的时间复杂度完成仅一次数组遍历,并达到获取新的有序数组的题目要求。

977. 有序数组的平方

给定有序数组,对数组进行平方后,获取 有序的新数组,要求O(N)的时间复杂度。对于这种题目,数组本身肯定要完成一次遍历的,一次遍历对应的就是O(N)的时间复杂度,那么一次遍历的同时如何获取到有序的新数组呢?那必然需要一定的技巧了,即结合数组有序的特性,使用双指针(双指针初值总是分别指向最大或最小值处)。

from typing import List
'''
977. 有序数组的平方
题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
    输入:nums = [-4,-1,0,3,10]
    输出:[0,1,9,16,100]
    解释:平方后,数组变为 [16,1,0,9,100]
    排序后,数组变为 [0,1,9,16,100]
题眼:排序数组+数字平方+排序新数组(有序总是和双指针联系在一起:双指针初值总是指向最大或最小值处)
思路:双指针:平方后的数组,最大值总是在左右两侧(双指针初始值),双指针初始化为数组首尾索引,哪个的平方大就更新哪个指针
'''


class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        pointer1, pointer2 = 0, len(nums) - 1  # 双指针初始化为数组首尾索引
        result, idx = [0] * len(nums), len(nums) - 1
        while pointer1 <= pointer2:  # 也可以用idx>=0的条件
            if nums[pointer1] * nums[pointer1] >= nums[pointer2] * nums[pointer2]:
                result[idx] = nums[pointer1] * nums[pointer1]
                pointer1 += 1  # 更新指针1
            else:
                result[idx] = nums[pointer2] * nums[pointer2]
                pointer2 -= 1  # 更新指针2
            idx -= 1
        return result


if __name__ == '__main__':
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(n) for n in in_line[1].split(']')[0].split('[')[1].split(',')]
            print(obj.sortedSquares(nums))
        except EOFError:
            break

360. 有序转化数组

“977. 有序数组的平方”的变体,要求二次函数变换之后的新的排序数组,需要分情况讨论,核心思想还是 结合数组有序的特性,使用双指针(双指针初值总是分别指向最大或最小值处)。

from typing import List
'''
360. 有序转化数组(Vip:答案未验证,题目是从其它博客那里copy的)
题目描述:给你一个已经 排好序 的整数数组 nums 和整数 a、b、c。对于数组中的每一个数 x,计算函数值 f(x) = ax^2 + bx + c,请将函数值产生的数组返回。
要注意,返回的这个数组必须按照 升序排列,并且我们所期望的解法时间复杂度为 O(n)。
示例 1:
    示例 1:
    输入: nums = [-4,-2,2,4], a = 1, b = 3, c = 5
    输出: [3,9,15,33]
    示例 2:
    输入: nums = [-4,-2,2,4], a = -1, b = 3, c = 5
    输出: [-23,-5,1,7]
    题眼:排序数组+数字平方+排序新数组(有序总是和双指针联系在一起:双指针初值总是指向最大或最小值处)
思路:“977. 有序数组的平方”的变体,双指针:a不为0时是二次函数,a大于0时最大值总是在左右两侧(双指针初始值),a小于0时最小值总是在左右两侧(双指针初始值);
a为0时,b大于0是单调递增函数,b小于0是单调递减函数,b也等于0是常函数(很数学的一道题目)
'''


class Solution:
    def sortTransformedArray(self, nums: List[int], a, b, c) -> List[int]:
        result = [0] * len(nums)
        # 情况1:a为0
        if a == 0:
            if b == 0:
                return [c] * len(nums)
            elif b > 0:  # 单调递增函数
                for i in range(len(nums)):
                    result[i] = b * nums[i] + c
            elif b < 0:  # 单调递减函数
                for i in range(len(nums)-1, -1, -1):
                    result[i] = b * nums[i] + c
        # 情况2:a大于0,最大值总是在左右两侧(双指针初始值)
        elif a > 0:
            pointer1, pointer2 = 0, len(nums) - 1  # 双指针初始化为数组首尾索引
            idx = len(nums) - 1
            while pointer1 <= pointer2:  # 也可以用idx>=0的条件
                if self.func(nums[pointer1], a, b, c) >= self.func(nums[pointer2], a, b, c):
                    result[idx] = self.func(nums[pointer1], a, b, c)
                    pointer1 += 1  # 更新指针1
                else:
                    result[idx] = self.func(nums[pointer2], a, b, c)
                    pointer2 -= 1  # 更新指针2
                idx -= 1
        # 情况3:a小于0,最小值总是在左右两侧(双指针初始值)
        elif a < 0:
            pointer1, pointer2 = 0, len(nums) - 1  # 双指针初始化为数组首尾索引
            idx = 0
            while pointer1 <= pointer2:  # 也可以用idx<len(nums)的条件
                if self.func(nums[pointer1], a, b, c) <= self.func(nums[pointer2], a, b, c):
                    result[idx] = self.func(nums[pointer1], a, b, c)
                    pointer1 += 1  # 更新指针1
                else:
                    result[idx] = self.func(nums[pointer2], a, b, c)
                    pointer2 -= 1  # 更新指针2
                idx += 1
        return result

    def func(self, val, a, b, c):
        return a * val * val + b * val + c


if __name__ == '__main__':
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(n) for n in in_line[1].split(']')[0].split('[')[1].split(',')]
            a = int(in_line[2].strip().split(',')[0])
            b = int(in_line[3].strip().split(',')[0])
            c = int(in_line[4])
            print(obj.sortTransformedArray(nums, a, b, c))
        except EOFError:
            break

88. 合并两个有序数组

“977. 有序数组的平方”的变体,双指针:最大值总是在两数组末尾(双指针初始值),双指针初始化为两个数组的尾索引,哪个的大就更新哪个指针;不过赋值是要在nums1中本地完成,而不是在一个新的数组空间中。

from typing import List
'''
88. 合并两个有序数组
题目描述:给你两个按 非递减顺序 排列的整数数组nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
    输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
    输出:[1,2,2,3,5,6]
    解释:需要合并 [1,2,3] 和 [2,5,6] 。
    合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
题眼:两个排序数组+排序新数组(有序总是和双指针联系在一起:双指针初值总是指向最大或最小值处)
思路:“977. 有序数组的平方”的变体,双指针:最大值总是在两数组末尾(双指针初始值),双指针初始化为两个数组的尾索引,哪个的大就更新哪个指针;不过赋值
是要在nums1中本地完成。
'''


class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        # 情况1:nums2为空
        if not nums2:
            return
        # 情况2:nums1为空nums2不为空也能处理!
        pointer1, pointer2 = m - 1, n - 1  # 双指针初始化为两个数组的“有效”尾索引
        idx = m + n - 1
        while pointer1 >= 0 or pointer2 >= 0:  # 有一个数组没访问完就继续循环;也可以用idx>=0的条件
            if pointer1 < 0:  # nums1元素访问完
                nums1[idx] = nums2[pointer2]
                pointer2 -= 1
            elif pointer2 < 0:  # nums2元素访问完
                nums1[idx] = nums1[pointer1]
                pointer1 -= 1
            elif nums1[pointer1] >= nums2[pointer2]:  # 两个数组都没访问完:nums1元素大
                nums1[idx] = nums1[pointer1]
                pointer1 -= 1
            else:  # 两个数组都没访问完:nums2元素大
                nums1[idx] = nums2[pointer2]
                pointer2 -= 1
            idx -= 1


if __name__ == '__main__':
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums1 = [int(n) for n in in_line[1].split(']')[0].split('[')[1].split(',')]
            m = int(in_line[2].strip().split(',')[0])
            nums2 = [int(n) for n in in_line[3].split(']')[0].split('[')[1].split(',')]
            n = int(in_line[4].strip())
            obj.merge(nums1, m, nums2, n)
            print(nums1)
        except EOFError:
            break

个人总结体会

1、对于以上题目,给定的数组本身肯定要完成一次遍历的,一次遍历对应的就是O(N)的时间复杂度
2、一次遍历的同时获取到变换后的有序的新数组 需要 双指针访问这种技巧:双指针初值总是指向最大或最小值处,就是题目的解题套路。
3、通过和leetcode分类刷题:基于数组的双指针(一、基于元素移除的类型)对比,可以发现两者在使用双指针的不同motivation,有助于我们在更进阶的双指针题目上有更深的算法思想理解,而不是纯记忆结题套路与模板。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

22世纪冲刺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值