题目描述
给定一个排序链表,删除所有的重复元素,使得每个元素只出现一次。
示例1:
输入: head = [1, 1, 2]
输出: head = [1, 2]
示例2:
输入: head = [1, 1, 2, 3, 3]
输出: head = [1, 2, 3]
解题思路
我们可以简单分析,当可使用辅助容器的时候,可以存在一个游历指针和一个辅助链表,指针游历链表并与辅助链表尾进行对比,若大于其值则将其插入辅助链表尾部,否则继续下一个节点。
步骤罗列
- 初始化一个指针
p
,一个辅助链表dummyHead
,一个指向辅助链表尾部的指针tailP
; p
指向head
,dummyHead
初始化为null
;p
遍历,并与tailP
比较值是否相等:相等时p
继续;否则插入tailP
,tailP
更新自己。
解题代码1
public static ListNode solutionWithCon(ListNode head){
/*
Time Com: O(n)
Space Com: O(m) (m:length of output linkedlist)
*/
if(head == null || head.next == null) return head;
//1. init pointers and container
ListNode p = head;
ListNode dummyHead = new ListNode(-1);
ListNode tailP = null;
//2. go through linkedlist
while(p != null){
if(tailP != null){
// 3. seek next node
if(p.val > tailP.val){
tailP.next = new ListNode(p.val);
tailP = tailP.next;
}
}else if(dummyHead.next == null){ // very first iteration
tailP = new ListNode(p.val);
dummyHead.next = tailP;
}
p = p.next;
}
return dummyHead.next;
}
复杂度分析
时间复杂度为:O(n)
空间复杂度:O(m) m为输出链表的长度
进阶1
可否有空间复杂度为O(1)的解题方法,这里我们惯性思维会考虑到双指针,这里设置slowP
为输出链表的尾节点,即在此节点之前均为排序后的且均无重复节点的序列;fastP
去遍历slowP
之后的节点,当fastP
到达整个链表尾部时,则结束遍历,此时令slowP的下一节点为null
.
步骤罗列
- 初始化两个指针
slowP
,fastP
均指向,slowP
指向head
,fastP
指向head.next
; - 当
fastP
大于slowP
值时,进行值的交换; fastP == null
为循环终止条件;slowP.next = null
, 返回head
;
解题代码2
public static ListNode solutionWithTwoP(ListNode head) {
/*
Time Com: O(n)
Space Com: O(1)
*/
if (head == null || head.next == null) {
return head;
}
//1. init pointers and container
ListNode slowP = head;
ListNode fastP = head.next;
//2. go through linkedlist
while (fastP != null) {
if (fastP.val > slowP.val)
{
// 3. found next node and exchange value
if (slowP.next != fastP)
slowP.next.val = fastP.val;
slowP = slowP.next;
}
fastP = fastP.next;
}
//4. release un-useful tail behind slowP
slowP.next = null;
return head;
}
复杂度分析
时间复杂度:对数据的遍历为一遍,认为O(n)
(严格的来说为O(m+n),n为输入链表长度,m为输出链表长度)
空间复杂度:O(1),不需要额外容器辅助;
进阶2
我们能否通过一个指针完成任务??在solutionWithTwoP代码中我们能否再进一步的精简代码,因为题干为已排序后的链表。
解题思路
我们依旧将slowP
作为输出链表的节点,对于遍历的另外一个依托,不是第二个指针,而是移动整个链表(时刻更新slowP.next
)
步骤罗列
- 创建并初始化
slowP=head
; - 比较
slowP
与slowP.next
; slowP.next == null
为循环终止条件;
解题代码3
public static ListNode solutionWithP(ListNode head) {
/*
Time Com: O(n)
Space Com: O(1)
*/
if (head == null || head.next == null) {
return head;
}
//1. init pointers and container
ListNode slowP = head;
//2. go through linkedlist
while (slowP.next != null && slowP.next.next != null) {
// 3. found next node and re-order node
if (slowP.next.val > slowP.val)
slowP = slowP.next;
slowP.next = slowP.next.next;
}
return head;
}
复杂度分析
时间复杂度:这里仅遍历了一次数据,故为O(n);
空间复杂度:不需要额外辅助容器,故为O(1);
GitHub代码
完整可运行代码文件请点击GitHub。