返回

轻松掌握数组操作技巧,LeetCode刷题记录【1】助你轻松搞定有序数组合并、删除重复项

前端

掌握 LeetCode 算法:解决数组操作的常见难题

作为软件工程师,算法和数据结构的熟练运用是必不可少的技能。而 LeetCode 作为算法练习的领头羊,更是备受程序员推崇,是面试备战的必备神器。

在本文中,我们将深入探讨 LeetCode 刷题记录中的三个经典数组操作问题:有序数组合并、移除数组指定元素和删除有序数组中重复项。通过逐一解剖这些问题,我们将在轻松掌握其解法的同时,提升我们的数组操作技巧,为编程面试做好充分准备。

有序数组合并

设想这样的场景:你手头有两个已经排好序的数组,nums1 和 nums2。现在你的任务是将这两个数组合并为一个新的有序数组。

解法一:暴力法

暴力法是一种直观的解法,它逐个比较 nums1 和 nums2 中的元素,将较小的元素加入到新数组中。这种方法虽然简单易懂,但效率较低,尤其是当数组规模较大时。

void merge(int* nums1, int m, int* nums2, int n) {
    int i = 0, j = 0, k = 0;
    while (i < m && j < n) {
        if (nums1[i] < nums2[j]) {
            nums1[k] = nums1[i];
            i++;
        } else {
            nums1[k] = nums2[j];
            j++;
        }
        k++;
    }
    while (i < m) {
        nums1[k] = nums1[i];
        i++;
        k++;
    }
    while (j < n) {
        nums1[k] = nums2[j];
        j++;
        k++;
    }
}

解法二:双指针法

双指针法是一种更为高效的解法。它使用两个指针 i 和 j 分别遍历 nums1 和 nums2,将较小的元素加入到新数组中。当一个数组遍历完毕后,将剩余的另一个数组的元素全部加入到新数组中。

void merge(int* nums1, int m, int* nums2, int n) {
    int i = m - 1, j = n - 1, k = m + n - 1;
    while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]) {
            nums1[k] = nums1[i];
            i--;
        } else {
            nums1[k] = nums2[j];
            j--;
        }
        k--;
    }
    while (j >= 0) {
        nums1[k] = nums2[j];
        j--;
        k--;
    }
}

移除数组指定元素

现在,我们有一个数组 nums 和一个元素 val。我们的目标是移除数组中所有值为 val 的元素,并返回移除后的数组长度。

解法一:双指针法

双指针法可以再次派上用场。它使用两个指针 i 和 j 分别遍历 nums。当 j 指向值为 val 的元素时,我们跳过该元素,继续遍历。当 j 指向非 val 元素时,我们将该元素移动到位置 i,并更新 i 指针。

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

解法二:原地修改法

原地修改法是一种更为简洁的解法。它使用一个指针 k 来覆盖数组中的非 val 元素。当 nums[i] 不等于 val 时,我们将 nums[i] 移动到位置 k 并更新 k。

int removeElement(int* nums, int n, int val) {
    int k = 0;
    for (int i = 0; i < n; i++) {
        if (nums[i] != val) {
            nums[k] = nums[i];
            k++;
        }
    }
    return k;
}

删除有序数组中重复项

最后,我们有一个有序数组 nums。我们的任务是删除所有重复元素,并返回不重复元素的数组长度。

解法一:双指针法

双指针法又一次大显身手。它使用两个指针 i 和 j 分别遍历 nums。当 nums[i] 等于 nums[j] 时,我们跳过重复元素,继续遍历。当 nums[i] 不等于 nums[j] 时,我们将 nums[j] 移动到位置 i + 1 并更新 i 指针。

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

解法二:原地修改法

原地修改法再次出马。它使用一个指针 k 来覆盖数组中的不重复元素。当 nums[i] 不等于 nums[i - 1] 时,我们将 nums[i] 移动到位置 k 并更新 k。

int removeDuplicates(int* nums, int n) {
    int k = 0;
    for (int i = 0; i < n; i++) {
        if (i == 0 || nums[i] != nums[i - 1]) {
            nums[k] = nums[i];
            k++;
        }
    }
    return k;
}

常见问题解答

  1. 为什么双指针法在这些问题中如此常用?
    双指针法是一种高效的技巧,因为它减少了数组的遍历次数,降低了时间复杂度。

  2. 原地修改法和双指针法有什么区别?
    原地修改法不会创建新数组,而是覆盖原数组中的元素。双指针法则会创建一个新数组来存储不重复的元素。

  3. 何时选择使用暴力法而不是双指针法或原地修改法?
    当数组规模较小时,暴力法可能更简单、更直观。然而,对于大型数组,双指针法或原地修改法在效率方面更胜一筹。

  4. 这些算法的时间复杂度是多少?
    有序数组合并、移除数组指定元素和删除有序数组中重复项的时间复杂度都是 O(n),其中 n 是数组的长度。

  5. 在编程面试中如何应对数组操作问题?
    首先理解问题,然后选择合适的算法。清晰地解释你的解决方案,并注意时间和空间复杂度。

结论

通过剖析这些 LeetCode 难题,我们掌握了解决数组操作问题的基本技巧。双指针法和原地修改法是两大法宝,可以大幅提升我们的算法效率。在编程面试中,熟练运用这些算法至关重要。通过坚持练习和不断探索,相信我们都能成为算法解题高手,为面试成功做好万全准备。