• 解剖—顺序表相关OJ练习题


    目录

    一、删除有序数组中的重复项,返回出现一次元素的个数。

    二、原地移除数组中所有数值等于val的元素

    三、合并两个有序数组

    四、旋转数组

    五、数组形式的整数加法


    一、删除有序数组中的重复项,返回出现一次元素的个数。

    26. 删除有序数组中的重复项 - 力扣(LeetCode)

    给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。数组大小numsSize已给出。

    考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

    • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
    • 返回 k 。
    1. int removeDuplicates(int* nums, int numsSize) {
    2. int src = 1;
    3. int dst = 0;
    4. while (src < numsSize) {
    5. if (nums[src] == nums[dst])
    6. src++;
    7. else
    8. nums[++dst] = nums[src++];
    9. }
    10. return dst + 1;
    11. }

    思路:采用双指针一前一后

    1、src负责寻找与当前dst相等的值,dst负责修改数组。

    2、如果src等于dst,则src向后移动一位,向后寻找不相同的值

    3、如果src不等于dst,则dst向后移动一位,将src的值赋值给dst,src向后移动一位继续循环比较后项元素。

    4、src指向数组最后一个元素时,比较后src大于数组大小numsSize时停止循环。

    5、返回值为新数组的大小dst+1。

    二、原地移除数组中所有数值等于val的元素

    27. 移除元素 - 力扣(LeetCode)

    给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移后数组的新长度。

    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

    元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    1. int removeElement(int* nums, int numsSize, int val) {
    2. int src = 0;
    3. int dst = 0;
    4. while (src < numsSize) {
    5. if (nums[src] != val)
    6. nums[dst++] = nums[src++];
    7. else
    8. src++;
    9. }
    10. return dst;
    11. }

    思路:采用双指针

    src负责寻找值为val的位置,dst负责修改数组。假设当前val等于7

    1、如果src不等于val,则dst赋值为src,然后整体向后移动。

    2、如果src等于val(dst也等于val),src向后移动一位。

    3、再次判断时src不等于val,dst(当前为val)被赋值为src(val后一项)。成功删除数组元素val。

    4、删除后dst和src均向后移动一位 。

    5、之后在数组中依次查找,没有等于val的元素则将src赋值给dst(后项值一次赋值给前项),赋值后二者向后移动一位。

    结果如下:

    6、最终返回数组长度dst。

    三、合并两个有序数组

    88. 合并两个有序数组

    给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

    请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

    注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

    示例 1:

    输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
    输出:[1,2,2,3,5,6]
    1. void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    2. int end1 = m - 1;
    3. int end2 = n - 1;
    4. int i = m + n - 1;
    5. while (end1 >= 0 && end2 >= 0) {
    6. if (nums2[end2] > nums1[end1])
    7. nums1[i--] = nums2[end2--];
    8. else {
    9. nums1[i--] = nums1[end1--];
    10. }
    11. }
    12. while (end2 >= 0)
    13. nums1[i--] = nums2[end2--];
    14. }

    思路:三指针法

    假设合并这两个数组:

    1、定义 end1 和 end2 分别为数组 num1 和 num2 的最后一个元素下标,定义 i 为合并后num1新的最后一个元素下标.

    2、因为是递增数列,所以我们从后向前先比较两个数组最后一个元素 (最大元素) 的大小。

    大的元素赋值给数组num1尾节点,然后将 i、end1和end2向前移动一位继续比较。

    3、如果end2位置元素不大于end1位置元素,则将end1位置元素赋值给 i 位置,end1和 i 向前移动一位。

    4、最后end1已将小于零时,end2还等于0,证明数组num2中还有未合并的元素且该元素小于num1中最小元素(首元素)。

     

    5、我们通过第二个循环将数组num2中小于num1最小元素的元素赋值到num1剩余位置,end2小于零时赋值结束。

     当然也可以使用qsot排序做这道题,但时间复杂度为O(nlogn)大于上述解法的O(m+n)。

     所以综合考虑推荐三指针法!!!

     四、旋转数组

    189. 轮转数组 - 力扣(LeetCode)

    给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

    示例 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]
    1. void reserve(int* a, int left, int right)
    2. {
    3. while (left < right) {
    4. int tmp = a[left];
    5. a[left] = a[right];
    6. a[right] = tmp;
    7. left++;
    8. right--;
    9. }
    10. }
    11. void rotate(int* nums, int numsSize, int k) {
    12. if (k > numsSize)
    13. k %= numsSize;
    14. reserve(nums, numsSize - k, numsSize - 1);
    15. reserve(nums, 0, numsSize - k - 1);
    16. reserve(nums, 0, numsSize - 1);
    17. }

    思路:使用三次逆转法,让数组旋转k次

    • 逆转子数组[numsSize - k, numsSize - 1]
    • 逆转子数组[0, numsSize - k - 1]
    • 整体逆转[0,numsSize - 1]

    假设轮转次数k为3 

    1、首先写出逆转数组的函数reserve,逆置的原理就是依次交换首尾元素。

     2、然后找到要逆转的数组对应部分进行逆转。

    3、 逆转过程如下:

    五、数组形式的整数加法

     989. 数组形式的整数加法 - 力扣(LeetCode)

    整数的 数组形式  num 是按照从左到右的顺序表示其数字的数组。

    • 例如,对于 num = 1321 ,数组形式是 [1,3,2,1] 。

    给定 num ,整数的 数组形式 ,和整数 k ,返回 整数 num + k 的 数组形式 。

    示例 1:

    输入:num = [1,2,0,0], k = 34
    输出:[1,2,3,4]
    解释:1200 + 34 = 1234
    
    1. int* addToArrayForm(int* A, int ASize, int K, int* returnSize) {
    2. int* addRet = (int*)malloc(10001 * sizeof(int));
    3. //reti: 第i位的结果
    4. int reti = 0;
    5. //从低位开始相加
    6. int ai = ASize - 1;
    7. int next = 0; // 进位值
    8. while (ai >= 0 || K > 0)
    9. {
    10. int x1 = 0;
    11. //如果ai没有越界,还有未相加的位,取出一位存入x1
    12. if (ai >= 0)
    13. {
    14. x1 = A[ai];
    15. --ai;
    16. }
    17. int x2 = 0;
    18. //如果k大于0,获取k的第i位
    19. if (K > 0)
    20. {
    21. x2 = K % 10;
    22. K /= 10;
    23. }
    24. //第i位的结果:每一位的值 + 进位
    25. int ret = x1 + x2 + next;
    26. //如果结果大于9,需要进位
    27. if (ret > 9)
    28. {
    29. ret %= 10;
    30. next = 1;
    31. }
    32. else
    33. {
    34. next = 0;
    35. }
    36. //存入第i位的结果到数组中
    37. addRet[reti++] = ret;
    38. }
    39. //如果最高位有进位,需要在存入1
    40. if (next == 1)
    41. {
    42. addRet[reti++] = 1;
    43. }
    44. //逆置结果
    45. reverse(addRet, 0, reti - 1);
    46. *returnSize = reti;
    47. return addRet;
    48. }

     思路:模拟加法进行逐位相加, 从低位向高位相加,最后整体逆置,得到最终结果

    1、定义一个整数数组 addRet 用于存储相加的结果。数组的大小设为10001,足够容纳可能的最大结果。

    定义变量 reti,表示结果数组的当前索引,初始化为0。

    定义变量 ai,表示数组 A 的当前索引,初始化为 ASize - 1,即数组 A 的最高索引。

    定义变量 next,表示进位值,初始化为0。

    1. int* addRet = (int*)malloc(10001 * sizeof(int));
    2. int reti = 0;
    3. int ai = ASize - 1;
    4. int next = 0;

    2、使用循环处理每一位的相加,循环条件是 ai >= 0 或 K > 0,即数组 A 还有位数未相加,或者 K 还有位数未相加。

    while (ai >= 0 || K > 0)

    3、在循环内部,首先获取 A 数组当前位的值 x1,如果 ai 没有越界,就取出对应位置的值,并将 ai 减1。

    1. int x1 = 0;
    2. if (ai >= 0)
    3. {
    4. x1 = A[ai];
    5. --ai;
    6. }

    4、获取整数 K 的当前位值 x2,如果 K 大于0,就取出 K 的最低位,同时将 K 除以10,以准备处理下一位。

    1. int x2 = 0;
    2. if (K > 0)
    3. {
    4. x2 = K % 10;
    5. K /= 10;
    6. }

    5、计算当前位的结果 ret,是 x1x2 和之前的进位 next 三者之和。如果结果大于9,表示需要进位,就对结果取模10,然后将 next 设置为1;否则,next 设置为0。

    1. int ret = x1 + x2 + next;
    2. if (ret > 9)
    3. {
    4. ret %= 10;
    5. next = 1;
    6. }
    7. else
    8. {
    9. next = 0;
    10. }

    7、将计算得到的 ret 存入结果数组 addRet 的当前位置 reti,递增 reti,以准备处理下一位。

    addRet[reti++] = ret;

    8、循环结束后,检查最高位是否有进位。如果 next 为1,表示有进位,因此将1存入结果数组的当前位置 reti

    1. if (next == 1)
    2. {
    3. addRet[reti++] = 1;
    4. }

    9、调用 reverse 函数来逆置结果数组 addRet,以得到正确的结果顺序。

    reverse(addRet, 0, reti - 1);
    

    最后,将结果数组的大小 reti 赋值给 returnSize 指向的变量,以告知结果数组的大小。

    返回结果数组 addRet,它包含了相加的结果。

    1. *returnSize = reti;
    2. return addRet;
  • 相关阅读:
    Lucene数据写入流程
    SpringMVC完整版详解
    Jenkins 参数化构建中 各参数介绍与示例
    用R语言模拟M / M / 1随机服务排队系统
    EMS Advanced Data Import高级数据导入选项Crack版
    《三叶虫与其他故事》我的恐惧如涟漪扩散,荡漾过百万年的时光
    甲氨蝶呤修饰PEG-PLGA纳米粒MTX-PLGA|载紫杉醇PTX的PLGA纳米粒PLGA-SS-PTX(定制)
    正则表达式的应用领域及基本语法解析
    这恍惚间的岁月,一个 00 后的回忆录【自述】
    Git(七).git 文件夹瘦身,GitLab 永久删除文件
  • 原文地址:https://blog.csdn.net/m0_73800602/article/details/133893669