242 字 1 分钟阅读

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

思路

刚看到这一题的时候,脑子里首先想到的就是直接每个数平方一下在排下序就搞定了,但是进阶里面也说明了,如果要对每一个元素进行平方再进行排序,效率会很低,要寻找一种 O(n) 的解决方案。所以还是直接放弃这种方案吧。

双指针

如果从先平方再排序的思路来进行优化的话,影响排序是因为有负数的存在,如果有0的话那么0一定是平方最小,而如果没有0的话,而从左到右的最后一个负数或者第一个正数,一定是整个数组里面平方最小的数。那么可以借助归并排序的思路。从这个负数与非负数的边界位置向两边分开进行查找,再依次比较即可。

public int[] sortedSquares(int[] nums) {
    if (nums == null || nums.length == 0) {
        return nums;
    }
    int i = 0;
    // 获取第一个非负数的位置
    for (; i < nums.length; i++) {
        if (nums[i] >= 0) {
            break;
        }
    }
    // 最后一个负数的位置
    int left = i - 1;
    // 第一个非负数的位置
    int right = i;
    // merge结果数组
    int[] tmp = new int[nums.length];
    int index = 0;
    while (left >= 0 && right < nums.length) {
        if (nums[left] * nums[left] < nums[right] * nums[right]) {
            tmp[index++] = nums[left] * nums[left];
            left--;
        } else {
            tmp[index++] = nums[right] * nums[right];
            right++;
        }
    }
    while (left >= 0) {
        tmp[index++] = nums[left] * nums[left];
        left--;
    }
    while (right < nums.length) {
        tmp[index++] = nums[right] * nums[right];
        right++;
    }
    return tmp;
}

双指针优化

上面的方案需要从中间开始找,要先找到负数与非负数的边界,再从该边界向两边寻找。需要注意两边不能越界,而边界问题一直是编码中很烦人的问题。试用下反向思维,从中间开始找是从最小的开始,那我也可以从最大的开始,使用双指针从两边向中间找,两个指针相遇就停止,相对于上面的方法节省了很多代码量。

public int[] sortedSquares(int[] nums) {
    if (nums == null || nums.length == 0) {
        return nums;
    }
    int[] tmp = new int[nums.length];
    int left = 0;
    int right = nums.length - 1;
    int index = nums.length - 1;
    while(left <= right){
        if(nums[left] * nums[left] >= nums[right] * nums[right]){
            tmp[index--] = nums[left] * nums[left];
            left++;
        }else{
            tmp[index--] = nums[right] * nums[right];
            right--;
        }
    }
    return tmp;
}