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] <= 104
nums 已按 非递减顺序 排序
进阶:
请你设计时间复杂度为 O(n) 的算法解决本问题
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码(算平方再排序)
暴力解决,先算出平方再排序
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* sortedSquares(int* nums, int numsSize, int* returnSize){
int loge=1,i,j,t;
for(i=0;i<numsSize;i++)
{
nums[i]=nums[i]*nums[i];
}
while(loge!=0)
{
loge=0;
for(j=0;j<numsSize-1;j++)
{
if(nums[j]>nums[j+1])
{
t=nums[j];
nums[j]=nums[j+1];
nums[j+1]=t;
loge=1;
}
}
}
*returnSize=numsSize;
return nums;
}
代码(双指针)
使用双指针
因为数组是有序的,所以最大的值只能是在数组的两端
我们只需要将最左端的数的平方与最右端的数的平方比较
将大的数逆序放入新的数组,
如果放的是最左端的,就将左端的指针(也就是下标)加一
如果是最右端的,就将右端的指针减一
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* sortedSquares(int* nums, int numsSize, int* returnSize){
int i=0,j=numsSize-1,k=j;
int *nums2=malloc(sizeof(int)*numsSize);//开一个新的数组,i,j分别为左右指针
while(i<=j)
{
if(nums[i]*nums[i]<nums[j]*nums[j])//左边小于右边,右边放入,下标减一
{
nums2[k]=nums[j]*nums[j];
j--;
}
else
{
nums2[k]=nums[i]*nums[i];
i++;
}
k--;
}
*returnSize=numsSize;
return nums2;
}
189.旋转数组
189. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
这题是中等难度,估计就是因为要三种解决办法,还有空间复杂度为o(1)
第一种(用新数组解决)
用一个新的数组将每个数放至正确的位置,设数组长度为n,移动k次。
遍历原数组,将原数组下标为 i 的数放在新数组下标为(i+k)%n的位置。
再将新的数组复制过去就好了
(i+k)%n:当i+k是小于n的时候,我们存放的位置就是i+k,如果大于n我们就存在i+k-n位置上。
void rotate(int* nums, int numsSize, int k){
int i=0,t,j=numsSize-1;
int a[numsSize];
for(i=0;i<j+1;i++)
{
a[(k+i)%numsSize]=nums[i];
}
for(i=0;i<numsSize;i++)
{
nums[i]=a[i];
}
}
该解法时间空间复杂度都为o(n)
第二种(用数组翻转)【简单且高效】
设置一个函数,让数组里面的i到k个元素进行翻转(倒过来)
如【1,2,3,4,5】->【5,4,3,2,1】
实现方法:只需要循环交换左右两边的数即可
void rotate(int* nums, int numsSize, int k){
int i=0,j=numsSize-1;
k=k%numsSize;
rotate2(nums,i,j);
rotate2(nums,i,k-1);
rotate2(nums,k,j);
}
void swap(int *a,int *b)
{
int t=*a;
*a=*b;
*b=t;
}
void rotate2(int *nums,int star,int end)//翻转函数
{
while(star<end)
{
swap(nums+star,nums+end);
star++;
end--;
}
}
具体实现过程
例:数组为【1,2,3,4,5】,k=2;
第一遍翻转:【1,2,3,4,5】->【5,4,3,2,1】
第二遍翻转:【5,4,3,2,1】->【4,5,3,2,1】
第三遍翻转:【4,5,3,2,1】->【4,5,1,2,3】
时间复杂度:O(n)
空间复杂度:O(1)
第三种(一次性移动k个)
如果是一次性一个一个的移动,然后用循环来确定移动的次数,这是一个二重循环
如果一次性移动k个,只需在一重循环里面进行
具体思路,第一次将数组的第一个数移动到正确的位置,然后将第(k的倍数)个数移到正确的位置,然后通过求(pos+k)% len的余数将剩下的数移到正确的位置。(pos是覆盖时,数组的下标)
在移动过程中,会覆盖掉原来的值,所以我们用temp来保存被覆盖的数,per保存temp用来下次循环覆盖下一个数。
在这个过程中,如果(pos+k)%len等于0,那么就和初始化时一样,做得就会变成第一次循环时做的事,就死循环了,所以这个时候pos需要加一,然后一直循环
到(pos+k)%len为1的时候又和pos为一的时候的事情一样了,又需要加一。。。。
void rotate(int* nums, int numsSize, int k)
{
int i=0,per=nums[0],pos=0,temp=nums[0],n=numsSize;//初始化
if(k%numsSize==0)
return;
while(n!=0)
{
pos=(pos+k)%numsSize;//pos为下标
temp=nums[pos];//保存被覆盖的值
nums[pos]=per;//per覆盖nums【pos】
per=temp;//将这一轮保留的值用于下一轮覆盖
if(pos==i)//避免死循环
{
i++;
pos++;
per=nums[pos];
temp=per;
}
n--;
}
}
时间复杂度o(n)
空间o(1)