链表 from 代码随想录
移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
思路:设置一个新的节点,节点的下一个是链表的第一个节点
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeElements(head *ListNode, val int) *ListNode {
ptr := &ListNode{Next:head}
for vptr := ptr;vptr.Next != nil;{
if vptr.Next.Val == val{
vptr.Next = vptr.Next.Next
}else{
vptr = vptr.Next
}
}
return ptr.Next
}
设计链表
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
单向链表
设置一个头部的哨兵节点
type MyLinkedList struct {
head *ListNode
size int
}
type ListNode struct {
Next *ListNode
Val int
}
func Constructor() MyLinkedList {
return MyLinkedList{head:&ListNode{},size:0}
}
func (this *MyLinkedList) Get(index int) int {
if index > this.size - 1 || index < 0 {
return -1
}
ptr := this.head
for i:=0;i<=index;i++{
ptr = ptr.Next
}
return ptr.Val
}
func (this *MyLinkedList) AddAtHead(val int) {
this.AddAtIndex(0,val)
}
func (this *MyLinkedList) AddAtTail(val int) {
this.AddAtIndex(this.size,val)
}
func (this *MyLinkedList) AddAtIndex(index int, val int) {
if index > this.size {
return
}
index = max(index,0)
this.size ++
ptr := this.head
for i:=0;i<index;i++{
ptr = ptr.Next
}
adder := &ListNode{Val:val,Next:ptr.Next}
ptr.Next = adder
}
func max(a,b int)int{
if a > b {
return a
}
return b
}
func (this *MyLinkedList) DeleteAtIndex(index int) {
if index > this.size - 1 || index < 0{
return
}
this.size --
ptr := this.head
for i:=0;i<index;i++{
ptr = ptr.Next
}
ptr.Next = ptr.Next.Next
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.Get(index);
* obj.AddAtHead(val);
* obj.AddAtTail(val);
* obj.AddAtIndex(index,val);
* obj.DeleteAtIndex(index);
*/
双向链表
在两头各设置一个哨兵节点
type MyLinkedList struct {
head,tail *node
size int
}
type node struct{
val int
next,prev *node
}
func Constructor() MyLinkedList {
head := &node{}
tail := &node{}
head.next = tail
tail.prev = head
return MyLinkedList{head:head,tail:tail,size:0}
}
func (this *MyLinkedList) Get(index int) int {
if index >= this.size || index < 0 {
return -1
}
if index+1 > this.size/2 {
eptr := this.tail
for i:=0;i <= this.size-index-1;i++{
eptr = eptr.prev
}
return eptr.val
}else{
sptr := this.head
for i:=0;i <= index;i++{
sptr = sptr.next
}
return sptr.val
}
}
func (this *MyLinkedList) AddAtHead(val int) {
this.AddAtIndex(0,val)
}
func (this *MyLinkedList) AddAtTail(val int) {
this.AddAtIndex(this.size,val)
}
func (this *MyLinkedList) AddAtIndex(index int, val int) {
if index > this.size {
return
}
this.size++
index = max(index,0)
if index+1 > this.size/2 {
eptr := this.tail
for i:=0;i < this.size-index-1;i++{
eptr = eptr.prev
}
adder := &node{next:eptr,prev:eptr.prev,val:val}
eptr.prev.next = adder
eptr.prev = adder
}else{
sptr := this.head
for i:=0;i < index;i++{
sptr = sptr.next
}
adder := &node{next:sptr.next,prev:sptr,val:val}
sptr.next.prev = adder
sptr.next = adder
}
}
func max (a,b int)int{
if a > b {
return a
}
return b
}
func (this *MyLinkedList) DeleteAtIndex(index int) {
if index >= this.size || index <0 {
return
}
if index+1 > this.size/2 {
eptr := this.tail
for i:=0;i < this.size-index-1;i++{
eptr = eptr.prev
}
if eptr.prev.prev != nil{
eptr.prev.prev.next = eptr
}
eptr.prev = eptr.prev.prev
}else {
sptr := this.head
for i:=0;i < index;i++{
sptr = sptr.next
}
if sptr.next.next != nil{
sptr.next.next.prev = sptr
}
sptr.next = sptr.next.next
}
this.size --
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.Get(index);
* obj.AddAtHead(val);
* obj.AddAtTail(val);
* obj.AddAtIndex(index,val);
* obj.DeleteAtIndex(index);
*/
翻转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
迭代
思路:双指针(ptr指向当前节点;prev指向ptr的上一个节点),创建next临时节点
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
var prev *ListNode
ptr := head
for ptr != nil{
next := ptr.Next
ptr.Next = prev
prev = ptr
ptr = next
}
return prev
}
递归
注释来自leetcode评论区
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
// 以链表1->2->3->4->5举例
func reverseList(head *ListNode) *ListNode {
if head == nil || head.Next == nil{
/*
直到当前节点的下一个节点为空时返回当前节点
由于5没有下一个节点了,所以此处返回节点5
*/
return head
}
//递归传入下一个节点,目的是为了到达最后一个节点
newHead := reverseList(head.Next)
/*
第一轮出栈,head为5,head.next为空,返回5
第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,
把当前节点的子节点的子节点指向当前节点
此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
此时链表为1->2->3->4<-5
返回节点5
第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
此时链表为1->2->3<-4<-5
返回节点5
第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
此时链表为1->2<-3<-4<-5
返回节点5
第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
此时链表为1<-2<-3<-4<-5
返回节点5
出栈完成,最终头节点5->4->3->2->1
*/
head.Next.Next = head
head.Next = nil
return newHead
}
双指针递归
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
return reverse(nil,head)
}
func reverse (prev,head *ListNode) *ListNode {
if head == nil{
return prev
}
tmp := head.Next
head.Next = prev
return reverse(head,tmp)
}
两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
迭代
思路:衔接虚拟头节点,通过上一个节点prev后两个节点
1 加上head
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
dummy := &ListNode{Next:head}
prev := dummy
for head != nil && head.Next != nil{
prev.Next = head.Next
next := head.Next.Next
head.Next.Next = head
head.Next = next
prev = head
head = next
}
return dummy.Next
}
不带head(推荐)
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
dummy := &ListNode{Next:head}
prev := dummy
for prev.Next != nil && prev.Next.Next != nil{
node1 := prev.Next
node2 := prev.Next.Next
prev.Next = node2
node1.Next = node2.Next
node2.Next = node1
prev = node1
}
return dummy.Next
}
递归
思路:
在这里插入图片描述
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil{
return head
}
one := head
two := one.Next
three := two.Next
two.Next = one
one.Next = swapPairs(three)
return two
}
删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
思路:快慢指针,慢指针为dummy,快指针为head,快指针先移动n次,然后移动慢指针,直到快指针为空
时间复杂度O(n),空间复杂度O(1)
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
dummy := &ListNode{Next:head}
fp,sp := head,dummy
for i:=0;i<n;i++{
fp = fp.Next
}
for ;fp != nil;fp = fp.Next{
sp = sp.Next
}
sp.Next = sp.Next.Next
return dummy.Next
}
链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
哈希集合存储
思路:创建一个hashmap,先遍历headA存储全部元素,再去遍历b去尝试命中hashmap,命中返回节点,否则返回nil
空间复杂度O(m)m为headA的长度,时间复杂度为O(m+n) m,n为headA和headB的长度
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
vis := map[*ListNode]bool{}
for tmp:=headA;tmp != nil;tmp = tmp.Next{
vis[tmp] = true
}
for tmp:=headB;tmp != nil;tmp = tmp.Next{
if vis[tmp]{
return tmp
}
}
return nil
}
双指针
思路:记住链表拼接
存在相同节点:ptrA遍历完a表再遍历b表,步数为a+(b-c),ptrB遍历完b表再遍历a表,步数为b+(a-c),
不存在相同节点:ptrA:a+b;ptrB:b+a
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil{
return nil
}
ptrA,ptrB := headA,headB
for ptrA != ptrB {
if ptrA == nil{
ptrA = headB
}else {
ptrA = ptrA.Next
}
if ptrB == nil{
ptrB = headA
}else{
ptrB = ptrB.Next
}
}
return ptrA
}
环形链表II
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
哈希表-空间复杂度较高,不推荐
思路:遍历链表,先尝试命中map,如果没有就将元素存储在map里
时间复杂度O(n),空间复杂度O(n)
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
gain := map[*ListNode]int{}
pos := 0
for head != nil {
if _,ok := gain[head];ok {
return head
}
gain[head] = pos
pos ++
head = head.Next
}
return nil
}
双指针
思路 ,这是快慢指针,快指针速度为2,慢指针速度为1
>第一次相遇时:sp走了 x + y;fp走了x+y+n(z+y),由此可推算出
2spLength = fpLength -> 2(x+y) = x+y+n(z+y) -> x+y = n(y+z)
-> x = n(y+z) - y -z + z -> x = (n-1)(y+z) + z
(此为简化抽象过程,n可以为随机大于等一1的数字)当n等于0时,x = z第一次相遇后,将fp重定位到head,以速度为1前进直到再次与sp相遇得出胡那行链表的入口节点
自解版本
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
fp,sp := head,head
for fp != nil{
if fp.Next == nil{
return nil
}
fp = fp.Next.Next
sp = sp.Next
if fp == sp {
break
}
}
if fp == nil{
return nil
}
fp = head
for fp != sp{
fp = fp.Next
sp = sp.Next
}
return fp
}
标准版本
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
fp,sp := head,head
for fp != nil{
if fp.Next == nil{
return nil
}
fp = fp.Next.Next
sp = sp.Next
if fp == sp {
fp = head
for fp != sp {
fp = fp.Next
sp = sp.Next
}
return fp
}
}
return nil
}