力扣 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] <= 104nums已按非递减顺序 排序
我的解法
看到题目要实现的两个功能,一平方二排序,首先想到的是暴力解法,与后面解法一逻辑一致,但由于是独立完成且出现一些值得注意的问题,所以放在了这里。
✅首先写的是平方功能:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i=0;i<nums.size();i++){
nums[i] = nums[i]*nums[i]; //第一次用了 ==
}
return nums; //第一次返回了 nums[i]
}
};
虽然简单,但还是出现两个问题:
- 平方赋值时将赋值运算符
=写成相等比较运算符== - 循环结束后返回值写成
return nums[i],显然函数返回类型是数组,故应返回nums且变量i在循环结束后已经销毁,循环外再访问属于未定义行为。
❌带排序功能的错误代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i=0;i<nums.size();i++){
nums[i] = nums[i]*nums[i];
}
for(int k=0;k<nums.size()-1;k++){
for(int j=0;j<nums.size();j++){
if(nums[j+1]<nums[j]){
nums[j] = nums[j+1];
}
}
}
return nums;
}
};
原代码中存在三个主要问题:
- 冒泡排序代码中内层循环
j的范围出错,导致读取越界,超出了数组限制,读取到垃圾值。在冒泡排序中,可以这样理解两层循环,外层循环k控制排序需要几轮,数组有n个元素,故最多排n-1轮;而内层循环实现每一轮从左到右两两比较,每一轮比较到未排序位置便停止,故j的取值范围是j < n - 1 - k,这样就保证了永远不会出现越界,不会读取垃圾值。 - 运行时出现
int溢出报错,原因在于使用两数相乘完成平方时,由于原代码冒泡排序逻辑出错,导致垃圾值赋入,超出int范围。可通过先将数据类型转换为long long解决,但实际上按照原题所给范围并不会溢出,故修改冒泡排序后便不需此操作。 - 原代码中
nums[j] = nums[j+1];只是直接覆盖了原值,没有完成交换,同时将nums[j]丢失。应该使用swap进行交换。
✅下面是带排序功能的完整力扣代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i=0;i<nums.size();i++){
nums[i] = (long long)nums[i]*nums[i];
}
for(int k=0;k<nums.size()-1;k++){
for(int j=0;j<nums.size()-1-k;j++){
if(nums[j+1]<nums[j]){
swap(nums[j],nums[j+1]);
}
}
}
return nums;
}
};
解法一 暴力解法
✅此解法思路与上方一致,只是更为简洁通用:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0;i < nums.size();i++){
nums[i] *= nums[i];
}
sort (nums.begin(),nums.end()); //利用内置函数进行快速排序
return nums;
}
};
解法二 双指针法
✅双指针法完整代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int k = nums.size() - 1; //使 k 在新数组最后一位
vector<int> result(nums.size(),0); // 定义新数组存放排序后的平方值
for(int i = 0,j = nums.size()-1;i <= j;){ // 双指针 i 在起始,j 在末尾
if(nums[i]*nums[i] < nums[j]*nums[j]){
result[k--] = nums[j]*nums[j]; //利用后置自减实现 k 的前移
j--; // j 平方填入后就前移
}
else{
result[k--] = nums[i]*nums[i];
i++; // i 平方填入后就后移
}
}
return result;
}
};
双指针法也很好理解:
因为题目所给数组都是有序的,那么数组平方的最大值不是在最左边就是最右边,此时利用双指针法,i指向起始位置,j指向终止位置。定义一个新数组result,和nums数组一样的大小,让k指向result数组终止位置。
如果nums[i] * nums[i] < nums[j] * nums[j] 那么result[k--] = nums[j] * nums[j];
如果nums[i] * nums[i] >= nums[j] * nums[j]那么result[k--] = nums[i] * nums[i];