「力扣」第 26 题:删除排序数组中的重复项


「力扣」第 26 题:删除排序数组中的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
 print(nums[i]);
}

思路:注意到排序数组这个条件。并且应该注意到一些特殊的测试用例,例如 nums = [] 的时候。注意,题目要求返回新数组的长度。

Python 代码:

class Solution:
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        size = len(nums)
        # 只要有重复的元素覆盖就可以了
        if size == 0:
            return 0
        # 接下来要赋值的那个元素
        j = 0
        for i in range(1, size):
            if nums[i] != nums[j]:
                j += 1
                nums[j] = nums[i]
        return j + 1

1、首先我们需要一个变量,这里我声明为memory 来保存一个被比较的数。首先,数组的第 1 个元素(index = 0 的那个)被存进去;

2、接着从数组的第 2 位开始遍历,遇到和 memory 的值一样的,就什么都不做,遇到和 memory 不一样的,把当前值更新到 memory 中,并且 j 加 1 ,在原数组 j 这个索引上也更新这个值。

分析:利用了 a sorted array 的特点,新数组的元素个数也一定不会超过原来的数组,所以可以不借助额外的空间来完成题目的要求。

public class Solution {

    public int removeDuplicates(int[] nums) {
        if(nums.length==0){
            return 0;
        }
        int memory = nums[0];
        int j = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] - memory > 0) {
                nums[j] = nums[i];
                j++;
                memory = nums[i];
            } else {
                continue;
            }
        }
        return j;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        // int[] nums = new int[]{1,1,1,2,2,2,3,3,3,4,5,6,7,7,7,8,9,10,10};
        int[] nums = new int[]{1};
        int result = solution.removeDuplicates(nums);
        System.out.println(result);
        System.out.println(Arrays.toString(nums));

    }
}

还有一种解法可以参:leet-solution
的解答。不借助额外的空间。一开始 j 在 i 后面一格,然后马上比较两个元素的值,如果元素的值一样,则 j 加 1 ,如果元素的值不一样,j 和 i 都加 1 。

解法2:

public class Solution2 {
    public int removeDuplicates(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }

        int j = 0;
        int i = 1;

        for (; i < nums.length; i++) {
            if (nums[i] == nums[j]) {
                // 什么都不做
            } else {
                nums[++j] = nums[i];
            }
        }
        // 返回的是数组的长度,所以要 + 1
        return j + 1;
    }
}

分析:

  • 要求从一个有序的数组中删除重复的元素。
  • 思考如何定义删除?是从数组中删除?还是放在数组的末尾?
  • 剩余元素的排列是否要保证原有的相对顺序?
  • 是否有空间复杂度的要求?
  • 这里也用到了循环不变量的定义,要明确才能正确写出代码逻辑。

思路

  1. 挨个加入到一个 Map 中,判断是否有键值,这个思路没有利用到数组的有序性,故不采纳;
  2. 后一个元素减去前一个元素,如果等于0,就说明重复了(我用这种办法)。
public class Solution {

    /**
     * 删除重复的元素,按照 LeetCode 上 Java 对删除数组元素的删除的判定,
     * LeetCode 上的规则就是不判定,只要这个数组前面有效索引上的数是正确的就可以了
     *
     * @param nums
     * @return 不重复的元素个数
     */
    public int removeDuplicates(int[] nums) {
        int k = 0;// 定义 [0,k)是一个没有重复元素的有序数组
        // 从第 1 个元素开始,依次考察前面的元素
        // 特别注意到三个元素连续的情况,例如 [6,6,6,6,7]
        // 判断第 1 个元素该不该进去
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        nums[k++] = nums[0];
        if (len == 1) {
            return 1;
        }
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] - nums[i - 1] != 0) {
                nums[k++] = nums[i];
            }
        }
        for (int i = k; i < nums.length; i++) {
            nums[i] = -1;
        }
        return k;
    }

    public static void main(String[] args) {
        //int[] sortedArray = {1, 2};
        //int[] sortedArray = {1, 1};
        int[] sortedArray = {1, 2, 2, 2, 3, 4, 5, 6, 6, 6, 7, 7, 8, 9};
        //int[] sortedArray1 = {1,2,3,2,3,4,5,6,6,6,7,7,8,9};
        Solution solution = new Solution();
        int non_duplicates = solution.removeDuplicates(sortedArray);
        System.out.println("不重复的元素个数有 => " + non_duplicates);
        System.out.println(Arrays.toString(sortedArray));
    }
}

总结:这一版我写得不好,吃过一次晚饭以后,才得到了 Accepted。总结如下:
1、没有考虑极端的情况;
2、第 1 个元素必须加入到结果数组中;
3、既然我是与前一个元素进行比较,那么我的索引最大值就应该是数组长度-1,边界值问题模糊不清也是我经常犯的错误。

(本节完)


文章作者: liweiwei1419
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 liweiwei1419 !
评论
 上一篇
「力扣」第 27 题:移动元素 「力扣」第 27 题:移动元素
「力扣」第 27 题:移动元素传送门:英文网址:27. Remove Element ,中文网址:27. 移除元素 。 给定一个数组 nums 和一个值 val,你需要*原地**移除所有数值等于 *val 的元素,返回移除后数组的新长度。
下一篇 
「力扣」第 3 题:无重复字符的最长子串(滑动窗口典型问题) 「力扣」第 3 题:无重复字符的最长子串(滑动窗口典型问题)
「力扣」第 3 题:无重复字符的最长子串(滑动窗口典型问题) 传送门:3. 无重复字符的最长子串; 题解链接:滑动窗口、哈希表优化 + 动态规划、滚动变量(Python 代码、Java 代码) 给定一个字符串,请你找出其中不含有重复
  目录