删除排序链表中的重复元素 II
力扣题目链接 -> 传送门
所用语言:Java
第一种方法是我自己想的,用了两次遍历,运行用时1ms,内存消耗38MB左右
第二种方法是我参照大佬的题解写的(和大佬的答案基本相似),运行用时0ms!不得不佩服大佬的细节处理
正文
解析①
可以看到,在题目的提示中结点值的范围是[-100, 100],这个范围并不是很大,我们可以使用数组来记录每个值出现的次数。
例如:我们定义一个偏移量int offset = 100
,使用偏移量将结点值的范围控制在[0, 200]之内。然后我们就可以定义一个记录数组int count[] = new int[201]
,数组的下标val + offset
表示结点值val
,而count[val + offset]
则表示值val
出现过的次数。如果count[val + offset]
> 1,表示值为val
的结点是重复结点,需要删除掉。
使用上面这种思路,我们一共需要两趟遍历。第一趟遍历用来填充记录数组count
;第二趟遍历用来删除重复结点。
Tip: 这是一种典型的,将数值定义转化为另一种定义,方便问题的求解。因为这个题目要求是删除所有重复的结点,所以在第二趟遍历中,count[val + offset]
的值不用改变。如果是要保留一个重复结点中的一个结点,在第二趟遍历时每删除一个重复结点count[val + offset]
的值就减一就好了,用来控制结点数。另外,这种方法不仅仅适用于有序链表,更适用于在无序链表中删除重复结点。(因为我一开始没有注意到链表是有序的,所以思考时代入的是对象是任何链表…(没有仔细看题真的好亏😤😤))
解析②
这个大佬的方法,则是巧妙地利用了链表是有序的这个特点。大佬的解析 -> 传送门
答案①
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return head;
}
int count[] = new int[201];
int offset = 100;
ListNode list = head;
do {
count[list.val + offset]++;
list = list.next;
} while (list != null);
list = head;
while (list.next != null) {
if (count[list.next.val + offset] > 1) {
list.next = list.next.next;
} else {
list = list.next;
}
}
// 处理头结点
if (count[head.val + offset] > 1) {
head = head.next;
}
return head;
}
答案②
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode tail = new ListNode();
ListNode newHead = tail;
while (head != null) {
if (head.next == null || head.val != head.next.val) {
tail.next = head;
tail = head;
} else {
// 跳过值相同的结点
while (head.next != null && head.val == head.next.val) {
head = head.next;
}
}
head = head.next;
}
tail.next = head;
newHead = newHead.next;
return newHead;
}
最后
个人感觉今天的这个题目是中等里面难度偏下的,题目本身做出来不是很难,难的是想到更巧妙的方案(虽然每道题都是这样)