数组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:删除有序数组的三道题的主要内容,如果未能解决你的问题,请参考以下文章