977. 有序数组的平方

217 阅读2分钟

[977. 有序数组的平方]

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

题目描述

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

思路

这道题其实有一个很明显的写法,就是先将数组进行平方,变成一个新的数组,我们称此为平方数组,之后再调用一般的排序函数,就可以实现我们的目的。但是这不是最好的做法,我们观察到,平方数组有一个特点,要么单调递增(1,2,3,4),要么单调递减(-4,-3,-2,-1),要么先递减,再递增(-4,-2,1,3,5)。不难发现最大值的平方会出现再数组的两端。那么我们就可以又用到我们的双指针了。

代码实现

使用双指针,一个指向数组头部,一个指向数组尾部。将他们所指的元素进行比较。将其中的大数写入我们的结果数组中,并将指针进行对应的移动,直到两个指针重合,完成整个数组的排序。这里我们有一个答案数组,能不能实现在原地进行排序呢?

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;--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;
            }
        }
        return ans;
    }
};

上面的做法,两个指针都需要移动,有没有什么办法,只移动一个呢?答案是有,我们在需要的时候,不妨交互两个指针的元素,使数组末尾的指针始终指向的数是大数,这样,数组头部的指针就不需要移动了。数组末尾的指针始终指向的数是大数,可以直接在原地进行计算,也就不需要结果数组了。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n=nums.size()-1;    //末尾指针
        int temp;
        
        while(n>=0){
            int min=nums[0]*nums[0];      //被固定的头部指针,其值为指针已经扫过区域的最小值
            int cur=nums[n]*nums[n];
            if(min>=cur)
                swap(nums[0],nums[n]);    //需要交换
            nums[n]*=nums[n];             //原地修改
            n--;                          //移动末尾指针
        }
        return nums;

    }
};

总结

上述内容使用双指针的算法思想,使用首尾指针,通过首尾两个指针不断的移动,在一个for循环下完成两个for循环的工作,大大优化了时间复杂度,并且进行了进一步优化,将双指针中的一个固定,可以看作一次遍历,即单指针的做法。