算法训练第四天 —— 两两交换链表中的节点、删除链表的倒数第 N 个结点、链表相交、环形链表 II

60 阅读3分钟

24 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)

解题思维:

对于移动链表节点的问题,主要两个两种解法:

  1. 虚拟头结点法
  2. 递归法
     public ListNode swap(ListNode head) {
         if (head == null || head.next == null) return head;
         ListNode dummyNode = new ListNode(0);
         dummyNode.next = head;
         ListNode prev = dummyNode;
         while (prev.next != null && prev.next.next != null) {
             ListNode temp = head.next.next;
             prev.next = head.next;
             head.next.next = head;
             head.next = temp;
             prev = head;
             head = head.next;
 ​
         }
         return dummyNode.next;
     }
 ​
 ​
   public ListNode swapPairs(ListNode head) {
         if (head == null || head.next == null) return head;
         ListNode next = head.next;
         // 递归下两个
         ListNode listNode = swapPairs(next.next);
         // 交换数据
         next.next = head;
         head.next = listNode;
         return head;
     }
 ​

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

解题思路:

  1. 暴力解法:首先取出链表的头结点,然后通过head.next来遍历整个链表的个数,然后再次遍历链表取出倒数第n个结点
  2. 快慢指针法:首先快指针先走n步数,然后快慢指针同时走,当快指针为null的时候,慢指针就是指向的倒数第N个结点
  public ListNode removeNthFromEnd(ListNode head, int n) {
         ListNode dummyNode = new ListNode();
         dummyNode.next = head;
 ​
         ListNode fastIndex = dummyNode;
         ListNode slowIndex = dummyNode;
 ​
         // 快指针先走n个节点
         for (int i = 0; i < n; i++) {
             fastIndex = fastIndex.next;
         }
 ​
         while (fastIndex.next != null) {
             fastIndex = fastIndex.next;
             slowIndex = slowIndex.next;
         }
 ​
         // 删除第n个节点
         slowIndex.next = slowIndex.next.next;
         return dummyNode.next;
     }

07 链表相交

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

解题思路:题目隐藏的一个点:如果 listAlistB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]这个是重点:

  1. 求出两个链表A、B的长度、长度差gap,保证A为最长的链表
  2. A链表先走gap,然后A、B链表同时向后移动,如果相等则返回,否者返回null
 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         ListNode curA = headA;
         ListNode curB = headB;
 ​
         int lenA = 0, lenB = 0;
 ​
         while (curA != null) { // 求链表A的长度
             lenA++;
             curA = curA.next;
         }
 ​
         while (curB != null) {
             lenB++;
             curB = curB.next;
         }
 ​
         curA = headA;
         curB = headB;
 ​
         // 让curA为最长链表的头,leaA为其长度 (此时保证A链表为最长的链表)
         if (lenB > lenA) {
             int tempLen = lenA;
             lenA = lenB;
             lenB = tempLen;
 ​
             ListNode tempNode = curA;
             curA = curB;
             curB = tempNode;
         }
 ​
         // 求长度差
         int gap = lenA - lenB;
 ​
         // A链表移动长度差
         while (gap-- > 0) {
             curA = curA.next;
         }
         while (curA != null) {
             if (curA == curB) {
                 return curA;
             }
             curA = curA.next;
             curB = curB.next;
         }
         return null;
     }

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

解题思路:

  1. 此题重要的是需要一个移动一个位置的慢指针,一个移动2个位置的快指针。同时向前移动,如果有环必定相遇。
  2. 当两人相等有环时候,这个时候需要特殊处理,同时向前移动,一个点从头开始,一个点从相遇点开始移动,再次相等则为环入口

image.png

    public ListNode detectCycle(ListNode head) {
         ListNode slow = head;
         ListNode fast = head;
 ​
         while (fast != null && fast.next != null) {
             slow = slow.next;
             fast = fast.next.next;
             if (slow == fast) { // 有环
                 ListNode index1 = fast;
                 ListNode index2 = head;
                 // 两个指针,从头节点和相遇结点,各走一步,直到连个节点相等,相遇点为环入口 // 这个是2(x+y) = x+y+n(y+z) 当n=1成立的点,一个点从相遇点开始、一个点从头开始
                 while (index1 != index2) {
                     index1 = index1.next;
                     index2 = index2.next;
                 }
                 return index1;
             }
         }
         return null;
     }