链表系列操作总结

本文总结了链表的基本操作,包括查找、增加、删除结点,以及变形操作如查找倒数第m个结点、删除倒数第m个结点和链表翻转。特别强调了在操作中多指针和虚拟头结点的应用,对于链表的删除和添加操作,多指针的协同工作至关重要。此外,还讨论了链表分区问题,同样采用多指针策略解决。
摘要由CSDN通过智能技术生成
/**
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

文中的m,n都是有效的

基本操作

查找第 m 个 结点

在末尾加入结点

public void addNode(ListNode head, int val) {
    ListNode cur = head;
    // 只有下个节点不是空节点
    while (cur.next != null) {
        cur = cur.next;
    }
    cur.next = new ListNode(val);
}

在第m个结点之前或者之后加入结点

	public ListNode addNode(ListNode head, int val, int m) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        
        //如果是在 m 之后 插入结点
        //找到m
        //while (m-- > 0) {
        //    cur = cur.next;
        //}
        
        // 找到 m 的 前驱
        while (--m > 0) {
            cur = cur.next;
        }
        ListNode next = cur.next;
        cur.next = new ListNode(val);
        cur.next.next = next;
        return dummyHead.next;
    }

删除最后一个结点,注意删除只有一个结点的情况,用虚拟头结点最省事

	public ListNode removeNode(ListNode head) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        // 找到倒数第二个结点
        while (cur.next != null && cur.next.next != null) {
            cur = cur.next;
        }
        cur.next = null;
        return dummyHead.next;
    }

删除第 i 个结点

public ListNode removeNode(ListNode head, int m) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        // 找到第 m 个结点的前驱
        while (--m > 0) {
            cur = cur.next;
        }
        // 直接修改前驱的后继改为 m 的后继
        cur.next = cur.next.next;
        return dummyHead.next;
    }

变形操作

接下来的操作基本结合 多指针 + 虚拟头结点

查找倒数第m个结点

public ListNode query(ListNode head, int m) {
        ListNode l1 = head;
        ListNode l2 = head;
        // 先让指针 l2 移动 m 步
        while (m-- > 0) {
            l2 = l2.next;
        }
        // 然后 l1, l2 以前移动,知道l2指向空
        // 这个时候l1指向的正好是倒数的第m个结点
        while (l2 != null) {
            l1 = l1.next;
            l2 = l2.next;
        }
        return l1;
    }

删除倒数第 m 个结点

public ListNode removeNode(ListNode head, int m) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode l1 = dummyHead;
        ListNode l2 = dummyHead;
        // 因为是倒数第 m个结点,所以我们需要找到倒数的 第 m+1个 结点
        // 注意这里我们使用了 虚拟头结点
        while (m-- >= 0) {
            l2 = l2.next;
        }
        // 找到删除结点的前驱
        while (l2 != null) {
            l1 = l1.next;
            l2 = l2.next;
        }
        l1.next = l1.next.next;
        return dummyHead.next;
    }

将链表进行翻转
1->2->3->4
4->3->2->1
利用虚拟头结点,不断的像在虚拟头结点的头部之后插入结点,并且修改插入结点后面的指向。
本质就是一个向链表头部不断添加结点。

public ListNode reverse(ListNode head) {
        ListNode dummyHead = new ListNode(0);
        ListNode cur = head;
        while(cur != null) {
            ListNode next = cur.next;
            cur.next = dummyHead.next;
            dummyHead.next = cur;
            cur = next;
        }
        return dummyHead.next;
    }

反转m到n之间的链表

1->2->3->4->5->6,反转第2个结点到第5个结点
1->5->4->3->2->6
这道题目也是应用三个指针来解决问题
首先
pre 指向 m 前驱
start 指向 m
then 指向 m 的下一指针
然后

  1. 修改start 指向 then 的下一个结点
  2. then 指向 pre 的下一个结点
  3. pre 指向 then
  4. 最后then 指向 start 的下一个结点
  5. 重复步骤1到4 n-m次
 public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        int i = m;
        while (--i > 0) {
            pre = pre.next;
        }
        ListNode start = pre.next;
        ListNode then = start.next;
        int diff = n - m;
        while (diff-- > 0) {
            start.next = then.next;
            then.next = pre.next;
            pre.next = then;
            then = start.next;
        }
        return dummyHead.next;
    }

给定一个链表和一个值x,对它进行分区,使所有小于x的节点都位于大于或等于x的节点之前。
您应该保留两个分区中每个节点的原始相对顺序。

Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5

这个题仍然是一个链表中的多指针应用,只要清楚了每次指针是怎么走的。基本上思路就有了。这个题和上面那个题很类似,也利用了三个指针。

public ListNode partition(ListNode head, int x) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        while (pre.next != null && pre.next.val < x) pre = pre.next;
        if (pre.next == null) return dummyHead.next;
        ListNode start = pre.next;
        ListNode then = start.next;
        while (then != null) {
            if (then.val >= x) {
                start = then;
                then = then.next;
            } else {
                start.next = then.next;
                then.next = pre.next;
                pre.next = then;
                pre = pre.next;
                then = start.next;
            }
        }
        return dummyHead.next;
    }

总结

总的来说指针的操作都需要结合一个虚拟头来进行操作,涉及到删除和添加操作的时候考虑多个指针协同工作。我觉得对于自己来说就是发现指针每次指向的变化容易把自己弄晕,这也是指针规律难点所在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值