题目描述
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
完整代码参考:GitHub 代码
题解
此题难度定级为简单,主要考察链表的基本操作。
解法1: 去重
思路:通过额外的 list 记录所有不重复的节点(不能使用 set,无序的存储会打乱链表的结构),将 list 中的节点加入到新的链表中。
算法:遍历所有结点并在 list 中存储每个结点的引用(或内存地址)。通过第一个节点构造首节点,然后在首节点之后添加新的 list 中的节点数据。
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return null;
}
// 构建 list 顺序存储节点值
List<Integer> list = new ArrayList<>();
while (head != null) {
if (!list.contains(head.data)){
list.add(head.data);
}
head = head.next;
}
// 构造首节点
ListNode result = new ListNode(list.get(0));
for (int i = 1; i < list.size(); i++) {
// 添加节点
addListNodeFromLast(result, list.get(i));
}
return result;
}
public ListNode addListNodeFromLast(ListNode result, int num) {
ListNode newNode = new ListNode(num);
// 遍历需要一个额外的存储空间存储当前节点,找到尾节点
ListNode temp = result;
// 头节点非空,遍历节点
while (temp.next != null) {
// 节点后移
temp = temp.next;
}
// 把当前节点设置为尾节点
temp.next = newNode;
return result;
}
解法2:递归
思路:此题是递归思想解题的一个非常好的应用。由于需要去重操作,所以
算法:递归的三要素。首先明确递归出口,此题中当移动后的头结点为空,或其下一个节点为空时,即可返回,作为递归的出口。其次,以 head.next 作为递归的返回值,为什么要以 head.next 作为返回值而不是 head 呢?这是由于需要把 head 与 head.next 作为比较,如果相同,则需要删除一个节点(即去重)。递归的具体操作就是去除重复节点,即 head = head.next,直接将 head 的下一个节点作为 head 节点,此时删除了 head 节点,完成去重。
具体示例如下:
链表:-3 -> -1 -> 0 -> 0 -> 0 -> 3 -> 3
1. 递归出口返回 head 指向最后一个节点,即 3 -> null
2. 返回值 head.next = 3 -> null,此时 head -> 3 -> 3 -> null
3. 由于 head.data == head.next.data,故此时需要去重,将 head.next 指针指向 head 节点,此时去除第一个 3 节点,完成后 head -> 3 -> null
4. 递归返回下一个值 head = 0 -> 3 -> null,此时 head.data != head.next.data,进入下一次递归
5. 递归返回下一个值 head = 0 -> 0 -> 3 -> null,此时需要去重,将 head.next 指针指向 head 节点,此时去除第一个 0 节点,完成后 head -> 0 -> 3 -> null
6. 以此类推,最终完成去重
public ListNode deleteDuplicates(ListNode head) {
// 递归出口
if (head == null || head.next == null) {
return head;
}
// 返回值
head.next = deleteDuplicates(head.next);
// 相邻节点值相同时,删除后面的节点
if(head.data == head.next.data) {
head = head.next;
}
return head;
}