你可以打印出来,平时没事复习,看看
学习来源:https://greyireland.gitbook.io/algorithm-pattern/shu-ju-jie-gou-pian/linked_list
用的人家的思路,题解自己写,算是对其补充,也算是打卡
链表相关的核心点
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

go虽然是值传递,但是链表用到了指针,传递的是一个地址,所以这种情况下可以说是址传递
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
current := head // 把头节点给current,代表当前节点
for current != nil { // 当前节点不为空
// 当下一个节点不为空,且当前节点的值等于下一个节点的值时候,把下一个节点替换为下下一个节点,简单说就是删除下一个节点,
for current.Next != nil && current.Val == current.Next.Val {
current.Next = current.Next.Next
}
current = current.Next // 使其移动到下一个节点,继续删除多余元素 使当前节点为空,跳出循环
}
return head
}
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

注意区分和83题,这一题要的是删除所有的只要是重复过的,举例出现了三个1,都删掉,83题是重复的删除,留一个,三个1删除两个留一个1
用到了虚拟节点dummy
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil {
return head
}
// 使用虚拟节点是因为节点头可能被删除,一旦节点头被删除就不好找到head这个链表了
dummy := &ListNode{Val: 0}
dummy.Next = head
headr := dummy // 这一步不要迷哈,上一步head已经完成了任务,把链表接给了dummy,所以head一身轻了,可以作为一个自由变量,如果不习惯的话,可以把代码中这一行以下的所有的head全都换一个名字
var rmVal int
for headr.Next != nil && headr.Next.Next != nil {
if headr.Next.Val == headr.Next.Next.Val {
rmVal = headr.Next.Val // 记录要删除的节点的值
for headr.Next != nil && headr.Next.Val == rmVal {
headr.Next = headr.Next.Next
}
} else {
// 删除完所有一样的元素后,去掉虚拟节点
headr = headr.Next
}
}
return dummy.Next
}
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

总结一下,这次带入例子思考的理解的特别慢,指针方向变了,我没理解,不过看了Carl的图解,一下就理解了
func reverseList(head *ListNode) *ListNode {
var pre *ListNode
cur := head
for cur != nil {
// next = 2,3,4,5
// cur接上pre
// pre = cur,反转指针方向
// cur = next 进入下一层
next := cur.Next
cur.Next = pre
pre = cur
cur = next
}
return pre
}
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

这一题看不懂…
思路:
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseBetween(head *ListNode, left int, right int) *ListNode {
if head == nil {
return head
}
dummy := &ListNode{Val:0}
dummy.Next = head
head = dummy
// head变成了 0->1->2->3->4->5->nil
var pre *ListNode
var i = 0
for i < left {
pre = head
head = head.Next
i++
}
// 链表变成了1(pre)->2(head)->3->4->5->nil
// i = 1
var next *ListNode
for head != nil && i <= right {
// 经典四步(双指针法)+ j++
temp := head.Next
head.Next = next
next = head
head = temp
i++
}
// 用于中间节点连接
var mid = head
// 循环结束变成 1 nil<-2<-3<-4 5(head)->nil
pre.Next = next // pre变成了1->4->3->2-nil
mid.Next = head // 1->4->3->2-5->nil
return dummy.Next
}
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
dummy := &ListNode{Val: 0}
head := dummy
for l1 != nil && l2 != nil {
if l1.Val < l2.Val {
head.Next = l1
l1 = l1.Next
} else {
head.Next = l2
l2 = l2.Next
}
head = head.Next
}
// 不要像是pattern给的代码一样,一个节点一个节点的给的....这个不太理解,他是指针,一个一个给的话,浪费资源
if l1 != nil {
head.Next = l1
}
if l2 != nil {
head.Next = l2
}
return dummy.Next
}
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。

思路:
好好读题
整两个链表,最后拼接在一起
好好思考一下dummy的妙用
func partition(head *ListNode, x int) *ListNode {
// 直接判断在x后边的元素是否有小于x的,就放在大于或等于x的节点的前面
if head == nil {
return head
}
headDummy := &ListNode{Val: 0}
tailDummy := &ListNode{Val: 0}
tail := tailDummy
// tail就是相当于tailcur,是当前的意思,节点会向下移动,tailDummy就是这个链表头节点,head同样道理
headDummy.Next = head
head = headDummy // 等于是给head加了一个前节点0,思考一下为什么要加一个虚拟头节点
for head.Next != nil {
if head.Next.Val < x {
head = head.Next
} else {
t := head.Next // 移除前保存这个值
head.Next = head.Next.Next // 移除
// 把t给另一个链表tail
tail.Next = t
tail = tail.Next
}
}
// 拼接两个链表
tail.Next = nil
head.Next = tailDummy.Next
return headDummy.Next
}
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

