反转链表及其变种

本人两年java后转的go,代码提供这两种语言,这里涉及到的题目有三个:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值