问题描述:
Follow up for "Remove Duplicates":
What if duplicates are allowed at most twice?
For example,
Given sorted array nums = [1,1,1,2,2,3]
,
Your function should return length = 5
, with the first five elements of nums being 1
, 1
, 2
, 2
and 3
. It doesn't matter what you leave beyond the new length.
原问题链接:https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/
问题分析
初始方法
这是一个比较容易想到的办法,就是既然每个元素只允许出现两次,我们可以用一个map来记录每个元素出现的次数。同时用一个计数器来统计所有出现的合法的字符。在遍历整个列表的时候,判断每个元素,如果这个元素不在这个map里,则统计数coun++,并将这个值和对应的出现次数1加入到map中。如果map里有这个元素,但是出现的次数小于2,则count++,并将对应的出现次数加1。这里需要对元素做一个交换的操作,保证最后元素的顺序符合要求。
详细的实现如下:
public class Solution { public int removeDuplicates(int[] nums) { Map<Integer, Integer> map = new HashMap<>(); int count = 0; for(int i = 0; i < nums.length; i++) { int item = nums[i]; if(map.containsKey(item)) { if(map.get(item) < 2) { map.put(item, map.get(item) + 1); swap(nums, count++, i); } } else { map.put(item, 1); swap(nums, count++, i); } } return count; } private void swap(int[] a, int i, int j) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } }
这种思路比较容易想到,但是由于里面有大量的类型boxing, unboxing,所以它的执行效率受到比较大的影响。
改进方法
实际上如果仔细分析问题描述中的要求,我们还有一些地方没有利用上。比如说它里面给定的元素都是排序的。这是一个非常有用的地方。既然都是排序的,那么我们不用费劲去用一个map来保存元素了。只要从头往后遍历的时候去看这个元素,如果它当前的值比它索引之前两个位置的元素大,那么说明这个元素和前面的不同,需要将它加入到数组前面去。我们用一个变量i来表示符合条件的元素的索引。同时,针对数组长度的情况,要考虑当前索引小于2的边界情况。
这种实现更加简单高效,详细的代码实现如下:
public class Solution { public int removeDuplicates(int[] nums) { int i = 0; for (int n : nums) if (i < 2 || n > nums[i - 2]) nums[i++] = n; return i; } }