删除排序链表中的重复元素
力扣题目链接 -> 传送门
所用语言:Java
第一种方法是用了两次遍历,运行用时1ms,内存消耗37MB左右
第二种方法是用了一次遍历,运行用时0ms,内存消耗37MB左右
正文
解析①
可以看到,在题目的提示中结点值的范围是[-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
;第二趟遍历用来删除重复结点。
另外,在第二趟遍历时,因为题目要求要保留重复结点中的一个,所以在删除结点的同时也要对count[val + offset]
减一,以改变重复结点的个数。当其个数等于1时,停止对该值结点的删减。
Tip: 这是一种典型的,将数值定义转化为另一种定义,方便问题的求解。**另外,这种方法不仅仅适用于有序链表,更适用于在无序链表中删除重复结点。
解析②
利用链表是有序的这个特点:如果值为n的结点有重复,那么重复结点是它的下一个结点。
所以,当遇到重复结点时,我们只需要一个一个地进行删除好了(基操)。详情请看答案②
答案①
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) {
// 注意: count[list.next.val + offset]-- 和 list.next = list.next.next 的顺序不能颠倒
count[list.next.val + offset]--;
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 list = tail;
while (head.next != null) {
if (head.val != head.next.val) {
tail.next = head;
tail = head;
head = head.next;
} else {
// 跳过相同值的结点,但是head依旧停留在原处
head.next = head.next.next;
}
}
tail.next = head;
return list.next;
}
最后
简单题,考察链表的操作而已
删除排序链表中的重复元素II 请看 -> 传送门