数组5:删除有序数组的三道题

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数组5:删除有序数组的三道题相关的知识,希望对你有一定的参考价值。

​做了前面几个题目,是否感觉算法其实就是将一个问题改改条件多折腾,下面这三个例子会进一步让你意识到这一点:

leetcode26. 删除有序数组中的重复项

leetcode27. 移除元素

leetcode80. 删除有序数组中的重复项 II

对于这种「相同元素最多保留 k 位元素」或者「移除特定元素」的问题,基本的做法是从题目本身特点出发,确定删除或者移动的逻辑,难度不算大。

面试时,我们希望刷到更多的原题来保证面试能过。而面试官则希望考察求职者真实的能力,因此希望通过修改条件来让对方无法刷到原题。在刷了第80题之后,你一定知道怎么通过修改花样来成为面试官。

我们分别来看:

一.第26. 删除有序数组中的重复项

给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。

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

输入:nums = [1,1,2]输出:2, nums = [1,2]解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

这个题还是比较简单的,使用双指针最方便,一个指针负责数组遍历,一个指向有效数组的最后一个位置。当两个指针的值不一样时,才将指向有效位的向下移动。

    public int removeDuplicates1(int[] nums) {        int n = nums.length;        //j用来标记有效位        int j = 0;        for (int i = 0; i < n; i++) {            if (nums[i] != nums[j]) {                nums[++j] = nums[i];            }        }        return j + 1;    }

测试代码:

注意这里更换测试序列的方法,在线性表之一介绍过,这是面试时特别实用的方法,一定要记住哟。

    public static void main(String[] args) {//        int[]arr=new int[]{1,1,2};        int[] arr = new int[]{0, 0, 1, 1, 1, 2, 2, 3, 3, 4};        int last = removeDuplicates1(arr);        for (int i = 0; i < last; i++) {            System.out.print(arr[i] + "  ");        }    }

输出结果为:

0  1  2  3  4

二.leetcode27. 移除元素

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

要求:不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例1:

输入:nums = [3,2,2,3], val = 3输出:2, nums = [2,2]

示例2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2输出:5, nums = [0,1,4,0,3]

思路:

  这个题目与上面的是不是类似呢?少不了双指针,不过呢,我们有两种处理方式,为了开拓思路,我们都看一下:

(1)第一种方法

将数组分成前后两段:前半部分是有效部分,后半部分是无效部分。所以我们可以很快地写出对应的实现:

    public int removeElement(int[] nums, int val) {        int ans = 0;        for(int num: nums) {            if(num != val) {                nums[ans] = num;                ans++;            }        }        return ans;    }

这里虽然只有一个变量ans,但是for循环的写法帮助我们减少了一个定义。

(2)第二种方式

这里还有一种解法,有的地方叫做交换移除。思路就是我们还是从两端开始向中间遍历,left遇到num[i]=val的时候停下来,右侧继续。当右侧遇到num[j]!=val的位置的时候,将num[j]交换或者直接覆盖num[i]。之后i继续向右走。

你品,你细品,这个思路和前面的奇偶调整是不是完全一致?

   public int removeElement(int[] nums, int val) {        int ans = nums.length;        for (int i = 0; i < ans;) {            if (nums[i] == val) {                nums[i] = nums[ans - 1];                ans--;            } else {                i++;            }        }        return ans;    }

三.leetcode 80. 删除有序数组中的重复项 II

给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素最多出现两次 ,返回删除后数组的新长度。

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

思路:

看到这个题,你是否会认同文章开头说的,算法就是给基本数据结构操作换个条件,来回折腾?这里如果改成三次、四次、K次是不是就是面试可能会遇到的题?

为了让解法更有普适性,我们就将保留2个修改为保留K个。我们仍然将数组分为两端,前者是处理完的,后者是未处理的,我们的步骤就是:

s1: 对于新访问的数字,与第K位置的比较,不相同的话直接保留进行了。

s2: 如果是相同的,由于是保留K个相同数字,如果前面出现的次数小于K就保留,否则就不要。

代码就是这样了:

int process(int[] nums, int k) {        int u = 0;         for (int x : nums) {            if (u < k || nums[u - k] != x){                nums[u++] = x;            }                 }        return u;    }

 再回到本题的要求,2个相同则保留,我们几乎什么都不动,传入的参数k设置为2就行了:

 public int removeDuplicates(int[] nums) {           return process(nums, 2);    }

看到了吗?假如面试官给你任意的K,你还怕吗?如果我们刷到这个程度,刷题才是真正有效的,而不会刷了很多题还是一脸懵B。

以上是关于数组5:删除有序数组的三道题的主要内容,如果未能解决你的问题,请参考以下文章

三道题学会二分查找——javapythonc三种语言解题( ̄▽ ̄)~*

26删除有序数组中的元素,数组仍然有序

典型的三道编程练习题

第二天

数组2:合并有序数组的两道题

滑动窗口10:存在重复元素的三个题