代码随想录算法训练营D4|24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,链表总结

目录

24. 两两交换链表中的节点

19. 删除链表的倒数第 N 个结点

面试题 02.07. 链表相交

142.环形链表II

链表总结


该题需要弄清楚如何翻转节点(cur指针的位置)以及判断反转结束的条件

public class SwapPairs {

    /**
     * 虚拟头节点法
     *
     * @param head
     * @return 头节点
     */
    public ListNode dummyHeadMethod(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        // 对于操作的节点需要一个cur指针指向它的上一个节点,用于记录原链表节点的顺序
        ListNode cur = dummy;
        ListNode tmp1, tmp2;

        // 两两交换操作需要满足偶数位节点不为null,同时也要保证不会空指针报错
        while (cur.next != null && cur.next.next != null) {
            // 记录原链表节点的指针
            tmp1 = cur.next;
            tmp2 = cur.next.next.next;
            // 交换
            cur.next = cur.next.next;
            cur.next.next = tmp1;
            tmp1.next = tmp2;
            // 移动cur指针,为下一轮交换做准备
            cur = tmp1;
        }
        return dummy.next;
    }
}

递归方法后补

19. 删除链表的倒数第 N 个结点

双指针法,利用快慢指针的差来找到倒数第N个节点

虚拟头节点,看起来涉及到链表的都需要

public class RemoveNthFromEnd {

    /**
     * 快慢指针法(利用快慢指针步差为n,来找到倒数第N个节点,操作节点必须要让指针指向上一个节点,因而步差为n+1)
     *
     * @param head
     * @param n    倒数第n个节点
     * @return
     */
    public ListNode twoPointersMethod(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        // 快指针用来找到链表的尾部
        ListNode fast = dummy;
        // 慢指针用来找到删除节点的上一个节点
        ListNode slow = dummy;

        // 快指针要比慢指针先多走N+1步,这样才能找到倒数第N+1个节点来删除第N个节点
        while (n-- >= 0 && fast != null) {
            fast = fast.next;
        }
        // 此时快慢指针同时移动
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        // 此时快指针已指向null,slow指针则指向要删除节点的上一个节点
        // 删除操作
        // 避免空指针异常
        if (slow.next != null) {
            slow.next = slow.next.next;
        }
        // 返回新头节点
        return dummy.next;
    }
}

面试题 02.07. 链表相交

由于链表的节点只能指向一个节点的特性,链表相交后的节点必然都是同一个,把它想象成相汇会更好理解。

public class IntersectionOfTwoLinkedLists {

    /**
     * 先移动长链表,实现同节点同步移动来找到相交节点
     *
     * @param headA
     * @param headB
     * @return
     */
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 获得两链表的长度
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0;
        int lenB = 0;

        while (curA != null) {
            lenA++;
            curA = curA.next;
        }
        while (curB != null) {
            lenB++;
            curB = curB.next;
        }
        curA = headA;
        curB = headB;

        // 使得curA为最长链表的头节点,lenA为最长链表的长度,方便统一后续操作
        if (lenB > lenA) {
            // 交换len
            int tmpLen = lenB;
            lenB = lenA;
            lenA = tmpLen;
            // 交换cur
            ListNode tmpNode = curB;
            curB = curA;
            curA = tmpNode;
        }
        // 求长度差,为了让他们在同一节点(使得末尾对齐,必然是末尾对齐的)
        int gap = lenA - lenB;
        while (gap-- > 0) {
            curA = curA.next;
        }
        // 同步移动,相同节点则直接返回
        while (curA != null) {
            if (curA == curB) {
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }


    /**
     * 合并链表,同步移动,终会同步到达相交点
     *
     * @param headA
     * @param headB
     * @return
     */
    public ListNode combineLinkedListMethod(ListNode headA, ListNode headB) {
        ListNode p1 = headA, p2 = headB;
        while (p1 != p2) {
            // p1和p2从各自的头节点开始同步移动
            if (p1 == null) {
                // 走完原链表,则走对方链表
                p1 = headB;
            } else {
                p1 = p1.next;
            }

            if (p2 == null) {
                p2 = headA;
            } else {
                p2 = p2.next;
            }
        }
        return p1;
    }
}

142.环形链表II

环形链表主要考察:

  1.  如何判断有环(双指针)
  2.  如何找到环形链表入口处

 

public class LinkedListCycleII {

    /**
     * 利用快慢指针判断是否有环,双指针相遇找到环入口
     *
     * @param head
     * @return
     */
    public ListNode detectCycle(ListNode head) {
        // 假设快指针每次走两个节点
        ListNode fast = head;
        // 慢指针每次只走一个节点
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;

            // 快慢指针相遇(类似于追及问题)
            // 快指针走过的路程 = 2倍的慢指针走过的路程
            // 2(x+y)= x+n(y+z)+y; -> x = (n-1)(z+y)+z;
            // 假设只走了一圈就相遇了,那么可以推出 x=z
            if (fast == slow) {
                // 两个节点各自从fast和head开始走,必定相遇在环形入口节点
                ListNode cur1 = fast;
                ListNode cur2 = head;
                while (cur1 != cur2) {
                    cur1 = cur1.next;
                    cur2 = cur2.next;
                }
                return cur1;
            }
        }
        return null;
    }
}

链表总结

链表的特性:通过指针将内存中分散的节点串联。

虚拟头节点的技巧,可以避免对头节点的单独处理。

双指针是个好方法,用得巧可以解决很多问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值