前言
第三篇尝试,继续。
记录三【有序数组的平方】
力扣题目【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.理解题目
1 数字平方好做,但是加一个递增排序的过程。
2 尝试过程:先写一个功能实现,看是否满足时间复杂度为 O(n) 。
二、第一次尝试下笔
代码实现
先上代码:
class Solution {
public:
vector<int>& sortedSquares(vector<int>& nums) {
int temp = 0;
for(int old = 0;old < nums.size();old++){ //old用作原始nums遍历
nums[old] = nums[old]*nums[old];
for(int i=old;i > 0;i--){
if(nums[i-1] > nums[i]){ //找到应该插入的地方
temp = nums[i];
nums[i] = nums[i-1];
nums[i-1] = temp;
}else{
break;
}
}
}
return nums;
}
};
尝试思路
1 遍历数组,求出每个元素的平方;再对平方进行排序。就算是冒泡排序,时间复杂度是 O(n^2)。
2. 那我就求完平方,就排序出来。
3. 走的弯路:
-
刚学了双指针,我尝试用两个索引操作同一个数组,比如:
vector<int> sortedSquares(vector<int>& nums) { int new = 0;//用作输出数组的索引 for(int old = 0;oid < nums.size();old++){ //old用作原始nums遍历 nums[old] = nums[old]*nums[old]; for(int i=new;i > 0;i--){ if(nums[i-1] > nums[i]){ //找到应该插入的地方 temp = nums[i]; nums[i] = nums[i-1]; nums[i-1] = temp; }else{ break; } } new++; } }
-
对于内层for循环,我绕了很久。:
1. 双指针使用:默认一个快一个慢,总是错开的;并且是从同一端出发。new和old初始都是0,最后写出来new和old同步,只用一个old操作就可以。 2. if找到插入的地方,因为上一条的误区,所以在new=0时,nums[i-1]不合理,浪费了时间。
-
时间复杂度分析:
1. 内层for循环最坏情况:输入的nums最后一个元素是0,排序时需要往前移动n位。 2. 外层for循环需要遍历整个数组,元素数量是n。 3. 所以时间复杂度是O(n^2)。
- 怎么降低时间复杂度呢?
三、代码随想录学习
学习内容:
1.解法一:每个数平方之后,排个序。
实现:for循环数组,每个元素都平方替换;再用sort(nums.begin(),nums.end())排序,sort函数快速排序实现,时间复杂度是 O(n + nlogn)。
2. 解法二:使用双指针。
思路:发现输入数组是从小到大顺序排列,所以平方之后最大值肯定在两端,越往中间,平方值越小。
所以——很巧妙的把双指针放到数组的两端,逐步往中间靠拢。
实现:
vector<int> result;
int k = nums.size()-1; //倒着往前填充result数组
for(int i=0,j=nums.size()-1;i <= j){ //为什么是=,不能漏掉元素
if(nums[i]*nums[i] > nums[j]*nums[j]){
result[k] = nums[i]*nums[i] ; //把大的值放进去
i++; //j不需要移动
//k--; 发现else也需要k--,所以统一放一起。
}else{ //当nums[i]*nums[i] == nums[j]*nums[j]时,谁先放入result都可以。所以if-else足够。
result[k] = nums[j]*nums[j] ;
j--;
}
k--;
}
return result;//不要忘记返回值。
时间复杂度分析:i和j从两端向中间靠拢,所以for循环一个数组的大小n,所以结果是O(n)。
改进之处:
1 我写的时间复杂度高,需要改成解法二;
2. 考虑到使用双指针,但是没有用对地方。
3. 没有观察出平方最大值在数组的两端,所以双指针没有用对方法。
总结
- 双指针使用很灵活:可以从同一端出发,一个快一个慢,使用方法【删除元素】;可以从两端出发,根据条件判断分别移动指针,一个往中间走的快,一个往中间走的慢,步调不一致,使用参考【有序数组的平方】。
(欢迎指正,转载表明出处。)