重生之我在代码随想录刷算法第四天 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

67 阅读5分钟

参考文献链接:代码随想录

本人代码是Java版本的,如有别的版本需要请上代码随想录网站查看。

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

[力扣题目链接]

解题思路

这道题我感觉比较简单,跟昨天翻转的比较像,只要能弄明白节点的next究竟指向谁,也就是到底怎么交换即可。

虚拟头节点

为了头节点操作方便,我们还是加入虚拟头节点,具体如何交换请看注释以及下方我画的图

image-20240914132723088.png

 /**
  * Definition for singly-linked list.
  * public class ListNode {
  *     int val;
  *     ListNode next;
  *     ListNode() {}
  *     ListNode(int val) { this.val = val; }
  *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  * }
  */
 class Solution {
     public ListNode swapPairs(ListNode head) {
         //加入虚拟头节点
         ListNode dummpy = new ListNode();
         dummpy.next = head;
         //tmp保存虚拟头节点
         ListNode tmp = dummpy;
         //如果当前节点的下个节点和下下个节点都不为null,那么这两个节点就要交换
         while(tmp.next != null && tmp.next.next!=null){
             //保存第一、二个节点
             ListNode firstNode = tmp.next;
             ListNode secondNode = tmp.next.next;
             //将tmp的next从一节点改为二节点
             tmp.next = tmp.next.next;
             //一节点的next改为二节点的next
             firstNode.next = secondNode.next;
             //将tmp的二节点位置改为一节点
             tmp.next.next = firstNode;
             //更新tmp节点到该交换的位置
             tmp = tmp.next.next;
         }
         return dummpy.next;
     }
 }

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

力扣题目链接

解题思路

这道题目比较简单但要读清题,是要删除倒数第n个节点。链表是没有数组那么灵活的,链表也不能倒着来,所以我们得先遍历一遍链表看它长度是多少,然后给他改为整数第几个节点去删除。

或者使用双指针就不用判断链表的长度了。

暴力解法

 /**
  * Definition for singly-linked list.
  * public class ListNode {
  *     int val;
  *     ListNode next;
  *     ListNode() {}
  *     ListNode(int val) { this.val = val; }
  *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  * }
  */
 class Solution {
     public ListNode removeNthFromEnd(ListNode head, int n) {
         ListNode dummpy = new ListNode();
         dummpy.next = head;
         int size = 0;
         ListNode countNode = dummpy;
         //循环去算出链表的长度
         while(countNode.next != null){
             size++;
             countNode = countNode.next;
         }
         countNode = dummpy;
         //移动相应次数,然后进行删除操作
         for(int i = 0;i < size - n;i++){
             countNode = countNode.next;
         }
         countNode.next = countNode.next.next;
         return dummpy.next;
     }
 }

双指针法

这道题目用双指针只是为了计数。

让一个指针先走n步,然后两个指针同时走,当快指针的next为null时,此时慢指针就走到了要删除的位置。

举个例子更明显一些,比如一个链表1->2->3->4,我们现在删除倒数第2位。

从虚拟节点开始,让快指针先走2步,此时快指针是在数字2的位置,此时慢指针也一起走,慢指向1的时候快指向3,慢2快4,此时快指针的next为null,慢指针走到了要操作的位置,删除下一个节点3即可。

 /**
  * Definition for singly-linked list.
  * public class ListNode {
  *     int val;
  *     ListNode next;
  *     ListNode() {}
  *     ListNode(int val) { this.val = val; }
  *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  * }
  */
 class Solution {
     public ListNode removeNthFromEnd(ListNode head, int n) {
         ListNode dump = new ListNode(-1);
         dump.next = head;
         ListNode slow = dump;
         ListNode fast = dump;
         for(int i = 0;i<=n;i++){
             fast = fast.next;
         }
         while(fast!=null){
             slow = slow.next;
             fast = fast.next;
         }
         slow.next=slow.next.next;
         return dump.next;
     }
 }

面试题 02.07. 链表相交

力扣题目链接

解题思路

这道题代码简单,但思路跟其余链表题不太一样。刚开始做这道题怎么都想不到如何能找到两个链表在哪里相交,因为这样是需要比较某两个节点是否相同,难不成我要遍历两个链表一个一个节点去比吗?

后来才发现是我傻了,如果链表相交,那他们相交之后的链表是一样的,即节点数也一样。也就是说一个长度为5的链表和一个长度为3的链表,你只需要让长度为5的链表移动到剩3的位置开始与另一个链表比较即可。

因此,只需要得出两个链表的长度,让长的那个先移动到对应位置,然后两个链表一起移动比较节点是否相同即可,只要相同,就说明他俩相交了。

 /**
  * Definition for singly-linked list.
  * public class ListNode {
  *     int val;
  *     ListNode next;
  *     ListNode(int x) {
  *         val = x;
  *         next = null;
  *     }
  * }
  */
 public class Solution {
     public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         ListNode cur1 = headA;
         int count1 = 0;
         ListNode cur2 = headB;
         int count2 = 0;
         while(cur1!=null){
             cur1 = cur1.next;
             count1++;
         }
         while(cur2!=null){
             cur2 = cur2.next;
             count2++;
         }
         cur1 = headA;
         cur2 = headB;
         if(count1>count2){
             for(int i = 0;i<count1-count2;i++){
                 cur1 = cur1.next;
             }
         }else{
             for(int i = 0;i<count2-count1;i++){
                 cur2 = cur2.next;
             }
         }
         while(cur1!=null){
             if(cur1==cur2){
                 return cur1;
             }
             cur1 = cur1.next;
             cur2 = cur2.next;
         }
         return null;
     }
 }

142.环形链表II

力扣题目链接

解题思路

这道题在我开始看的时候一头雾水,这怎么操作才能知道环的起点呢?

在我看了代码随想录的讲解后恍然大悟,原来这不仅是一道链表题,还是一道数学题。

以后遇到环形问题,两步走:是否有环,环的相交点在哪

是否有环

我们可以让一个快指针和慢指针同时从头节点走,快指针一次2步,慢指针一次1步,如果是环形链表则一定会相遇。

这个不难理解,自己实操一下模拟一下即可。

环的相交点

遇到这样的题目一看就不是光思考就能解决的,上图,上数学!

image-20240914143341885.png

因此就明了了,我们只需做两件事。

一是从头节点出发两个指针,一个步长为1一个为2。当他们相遇时,从相遇点和头节点分别出发一个步长为1的指针,当他们再次相遇,那么相遇点就是环入口。

 /**
  * Definition for singly-linked list.
  * class ListNode {
  *     int val;
  *     ListNode next;
  *     ListNode(int x) {
  *         val = x;
  *         next = null;
  *     }
  * }
  */
 public class Solution {
     public ListNode detectCycle(ListNode head) {
         ListNode slowNode = head;
         ListNode fastNode = head;
         ListNode index1;
         ListNode index2 = head;
         while(fastNode!=null&&fastNode.next!=null){
             fastNode=fastNode.next.next;
             slowNode=slowNode.next;
             if(slowNode==fastNode){
                 index1 = fastNode;
                 while(index1!=index2){
                     index1 = index1.next;
                     index2 = index2.next;
                 }
                 return index1;
             }
         }
         return null;
 ​
     }
 }