Leetcode刷题——数组题02

Leetcode刷题——数组题02

题目来源:leetcode

题一:移除元素

题目解读:题目要求原地移除,因此空间复杂度为O(1),这里我们就可以灵活的运用双指针。

代码(C语言版):

int removeElement(int* nums, int numsSize, int val){
    int i=0,j=0;
    while(i<numsSize){
        if(nums[i]==val){
            i++;
        }else{
            nums[j]=nums[i];
            i++;
            j++;
        }
    }
    return j;
}

代码解读:两个指针都从数组开头开始移动,其中一个指针遍历整个数组记作i,另外一个指针记录的都是不等于val的数记作j,当指针i遇到与val相等的数时,指针i就继续前进,不相等则将指针i指向的值赋给指针j,同时,两个指针都前移,当指针i走完时,则遍历结束,此时,指针j之前的数就是所求数组元素。

题二: 删除有序数组中的重复项

题目解读:与上题相类似,同样是原地修改原数组,因此我们同样可以借助双指针。

代码(C语言版):

int removeDuplicates(int* nums, int numsSize){
    int i=0;
    if(numsSize<=1){
        return numsSize;
    }
    int j = 1;
    while(j<numsSize){
        if(nums[j]==nums[i]){
            j++;
        }else{
            i++;
            nums[i]=nums[j];
            j++;
        }
    }
    return i+1;
}

代码解读:双指针的初始位置相邻,分别位于数组的第一位和第二位,因此我们一开始还需要对数组进行特殊情况考虑,当数组中的元素小于或等于1时,就无需去重,直接返回。我们定义前一个指针为i,后一个指针为j,同样,指针i遍历整个数组,指针j,指向的都是数组中已经存在的数。比较两个指针对应的数,当两个数相等时,指针i就继续前进,不相等则将指针i指向的值赋给指针j,同时,两个指针都前移,指针j永远都指向的是符合要求的数中最大的且仅出现过一次的数,后面遍历的数都与之相比较,当指针i走完时,则遍历结束,此时,指针j之前的数就是所求数组元素。

题三:合并两个有序数组

题目解读:此题没有空间复杂度的要求,因此通常便可以想到新建一个数组空间,依次将两个数组中的数有序的输进新数组中,最后拷贝到原数组中返回。

代码(C语言版):

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    //生成一个空的数组空间
    int* arr = (int*)malloc(sizeof(int)*(m+n));
    int i=0,j=0,k=0;
    while(i<m&&j<n){
        //将较小的放入新数组
        arr[k++]= nums1[i]<=nums2[j] ? nums1[i++] : nums2[j++];
    }
    while(i<m){
        arr[k++]= nums1[i++];
    }
    while(j<n){
        arr[k++]= nums2[j++];
    }
    //复制
    memcpy(nums1,arr,sizeof(int)*(m+n));
}

代码解读:采用三个指针,分别指向三个数组的开头位置,依次将两个数组中较小的数放进新数组中,当其中一个数组遍历完,另一个可能还没有结束,此时将没有遍历完的数依次放进新数组中去。最后拷贝到原数组。

优化

我们可以考虑不使用额外的空间,即空间复杂度为O(1),题目中有说nums1数组的初始长度为m+n,很明显就能猜出出题者的意图,那就是考查 原地修改 ,将空间复杂度降低到O(1)。因为这样不需要使用额外的数组空间了,我们完全可以把 nums2也放入 nums1中。原地修改时,为了避免从前往后遍历导致原有数组元素被破坏掉,我们要选择从后往前遍历!所以,我们总共需要创建三个指针,两个指针用于指向 nums1nums2的初始化元素数量的末位,也就是分别指向 m−1n−1 的位置,还有一个指针,我们指向 nums1数组末位即可。既然都知道了,那还等什么,开整!

代码(C语言版):

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    //逆向双指针
    int i=m-1,j=n-1,k=m+n-1;
    while(i>=0&&j>=0){
        nums1[k--] = nums1[i]>=nums2[j] ? nums1[i--] : nums2[j--];
    }
    while(i>=0){
        nums1[k--] = nums1[i--];
    }
    while(j>=0){
        nums1[k--] = nums2[j--];
    }
}

