LeetCode笔记——977.有序数组的平方

题目

977. 有序数组的平方
给你一个按 非递减顺序排序的整数数组 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) 的算法解决本问题

代码

//归并排序
class Solution {
    public int[] sortedSquares(int[] nums) {
       
        int num1Start = 0;
        int num1End = -1;

        for (int num : nums) {
            if (num < 0) {
                num1End++;
            }
        }
        int num2Start = num1End + 1;
        int num2End = nums.length - 1;
        int[] sortedRes = new int[nums.length];
        int m = num1End;
        int n = num2Start;
        int index = 0;
        while(m >= num1Start && n <= num2End) {
            if(Math.abs(nums[m]) < Math.abs(nums[n])) {
                sortedRes[index++] = nums[m] * nums[m--];
            } else {
                sortedRes[index++] = nums[n] * nums[n++];
            }
        }

        while (m >= num1Start) {
            sortedRes[index++] = nums[m] * nums[m--];
        }
        while (n <= num2End) {
            sortedRes[index++] = nums[n] * nums[n++];
        }
        return sortedRes;
    }
}

解析

当我们拿到这么一个数组[-4,-1,0,3,10]的时候,需要的是将每个数平方,按照非降序排列(其实就是非严格的递增序列)。

第一个反应就是按照每个数的绝对值排序,然后取平方,这样做是没问题的;

但是,题目中已经明确提出,这个数组本身就是非降序的,那么我们怎么利用这个条件呢?

这样一个数组很容易就会将它分成两个部分,一部分负数,一部分正数。而且两个部分都是有序的。

我们按照绝对值把它们混合在一起不好吗?

把两个数组合在一起,好像很熟悉——归并排序!

图片来源:图解排序算法(四)之归并排序

在这里插入图片描述

而且对这个题目来说不需要递归,因为两个序列都是排序后的,我们走一趟就能把他们合并到一起去了。

Hold on! 总感觉哪里怪怪的。[-4,-1,0,3,10]这个数组,分开后是[-4, -1][0, 3, 10],如果按照绝对值进行比较,那就是[4,1][0,3,10],一个是降序,一个是升序,这样是不行的。

那么把前面负数的部分逆序一下行吗?好像有点麻烦了。不如我们直接倒着遍历前面的负数部分😀

到现在我们基本清晰了:

  • 主体思路:利用归并排序,将正负数组通过比较绝对值大小进行合并,重新排序。
  • 需要解决的问题:1、找到正负数组的分界点;2、如何逆序遍历负数数组。

第一个问题,我们的解决方案是:

 int num1Start = 0;
 int num1End = -1;

 for (int num : nums) {
     if (num < 0) {
         num1End++;
     }
 }

第二个问题,通过寻找正负数组的分界点,我们能找到负数数组的开头(num1Start)和结尾(num1End)。遍历的时候我们从num1End开始遍历,每次递减直到num1Start就可以了。

//归并排序
class Solution {
    public int[] sortedSquares(int[] nums) {
       
        int num1Start = 0;
        int num1End = -1;

        for (int num : nums) {
            if (num < 0) {
                num1End++;
            }
        }
        int num2Start = num1End + 1;
        int num2End = nums.length - 1;
        int[] sortedRes = new int[nums.length];
        int m = num1End;
        int n = num2Start;
        int index = 0;
        while(m >= num1Start && n <= num2End) {
            if(Math.abs(nums[m]) < Math.abs(nums[n])) {
                sortedRes[index++] = nums[m] * nums[m--];
            } else {
                sortedRes[index++] = nums[n] * nums[n++];
            }
        }

        while (m >= num1Start) {
            sortedRes[index++] = nums[m] * nums[m--];
        }
        while (n <= num2End) {
            sortedRes[index++] = nums[n] * nums[n++];
        }
        return sortedRes;
    }
}

进阶

题目最后要求我们设计时间复杂度为 O(n) 的算法解决本问题

其实上面已经算是O(n)了,但是我们能否让代码更加简单点?

class Solution {
    public int[] sortedSquares(int[] nums) {
       int left = 0, right = nums.length - 1;
       int index = nums.length - 1;
       int[] res = new int[nums.length];
       while(index >= 0) {
           if(Math.abs(nums[left]) > Math.abs(nums[right])) {
               res[index--] = nums[left] * nums[left++];
           } else {
               res[index--] = nums[right] * nums[right--];
           }
       }
       return res;
    }
}

我们直接弄俩指针,一个left从头开始,一个right从尾开始,创建一个新的结果数组res,负数数组的绝对值是逆序排列的,正数数组的排列是正序的,所以相当于两个指针都是从大到小遍历对应的数组。left指针最多会停到第一个正数那里,接下来就会停止不动了。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值