本人两年java后转的go,代码提供这两种语言,这里涉及到的题目有三个:
- 1:反转链表 206 206. 反转链表 - 力扣(LeetCode)
- 2:反转链表二 92 92. 反转链表 II - 力扣(LeetCode)
- 3:k哥一组反转链表 25. K 个一组翻转链表 - 力扣(LeetCode)
206:简单反转链表
这个属于简单类型,但是这里有我做的时候踩到一个变量初始化值的坑。
核心思维:需要三个节点来进行控制,cur、pre、nxt。
- cur:指向当前的节点
- pre:指向cur前面那个节点,如果cur为头结点,则此时 pre 为空
- nxt:记录cur反转前的next节点,防止在反转后丢失 反转前的下一个节点,导致错乱
go 语言代码:
func reverseList(head *ListNode) *ListNode {
// var pre *ListNode = nil
pre := &ListNode{}
pre = nil
cur := head
for cur != nil {
nxt := cur.Next
cur.Next = pre
pre = cur
cur = nxt
}
return pre
}
注意啦:我的坑在这里:var pre *ListNode = nil 跟 pre := &ListNode{},我理解的是var pre *ListNode = nil不会分配内存空间,只是指向了nil,而pre := &ListNode{}会分配内存空间,里面初始化了值为 0,并不是代表指向了nil
:=这个符号直接取代了var和type,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用var方式来定义全局变量。
换句话说,“:=”只能在声明“局部变量”的时候使用,而“var”没有这个限制。
java代码:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null, cur = head;
while (cur != null) {
ListNode nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
92 反转局部链表
需要反转一段链表,这里面注意一段链表反转之后,怎么去跟原来的链表进行关联。
go语言:
// 虚拟一个哨兵 作用是防止 left = 1 时候的处理
dummy := &ListNode{Next: head}
p0 := dummy
//用循环得到p0,指向要反转局部链表的前一个节点处
for i := 0; i < left-1; i++ {
p0 = p0.Next
}
// pre:反转链表的前一个节点 cur:指向反转链表的当前节点
pre := &ListNode{}
cur := p0.Next
for i := 0; i < (right - left + 1); i++ {
// nxt:记录反转链表当前节点指向的下一个节点,防止反转后找不到反转前链表的下一个节点
nxt := cur.Next
// 开始反转
cur.Next = pre
// 为下次反转做准备
pre = cur
cur = nxt
}
// 因为是链表局部的反转,所以局部反转链表的前面那个节点要重新指向。
p0.Next.Next = cur
p0.Next = pre
return dummy.Next
注意思考以下问题:
- 为什么是left-1次遍历?
- p0:指向开始被反转的节点的前一个未反转的节点的指针;
- right-left+1是被反转的节点个数;
- cur:指向被反转的节点的指针;
- pre:指向遍历过的被反转的节点的指针;
- p0->next->next = cur;
- p0->next 反转节点的第一个节点 再指向未反转链表的头节点cur;
- p0->next = pre;
- p0指向下一个节点是已经反转的最后一个节点pre,即反转链表的头节点;
java代码:
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode(0, head), p0 = dummy;
for (int i = 0; i < left - 1; ++i)
p0 = p0.next;
ListNode pre = null, cur = p0.next;
for (int i = 0; i < right - left + 1; ++i) {
ListNode nxt = cur.next;
cur.next = pre; // 每次循环只修改一个 next,方便大家理解
pre = cur;
cur = nxt;
}
p0.next.next = cur;
p0.next = pre;
return dummy.next;
}
}
25: k 个一组节点进行链表反转
这个题的要点都写在注释里面。
go语言:
// reverseKGroup k 个一组反转链表 25
// https://leetcode.cn/problems/reverse-nodes-in-k-group/
func reverseKGroup(head *ListNode, k int) *ListNode {
n := 0
cur := head
// 获取链表n 的长度
for cur != nil {
n++
cur = cur.Next
}
// 虚拟一个哨兵 作用是防止 left = 1 时候的处理
dummy := &ListNode{Next: head}
p0 := dummy
// pre:反转链表的前一个节点 cur:指向反转链表的当前节点
pre := &ListNode{}
cur = p0.Next
// 每 k 个组进行反转链表,剩余节点不足 k个 的话则不进行翻转
for ; n >= k; n -= k {
for i := 0; i < k; i++ {
// nxt:记录反转链表当前节点指向的下一个节点,防止反转后找不到反转前链表的下一个节点
nxt := cur.Next
// 开始反转
cur.Next = pre
// 为下次反转做准备
pre = cur
cur = nxt
}
//实在看不懂就看视频吧
nxt := p0.Next // 局部反转链表 反转前 的第一个节点,也是局部反转链表 反转后 的最后一个节点
p0.Next.Next = cur
p0.Next = pre
p0 = nxt
}
return dummy.Next
// 这个题目可以看做是多个局部链表反转,然后再把这些反转后的局部链表组装起来
// 怎么反转?看92 怎么组装? 理解92,不行再去看视频
// todo https://www.bilibili.com/video/BV1sd4y1x7KN/
}
java语言:
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
int n = 0;
for (ListNode cur = head; cur != null; cur = cur.next)
++n; // 统计节点个数
ListNode dummy = new ListNode(0, head), p0 = dummy;
ListNode pre = null, cur = head;
for (; n >= k; n -= k) {
for (int i = 0; i < k; ++i) { // 同 92 题
ListNode nxt = cur.next;
cur.next = pre; // 每次循环只修改一个 next,方便大家理解
pre = cur;
cur = nxt;
}
// 见视频
ListNode nxt = p0.next;
p0.next.next = cur;
p0.next = pre;
p0 = nxt;
}
return dummy.next;
}
}
更多文章欢迎访问我的个人博客网站:www.zpf0000.com