图解演示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNFoKdk4-1634280964214)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20211015132750775.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JNCH20fA-1634280964220)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20211015133446413.png)]

题四:旋转数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehWVLAgi-1634280964226)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20211015133636748.png)]

题目解读:原题目没有空间复杂度的要求,因此我们可以使用额外的数组将每个数都放到正确的位置去。但它又有进阶的要求,空间复杂度为O(1),难度便提升了一些,这里我们巧妙的利用数组翻转的方法实现。该方法基于如下的事实:当我们将数组的元素向右移动 k次后,尾部 k个元素会移动至数组头部,其余元素向后移动 k个位置。

n=7,k=3时:

操作结果
原始数组1 2 3 4 5 6 7
翻转[0,3]区间的元素4 3 2 1 5 6 7
翻转[4,6]区间的元素4 3 2 1 7 6 5
翻转所有元素5 6 7 1 2 3 4

代码(C语言版):

void rotate(int* nums, int numsSize, int k){
     k = k%numsSize;
     rerver(nums,0,numsSize-k-1);
     rerver(nums,numsSize-k,numsSize-1);
     rerver(nums,0,numsSize-1);
}
void rerver(int* nums,int a,int b){
    while(a<b){
        int temp = nums[b];
        nums[b] = nums[a];
        nums[a] = temp;
        a++;
        b--;
    }
}

题五:数组形式的整数加法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBgSjeII-1634280964227)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20211015140325086.png)]

题目解读:该题我们采用逐位相加来思考,需要考虑到进位的记录,及最后数组的返回方式。

代码(C语言版):

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* addToArrayForm(int* num, int numSize, int k, int* returnSize){
    //求出整数k的位数
    int kNum = k,kSize = 0;
    while(kNum){
        kSize++;
        kNum/=10;
    }
    //生成新的数组空间,大小为位数较大的那个再加一,因为两数相加最多溢出一位
    int len = numSize > kSize ? numSize : kSize;
    int* arr = (int*)malloc(sizeof(int)*(len+1));

    //定义初始进位默认为0
    int addNum = 0;
    int aSize = 0;
    int i = numSize-1;
    while(len--){
        int a = 0;
        if(i>=0){
            a = num[i];
            i--;
        }
        int res = a + k%10 + addNum;
        k /=10;
        if(res > 9){
            res -= 10;
            addNum = 1;
        }else{
            addNum = 0;
        }
        arr[aSize++] = res;
    }
    if(addNum==1){
            arr[aSize++] = 1;
        }
    //逆置数组
    rerver(arr,0,aSize-1);
    *returnSize = aSize;
    return arr;
}
void rerver(int* nums,int a,int b){
    while(a<b){
        int temp = nums[b];
        nums[b] = nums[a];
        nums[a] = temp;
        a++;
        b--;
    }
}

代码解读:最终我们需要返回一个新的数组,因此我们便会考虑到新开辟的数组所占的空间大小,已知两个数相加最多比位数较多的那一位溢出一位,因此我们新开辟的数组长度要求比两个数位数较大的那一位多一位,数组的位数很容易就知道,整数的位数可以通过每次除10,通过可以除的次数判断该整数的位数。开辟好空间后,定义两个指针分别指向整数个位和数组的最后一位,整数通过每次除10后再与10取余得到每位的数,数组则可以通过指针前移实现。每次相加将结果依次放入新数组中,但加法我们要考虑到逢十进一的情况,因此在插入到新数组前我们通过addNum来判断是否需要进1,需要则把该值记录为1,下一位计算时加上if(i>=0){a = num[i];i--;}这段代码的作用在于当数组长度小于整数长度时,当整数还在进行取位计算时,数组的索引已经越界来,于是,我i吗便将越界后从数组中取到的值都自动设置为1.if(addNum==1){arr[aSize++] = 1;}这段代码的作用在于倘若我们进行最后一位相加的时候还需要进位,可此时遍历已经结束,这时,我们直接将1插入到数组的下一位。最后我们得到的数组再进行逆置操作便是最终结果了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老子裤子马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值