题目描述
合并两个有序链表,去重相同元素
eg:
1 -> 2 -> 4
1 -> 3 -> 5
合并后得到 1 -> 2 -> 3 -> 4 -> 5
解法一:暴力法
直接先合并,再遍历一遍结果链表,消除重复节点
func main() {
l1 := &ListNode{
Val: 1,
Next: &ListNode{
Val: 2,
Next: &ListNode{
Val: 4,
},
},
}
printList(l1)
l2 := &ListNode{
Val: 1,
Next: &ListNode{
Val: 3,
Next: &ListNode{
Val: 5,
},
},
}
printList(l2)
res := mergeTwoList(l1, l2) // 先合并
res = removeDuplicates(res) // 再去重
printList(res)
}
func printList(head *ListNode) {
for head != nil {
if head.Next != nil {
fmt.Printf("%d ->", head.Val)
} else {
fmt.Printf("%d\n", head.Val)
}
head = head.Next
}
}
func mergeTwoList(l1, l2 *ListNode) *ListNode {
dummy := &ListNode{}
cur := dummy
p1, p2 := l1, l2
for p1 != nil && p2 != nil {
if p1.Val <= p2.Val {
cur.Next = p1
p1 = p1.Next
} else {
cur.Next = p2
p2 = p2.Next
}
cur = cur.Next
cur.Next = nil
}
if p1 != nil {
cur.Next = p1
} else if p2 != nil {
cur.Next = p2
}
return dummy.Next
}
func removeDuplicates(head *ListNode) *ListNode {
cur := head
for cur != nil && cur.Next != nil {
if cur.Val == cur.Next.Val {
cur.Next = cur.Next.Next // 跳过重复节点
} else {
cur = cur.Next
}
}
return head
}
解法二:时间复杂度优化
- 如果两条待合并链表长度相近,那么可以在一边判断大小合并的过程中,消除重复元素,整体时间复杂度更优
- 如果两条待合并链表长度相差较大,那么一个链表遍历完了,另一个链表的剩余部分,还是得用上述方式,再消除重复节点
func main() {
l1 := &ListNode{
Val: 1,
Next: &ListNode{
Val: 2,
Next: &ListNode{
Val: 4,
},
},
}
printList(l1)
l2 := &ListNode{
Val: 1,
Next: &ListNode{
Val: 3,
Next: &ListNode{
Val: 5,
},
},
}
printList(l2)
res := mergeTwoListUniq(l1, l2)
printList(res)
}
func printList(head *ListNode) {
for head != nil {
if head.Next != nil {
fmt.Printf("%d ->", head.Val)
} else {
fmt.Printf("%d\n", head.Val)
}
head = head.Next
}
}
func mergeTwoListUniq(l1, l2 *ListNode) *ListNode {
dummy := &ListNode{}
cur := dummy
p1, p2 := l1, l2
for p1 != nil && p2 != nil {
if p1.Val < p2.Val {
cur.Next = p1
p1 = p1.Next
} else if p1.Val > p2.Val {
cur.Next = p2
p2 = p2.Next
} else { // 值相同,任选一个,两个指针都前进
cur.Next = p1
p1 = p1.Next
p2 = p2.Next
}
cur = cur.Next
}
if p1 != nil {
cur.Next = removeDuplicates(p1)
}
if p2 != nil {
cur.Next = removeDuplicates(p2)
}
return dummy.Next
}
func removeDuplicates(head *ListNode) *ListNode {
cur := head
for cur != nil && cur.Next != nil {
if cur.Val == cur.Next.Val {
cur.Next = cur.Next.Next // 跳过重复节点
} else {
cur = cur.Next
}
}
return head
}
扩展1:删除排序链表中的重复元素
前面采用的是最直观的遍历思路,去跳过链表上重复的节点(链表删除操作),如下所示
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
cur := head
for cur != nil && cur.Next != nil{
if cur.Next.Val == cur.Val{
cur.Next = cur.Next.Next
}else{
cur = cur.Next
}
}
return head
}
实际上,本题解法,还可以采用双指针法
由于链表已经排好序,所以重复的元素一定连在一起,我们让快指针 fast 走在前面探路,慢指针 slow 走在后面,fast找到一个不重复的节点就给 slow串联起来,并让 slow 前进一步。这样就保证了slow指针串联的节点都是不重复的,当fast 走到链表末尾时,断开slow与最后面重复节点的连接即可。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil || head.Next == nil{ // 空链表或只有一个节点,不会重复
return head
}
slow, fast := head, head
for fast != nil{
if fast.Val != slow.Val{ // slow指向所有不重复的节点
slow.Next = fast
slow = slow.Next
}
fast = fast.Next
}
// 断开`slow`与最后面重复节点的连接
slow.Next = nil
return head
}
扩展2:删除排序链表中的重复元素 II
解法一:双指针法
上一题是让你把多余的重复节点去掉,这道题要求你把所有重复的元素全都去掉,改动一下上一题双指针写法
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
// 有可能头节点就重复了,因此构造虚拟节点,方便操作
// 题目说了 -100 <= node.val <= 100,所以用 -101 作为虚拟头节点值
dummy := &ListNode{
Val: -101,
Next: head,
}
slow, fast := dummy, head
for fast != nil{
if fast.Next != nil && fast.Next.Val == fast.Val{
// 发现有重复元素,跳过这一段相同的节点
for fast.Next != nil && fast.Next.Val == fast.Val{
fast = fast.Next
}
// 此时fast走到了这一段重复元素的最后一个节点,需要全部删除,所以再走一步
fast = fast.Next
// slow.Next此时仍指向fast一开始的位置,即这一段重复元素的第一个节点
// 直接断开与这一段的连接
slow.Next = nil
}else{
// 不重复节点,接到slow后面
slow.Next = fast
fast = fast.Next
slow = slow.Next
}
}
return dummy.Next
}
解法二:分解链表
将原链表分解为两条链表,一条链表存放不重复的节点,另一条链表存放重复的节点,返回不重复的这条链表即为答案
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
// 题目说了 -100 <= node.val <= 100,所以用 -101 作为虚拟头节点值
dummyDup := &ListNode{
Val: -101,
Next: head,
}
dummyUniq := &ListNode{
Val: -101,
Next: head,
}
pDup, pUniq := dummyDup, dummyUniq
p := head
for p != nil{
if (p.Next != nil && p.Next.Val == p.Val) || (p.Val == pDup.Val){
pDup.Next = p
pDup = pDup.Next
}else{
pUniq.Next = p
pUniq = pUniq.Next
}
p = p.Next
}
// 切断新链表末尾与原链表剩余节点的连接
pDup.Next = nil
pUniq.Next = nil
return dummyUniq.Next
}