代码随想录 链表模块小结

今天花了一些时间再回看链表部分。链表部分其实只要搞清楚几个重点,感觉还是比较好下手的。

个人认为要把握的重点:

1、虚拟头结点的运用

2、操作节点的次序

3、迭代前后的节点相对位置

4、操作节点本身与其属性

一、虚拟头结点的运用

虚拟头结点一般涉及到节点删除操作时,都会使用到,目的是减去处理头结点的逻辑,将链表中的所有节点统一看待,即所有节点用一套删除逻辑。

以LC_203删除链表节点来说。

203. 移除链表元素 - 力扣(LeetCode)

删除任意节点,需要让当前节点的前一个节点指向当前节点后一个节点,那么头结点该怎么删?

此时需要虚拟头结点,让其next指向实际头结点。

之后从虚拟头结点开始遍历就可以了。

ListNode prehead = new ListNode();  // 虚拟头结点
prehead.next = head;   // 连接实际头结点
ListNode node = prehead;   // 遍历节点从虚拟头结点开始 可以认为node就是prehead

这里有个关键点,一开始的node是等于prehead的,如果要删除的是头结点,那么让node.next指向node.next.next,会连带着prehead.next一起指向node.next.next,那么就做到了删除头结点。

后续遍历过程中如果遇到不是要删除的节点,那么只有node会向后走,prehead和node会分离。

这个也是重点里面的第四点。

完整代码如下,要注意最后返回的是prehead.next,因为这才是实际的头结点。

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        
        ListNode prehead = new ListNode();  // 虚拟头结点
        prehead.next = head;   // 连接实际头结点
        ListNode node = prehead;   // 遍历节点从虚拟头结点开始 可以认为node就是prehead
        while (node.next != null) {  // 注意是node.next不为空 即node指向的节点为空
            if (node.next.val == val) {
                // 让node指向下一个节点的下一个节点
                // 一开始node和prehead是一起移动 所以如果删头结点也是可以的
                node.next = node.next.next;
            }else {
                // 开始执行这一步 意味着node和prehead分离
                node = node.next;
            }
        }
        // 虚拟头结点的下一个节点才是实际头结点
        return prehead.next;
    }
}

二、操作节点的次序

以LC_206反转链表为例。

206. 反转链表 - 力扣(LeetCode)

翻转链表涉及的节点操作次序问题很重要,因为次序错误会导致丢失部分节点。

反转链表的操作节点顺序如下:

  1. 首先用temp保存curNode的next  因为接下来要改变lastNode节点的next指向了
  2. 再操作curNode对应的节点,让该节点指向preNode
  3. 接着更新指针节点的指向,让preNode等于curNode
  4. 最后让curNode等于temp

次序不能乱。

如果想不明白,画画图会清晰很多

完整代码如下

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode preNode = null;  // 前一个节点
        ListNode curNode = head;  // 当前节点
        while (curNode != null) {
            ListNode temp = curNode.next;
            curNode.next = preNode;   // 操作属性 就是操作对应节点的属性
            preNode = curNode;        //  循环结束的节点指向的状态一定是循环未开始时的状态
            curNode = temp;
        }
        return preNode;  // preNode成为头结点
    }
}

三、迭代前后的节点相对位置

节点指针在进入循环前的相对位置,和每轮循环后的相对位置要保持一致。

这一点只要是在遍历链表那么就一定是成立的。

节点指针在进入循环前的相对位置,需要通过具体的题目去分析,也可能有多种不同的初始化相对位置。

以LC_24两两交换节点为例。

24. 两两交换链表中的节点 - 力扣(LeetCode)

进入循环前的初始状态

ListNode pre = preHead;  // 两个指针,pre为cur的前一个节点
ListNode cur = head;

进入循环对节点进行操作后,这三个节点相对位置应当保持与循环前一致,所以要重新调整节点的指向,使其与循环前一致。

// 更新节点指向 回到之前的状态 pre在cur前面
pre = cur;
cur = cur.next;

完整代码如下,不太理解的话画一下操作完节点之后的各指针状态就比较清楚了。

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null) return head;

        ListNode preHead = new ListNode(); // 虚拟头结点
        preHead.next = head;  // next指向head
        ListNode pre = preHead;  // 两个指针,pre为cur的前一个节点
        ListNode cur = head;

        while (cur != null && cur.next != null) { // cur.next != null 是到尾结点 不用交换
            // 两两交换
            ListNode tmp = cur.next; // tmp其实可以不用
            pre.next = tmp;
            cur.next = tmp.next;  
            tmp.next = cur;   // tmp.next的更新得放最后 不然cur.next会找不到下一个
            
            // 更新指针指向
            pre = cur;
            cur = cur.next;
        }
        return preHead.next;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值