力扣977.有序数组的平方(977.有序数组的平方)
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) 的算法解决本问题
2. 写前思路
- 非递减顺序意思是不是递减的,可能递增,可能无序,看例子应该是递增的意思
- 要求时间复杂度为O(n),即只能遍历一遍数组,一旦使用排序算法,基本上都会变成O(nlogn),所以不能使用排序算法。
- 由于计算的是数字的平方,所以负数会变成正数,那么最终最小的数应该是数组中间位置的某个数。是否可以考虑0?但是不能这么猜,不过如果找到某个分界点,左边是负数,右边是正数,似乎可以。
- 思路萎靡,寻求gpt,提示双指针,left和right,遂思路打开。遍历原数组是O(n),遍历结果数组也是O(n) 。既然不知道最小的,但是可以通过数组第一个和最后一个的结果得到最大的,依次类推,可以倒序填充结果数组。即从两头开始挪动指针,依次获取最大值,倒着插入结果数组。
3. 实现代码
分析:时间复杂度为O(n),空间复杂度为O(n)
时间复杂度为O(n):是因为只将数组循环了一次,即最耗时的操作花了O(n)。
空间复杂度为O(n):left,right指针占了2个基本变量的空间,即O(1),结果数组占用了n个基本变量的空间,即O(n),综合来看为O(n)
public class Solution977 {
public static int[] sortedSquares(int[] nums) {
int[] result = new int[nums.length];
//双指针
int left = 0;
int right = nums.length -1;
//倒序填充result
for (int i = nums.length-1;i>-1;i--){
int leftNumber = nums[left] * nums[left];
int rightNumber = nums[right] * nums[right];
if (leftNumber>rightNumber){
result[i] = leftNumber;
left = left +1;
}else{
result[i] = rightNumber;
right = right -1;
}
}
return result;
}
public static void main(String[] args) {
int[] nums = {-4,-1,0,3,10};
int[] ints = sortedSquares(nums);
for (int anInt : ints) {
System.out.println(anInt);
}
}
}
4. 运行结果
5. 官方题解分析(leetcode.cn/problems/sq…)
- 参考某条评论:我理解的非递减的意思是数组中有可能存在相同的值,然后这两个值不能称为是递减关系。大概可以解释非递减的意思。
- 官方给了三个答案,第一个思路即一开始设想的计算结果后排序,显然时间复杂度为O(nlogn),不符合结果;第二个思路类似于一开始所想的找最小值,将负数正数分为两组,依次向两边移动指针,符合时间复杂度O(n),但是需要考虑左边界和右边界,比较麻烦。第三个思路和实现思路一样,从两边往中间移动指针,依次填充最大值,符合时间复杂度O(n),由于是两边往中间移动,所以无需考虑边界问题,最后一次循环时,left=right,取谁的值无所谓。但是官方提供的空间复杂度为O(1),应该是没有包含结果的存储空间(翻看下面的回复后发现确实如此)。
6. 感悟
算法确实是考验思维的活跃度,即脑子灵不灵活,这题挺典型了,并没有涉及复杂的数据结构,但是考虑从小到大排序时,一般的思路都是从最小的排起,从而陷入了思维误区。如果能换个方向思考,从小到大排序,等于将数组倒着从大到小排序,问题就迎刃而解了。下次遇到同类型的题应该发挥思维,从不同角度考虑下。