算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。

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

  • 使用虚拟头结点
  • 交换分三步走

算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第1张图片

 操作之后,链表如下:

算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第2张图片

看这个可能就更直观一些了:

 算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第3张图片

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        //设置虚拟头结点
        ListNode dummyNode = new ListNode(0, head);
        ListNode pre = dummyNode;
        //while的条件是至少保证有两个元素(举例 1->2->3->4),此时head为1,pre为虚拟头结点
        //pre->1(head)->2->3->4
        while (pre.next != null && pre.next.next != null) {
            ListNode temp = head.next.next;  //保存3
            pre.next = head.next;  //pre-> 2
            head.next.next = head; //2 -> 1(如果不保存3的话,3就再也找不到了),pre->2->1(head) 3->4
            head.next = temp;      //1 -> 3 ,pre->2->1(head)->3->4
            pre = head;           //2->1(pre)->3->4
            head = head.next;     //2->1(pre)->3(head)->4
//          下一轮保存null。第一步 1->4,第二步 4->3,
//          第三步 3 -> null ,第四步,pre和head后移
        }
        return dummyNode.next;
    }
}

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

1.定义fast指针和slow指针,初始值为虚拟头结点

 算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第4张图片

 2.fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:

 算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第5张图片

 3.fast和slow同时移动,直到fast指向末尾,如题:

 算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第6张图片

4.删除slow指向的下一个节点,如图: 

 算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第7张图片

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null || n < 1) {
            return head;
        }
        ListNode dummyNode = new ListNode(0, head);
        //定义双指针(初始指向虚拟头结点)
        ListNode slow = dummyNode;
        ListNode fast = dummyNode;
        //假设n等于2,(1->2->3),fast移动到2
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }
        //fast移动到3,slow移动到1,两者相差2(n的值)
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        //此时 slow的位置就是待删除元素的前一个位置。
        slow.next = slow.next.next;
        return dummyNode.next;
    }
}

面试题 02.07. 链表相交

求两个链表交点节点的指针。注意:交点不是数值相等,而是指针相等。

1.求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置。

算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第8张图片

2.比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

 算法Day4|链表专题二 24. 两两交换链表中的节点,19.删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II。_第9张图片

public class Solution {
   public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lenA = 0;
        int lenB = 0;
        ListNode nodeA = headA;
        ListNode nodeB = headB;
        while (nodeA != null) {// 求链表A的长度
            lenA++;
            nodeA = nodeA.next;
        }

        while (nodeB != null) {// 求链表B的长度
            lenB++;
            nodeB = nodeB.next;
        }
        nodeA = headA;
        nodeB = headB;          // 重新指向头结点
        if (lenB > lenA) {
            int temp = lenA;
            lenA = lenB;
            lenB = temp;        //swap(lenA,lenB)
            ListNode tempNode = nodeA;
            nodeA = nodeB;
            nodeB = tempNode;    //swap(nodeA,nodeB)
        }
        int chaLen = lenA - lenB; //计算lenA和lenB的差值
        for (int i = 0; i < chaLen; i++) {
            nodeA = nodeA.next;   //将nodeA移动到和nodeB长度一致的位置
        }
        while (nodeA != null) {   //比较指针是否相同,相同返回
            if (nodeA == nodeB) {
                return nodeA;
            } else {
                nodeA = nodeA.next;
                nodeB = nodeB.next;
            }
        }
        return null;
    }
}

142. 环形链表 II

主要考察两知识点:

  • 判断链表是否有环
  • 如果有环,如何找到这个环的入口

1.判断链表是否有环

快慢指针法,分别定义 fast slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

 2.如果有环,如何找到这个环的入口

结论:从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是环形入口的节点

具体步骤:

1.在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

2.让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

public class Solution {
    //142. 环形链表 II(是否有环,找到环的入口)
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;  // 快指针走两步
            slow = slow.next;  // 慢指针走一步
            if (fast == slow) {// 两者相遇,说明有环
                ListNode index1 = fast; //相遇位置
                ListNode index2 = head; //头结点
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

你可能感兴趣的:(算法,链表,算法,数据结构)