func sortList(head *ListNode) *ListNode {
return mergeSort(head)
}
// 快慢指针找中间的节点
func findMiddle(head *ListNode) *ListNode {
slow := head
fast := head.Next
for fast != nil && fast.Next != nil {
fast = fast.Next.Next
slow = slow.Next
}
return slow
}
// 合并两个升序链表
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
dummy := &ListNode{Val: 0}
head := dummy
for l1 != nil && l2 != nil {
if l1.Val < l2.Val {
head.Next = l1
l1 = l1.Next
} else {
head.Next = l2
l2 = l2.Next
}
head = head.Next // 这一步不要忘了,当前head有了节点后指向下一个
}
if l1 != nil {
head.Next = l1
}
if l2 != nil {
head.Next = l2
}
return dummy.Next
}
func mergeSort(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
// 找中点,分割,合并
middle := findMiddle(head)
tail := middle.Next
middle.Next = nil
// dfs算法思想,找到最小子问题,递归
left := mergeSort(head)
right := mergeSort(tail)
res := mergeTwoLists(left, right)
return res
}
给定一个单链表 `L` 的头节点 `head` ,单链表 `L` 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
判断head为nil,和head.Next为nil情况直接返回
找中点,分割链表
反转后边的链表tail
改写合并两个升序链表为交叉合并,判断条件变为判断toggle的情况
func reorderList(head *ListNode) {
if head == nil || head.Next == nil{
return
}
middle := findMiddle(head)
tail := middle.Next
tail = reverseList(tail)
middle.Next = nil
head = mergeTwoList(head,tail)
}
// 快慢指针找中点
func findMiddle(head *ListNode) *ListNode {
slow := head
fast := head.Next
for fast != nil && fast.Next != nil {
fast = fast.Next.Next
slow = slow.Next
}
return slow
}
// 循环合并两个链表
func mergeTwoList(l1 *ListNode, l2 *ListNode) *ListNode {
dummy := &ListNode{Val: 0}
head := dummy
toggle := true
for l1 != nil && l2 != nil {
if toggle {
head.Next = l1
l1 = l1.Next
} else {
head.Next = l2
l2 = l2.Next
}
toggle = !toggle
head = head.Next
}
if l1 != nil {
head.Next = l1
}
if l2 != nil {
head.Next = l2
}
return dummy.Next
}
// 反转后半段的新链表,反转链表经典四步
func reverseList(head *ListNode) *ListNode {
var pre *ListNode
for head != nil {
cur := head.Next
head.Next = pre
pre = head
head = cur
}
return pre
}
给你一个链表的头节点 head ,判断链表中是否有环。
利用快慢指针会造成差距越来越大的特性,如果有环,走完之后,快指针每次循环以一个单位的速度追到慢指针
func hasCycle(head *ListNode) bool {
if head == nil {
return false
}
slow := head
fast := head.Next
for fast != nil && fast.Next != nil {
if fast == slow {
return true
}
fast = fast.Next.Next
slow = slow.Next
}
return false
}
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。

func detectCycle(head *ListNode) *ListNode {
if head == nil {
return head
}
// 还是快慢指针法
slow := head
fast := head.Next
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if fast == slow {
slow = slow.Next
fast = head
for slow != fast {
slow = slow.Next
fast = fast.Next
}
return slow
}
}
return nil
}
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

思路
自己写出来的,开心…不过是简单题
func isPalindrome(head *ListNode) bool {
if head == nil {
return true
}
// 快慢节点,分割
mid := middle(head)
// 反转链表
tail := reverseList(mid.Next)
mid.Next = nil
// 比对
for head != nil && tail != nil {
if head.Val != tail.Val{
return false
}
head = head.Next
tail = tail.Next
}
return true
}
func middle(head *ListNode) *ListNode {
slow := head
fast := head.Next
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
}
return slow
}
func reverseList(head *ListNode) *ListNode {
if head == nil {
return head
}
// 经典四步法
var pre *ListNode
for head != nil {
cur := head.Next
head.Next = pre
pre = head
head = cur
}
return pre
}
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。

/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* Random *Node
* }
*/
func copyRandomList(head *Node) *Node {
if head == nil {
return head
}
cur := head
for cur != nil {
// 像极了反转链表的经典四步
clone := &Node{Val: cur.Val, Next: cur.Next}
temp := cur.Next
cur.Next = clone
cur = temp
}
// 处理random指针
cur = head
for cur != nil {
if cur.Random != nil {
cur.Next.Random = cur.Random.Next
}
cur = cur.Next.Next
}
// 分割两个链表
cur = head
cloneHead := cur.Next
// 很妙,利用指针,循环交替,把两个交叉链表分割开来了
for cur != nil && cur.Next != nil {
temp := cur.Next
cur.Next = cur.Next.Next
cur = temp
}
return cloneHead
}