c语言算法第二天

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值