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]
来源:力扣(LeetCode)
2.题目分析
这是一道简单题,我们很容易就可以想出先遍历平方再调用sort
函数进行排序的算法,这个复杂度也是O(N),但是好像这还是暴力解法,所以我们尝试进行优化。对于这种有序数组,我们采用双指针可以很好的利用其规律。
采用双指针处理问题的话无非就是两个思路:
- 同向双指针(快慢指针)
- 反向双指针
在本题中采用双指针的话我们要考虑选择哪种,因为有序数组可能包含正数负数,所以前面计算的平方值反而可能更大,这导致其排序变乱,但是它有一点不变:对于当前数组来说,其最大平方值必然是处于数组两端节点元素中的一个,所以我们就考虑下述思路的双向双指针,其中为了避免频繁的移动数组我们选择创建一个新的容量相同的数组。
- 首先定义两个指针first和end,分别指向数组头尾,定义数组result;
- 我们分别对其首尾元素进行平方求值,此时将较大的平方求值存入数组result末尾;
- 当first<=end时我们循环第二步知道条件不满足。
- 其实上面说的比较简单,在具体使用双指针法解题时,如何确定两个指针的循环条件确实是一件令人头痛的事,一般来说我们的边界条件都是在
i<j
和i<=j
之中选的,二者在解题时都是可以选的,只是可能选择了某一种就要多一些处理步骤,如二分法中的left<right
和left<=right
。我们需要明白的是若是采用”<“
,那么就说明循环结束的最后一刻存在两个备选值,若是<=
则只有一个值要处理。在具体解题时我们可以将边界条件带入解题过程来看选择哪个比较好。本题中要对所有元素进行处理,我们选择first<=right
,首尾指针相遇时只剩最后一个元素,将其放置数组头部就可以。 - 另外需要注意的是,注意代码不能像下面这么写,在求值表达式中,如果修改了表达式中一个对象的值,那么最好不要在同一表达式中使用它,这会引发难以预料的结果,因为大多数情况下同一个表达式中求值顺序是不确定的(只有特定的运算符才会遵从从左到右)。
if(A[i]*A[i]<A[j]*A[j]) result[k--]=A[j]*A[j--];
else result[k--]=A[i]*A[i++];
要想使上面的的语句正确我们加个括号就可以了,这可以强制规定求值顺序也就是类似下面的写法:
if(A[i]*A[i]<A[j]*A[j]) result[k--]=(A[j])*A[j--];
else result[k--]=(A[i])*A[i++];
3.题目解答:
双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& A) {
int k=A.size()-1;
vector<int> result(k+1,0);
int i=0,j=k;
while(i<=j){
if(A[i]*A[i]<A[j]*A[j]){
result[k--]=A[j]*A[j];
j--;
}
else{
result[k--]=A[i]*A[i];
i++;
}
}
return result;
}
};
暴力求解
class Solution {
public:
vector<int> sortedSquares(vector<int>& A) {
for (int i = 0; i < A.size(); i++) {
A[i] *= A[i];
}
sort(A.begin(), A.end()); // 快速排序
return A;
}
};