【代码随想录】链表-golang

链表 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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值