题目描述

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

题目链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array/

题目分析

思路一:直接排序

无论是先平方再排序还是先排序再平方对于最终结果无影响

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> ans;
        for (int num: nums) {
            ans.push_back(num * num);
        }
        sort(ans.begin(), ans.end());
        return ans;
    }
};

时间复杂度:O(n log n)

空间复杂度:O(log n)

思路二:从中间使用双指针

在第一个方法中,没有利用到[按非递减顺序排序的整数数组]这一特性,如何利用这一特性从而避免二次排序成为了节省时间的关键。在本题中可以使用双指针来分别遍历负数部分和正数部分来利用好输入数组的特性。

思路如下:

1:首先找到数组中正负的分界点的下标

2:根据分界点创建两个指针,一个指针指向分界点,另一个指针指向分界点的后一位。第一个指针负责向前遍历(即负责遍历负数部分),第二个指针负责向后遍历(即负责遍历正数部分)

3:通过if else条件判断,将平方的值依次放到答案数组中然后返回即可,详细代码如下

class Solution {
public:
    vector<int> sortedSquares(vector<int> &nums) {
        int n = nums.size();
        int negative = -1;
        for (int i = 0; i < n; ++i) {
            if (nums[i] < 0) {
                negative = i;
            } else {
                break;
            }
        }
        vector<int> ans;
        int i = negative, j = negative + 1;
        while (i >= 0 || j < n) {//当两边指针有一个没有指到边界到时候
            if (i < 0) {//当左边指针到达左边界时,计算并移动右边指针
                ans.push_back(nums[j] * nums[j]);
                ++j;
            } else if (j == n) {//右边指针到达右边界时,计算并移动左边指针
                ans.push_back(nums[i] * nums[i]);
                --i;
            } else if (nums[i] * nums[i] < nums[j] * nums[j]) {//当左指针的平方小于右指针的平方时
                // 计算并添加左指针的数的平方,然后将左指针向左移动
                ans.push_back(nums[i] * nums[i]);
                --i;
            } else {//当右指针的平方小于左指针的平方时
                // 计算并添加右指针的数的平方,然后将右指针向右移动
                ans.push_back(nums[j] * nums[j]);
                ++j;
            }
        }
        return ans;
    }
};

时间复杂度:O(n)

空间复杂度:O(1)

思路三:从两头使用双指针

在第二个思路中,我们需要先找到一个“分界点”然后才能开始使用双指针,那么可以不可以从两头开始呢?

答案是可以的:我们可以使用两个指针,分别从两头开始,直到两指针相遇

根据题意,可以创建一个大小与原数组相同的答案数组ans,计算并比较两指针所指向数字的平方,将其倒序填入到答案数组ans中,最终将答案数组返回即可。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n);
        for (int i = 0, j = n - 1, pos = n - 1; i <= j;) {//i和j分别指向原始数组的头和尾,pos指向答案数组的尾部
        //结束条件为两指针相遇
            if (nums[i] * nums[i] > nums[j] * nums[j]) {//找到相对大的值,将其放到答案数组末尾
                ans[pos] = nums[i] * nums[i];
                ++i;
            }
            else {
                ans[pos] = nums[j] * nums[j];
                --j;
            }
            --pos;//答案数组的指针从后向前移动
        }
        return ans;
    }
};

Q.E.D.