Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II

Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II

  • leetcode 24 两两交换链表中的节点
  • 24. 两两交换第二次做感悟
  • leetcode 19 删除链表的倒数第N个节点
  • 19 删除链表的倒数第N个节点第二次感悟:
  • 面试题 02.07. 链表相交
  • leetcode 142 环形链表 II
  • leetcode 142 环形链表 II 第二次做感悟

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

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

用虚拟头结点,这样会方便很多。

本题链表操作就比较复杂了,建议大家先看视频,视频里我讲解了注意事项,为什么需要temp保存临时节点。

题目链接/文章讲解/视频讲解:
https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html

本题关键:

  • 如何两两交换?
  • 循环终止条件?

举个栗子

Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II_第1张图片

  1. 如何两两交换? 两两交换时, 交换的是节点, 而不是数值. 为了方便操作第一个节点, 采用虚拟头结点的方式.
    交换的逻辑: 找到需要交换的两个节点的前一个节点cur, 进行交换, 再让cur指向接下来需要交换的两个节点前的一个节点.
    Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II_第2张图片
    图中插入了虚拟头结点
    temp1temp2保存1,3节点
 ListNode temp1 = cur.next;
ListNode temp2 = cur.next.next.next;

Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II_第3张图片

图中cur指向2, 会丢掉1, 所以要temp1保存1

 cur.next = temp1.next;

Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II_第4张图片
图中2指向1, 会丢掉3, 所以要temp2保存3

cur.next.next = temp1;

Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II_第5张图片
图中1指向3

temp1.next = temp2;

最后把捋直了就是这个样子:
在这里插入图片描述

因为要交换3和4, 要让cur到如图的位置也就是之前temp1的位置

  1. 循环终止条件?
  • 例子中偶数个节点, 插入了虚拟头结点就变成奇数个, cur从虚拟头结点开始, 最后会落在结点4, 那终止条件就是cur.next != null
  • 同理如果是奇数个, 就会变成偶数个, cur会落在倒数第二个节点上, 那中止条件就是 cur.next.next != null
    这里要注意写循环条件时, 要先写cur.next != null 再写 cur.next.next != null, 不然如果cur.next是null的话, 先判断cur.next.next就会出现空指针异常了

最终代码如下

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode cur = dummyNode;

        while (cur.next != null && cur.next.next != null) {
            ListNode temp1 = cur.next;
            ListNode temp2 = cur.next.next.next;

            cur.next = temp1.next;
            cur.next.next = temp1;
            temp1.next = temp2;
            cur = temp1;
        }
        return dummyNode.next;
    }
}

24. 两两交换第二次做感悟

第一印象:
  我觉得倒腾链表节点的题不难,这道题我自己做也做出来了,感觉是要三个指针一起倒腾。

看完题解的思路:
  我发现题解是上面那种思路,但我自己做的时候却是
Day4 链表: 24两两交换 | 19 删除链表的倒数第N个节点 | 02.07. 链表相交 | 142. 环形链表 II_第6张图片
  思路是一致的,但是对节点的选取是不一样的。pre 先指向 temp,cur 再指向 temp.next (也就是3 号节点),最后 temp 再指向 cur。这样就不会弄丢 3 号节点,和上面的的区别也只是顺序发生了一个变化。

实现中的困难:
  这道题也没啥困难,判断循环终止条件需要注意一下,我这样写的话需要在移动temp节点之前判断一下 cur 是不是null。 temp = cur.next;

附上代码,成功了就完事了:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null) {return null;}
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode pre = dummyNode;
        ListNode cur = head;
        ListNode temp = cur.next;

        while (temp != null) {
            pre.next = temp;
            cur.next = temp.next;
            temp.next = cur;
            pre = cur;
            cur = pre.next;
            if (cur == null) {
                break;
            }
            temp = cur.next;
        }
        return dummyNode.next;
    }
}

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

双指针的操作,要注意,删除第N个节点,那么我们当前遍历的指针一定要指向 第N个节点的前一个节点,建议先看视频。

题目链接/文章讲解/视频讲解:https://programmercarl.com/0019.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B9.html

本题关键:

  1. 怎么找到倒数的第n个结点

思路:

  1. 怎么找到倒数的第n个结点?
    使用快慢指针, 快指针先走n个,快慢指针再一起走, 快指针走到头, 慢指针就是倒数第n个了. 这里自己举个小栗子就很清晰.
    但是要注意, 删除节点一定要找到它前面的一个节点, 所以快指针要先走n+1个.

这道题比较简单, 仍然要虚拟头结点
代码如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode fast = dummyNode;
        ListNode slow = dummyNode;
        n++;
        
       while (n-- != 0 && fast != null) {
           fast = fast.next;
       }
       
       while (fast != null) {
           slow = slow.next;
           fast = fast.next;
       }
       `
       slow.next = slow.next.next;
       return dummyNode.next;
    }
}

19 删除链表的倒数第N个节点第二次感悟:

看完题解的思路:
  我的方法是走一遍链表记录长度,从删除倒数变成删除正数,比较容易想,但比题解的麻烦,但我ac了,附上代码:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode pre  = new ListNode();
        ListNode cur = dummyNode;
        int length = 0;

        while (cur.next != null) {
            cur = cur.next;
            length++;
        }

        if (n > length) {
            return null;
        }

        //有五个,倒数第二个就是正数第四个,就是5-2+1
        int size = length - n + 1;
        cur = dummyNode;
         pre.next = cur;

        while (size-- > 0) {
            cur = cur.next;
            pre = pre.next;
        }
        pre.next = cur.next;
        return dummyNode.next;
    }
}

面试题 02.07. 链表相交

本题关键:

  1. 怎么找到第一个相交的节点?
  2. 找到之后, 后面的节点要是不一样怎么办?

思路:

  • 怎么找到第一个相交的节点?
    要让链表尾对齐, 以短的那条长度n为标准, 只看长链表的后n个节点.
    这里只需要注意一件事情: 相交的链表直接比较指针相等, 只要有一处相等后续就相等, 而不是考虑节点的值.
    也就是说对于指针curA 和 curB 可以有语句 if (curA == curB) {…} 去判断节点是否相等。

    第一次做的时候忘记附上代码了,题目实现过程不难,我不会的地方在于我觉得ListNode对象不能直接判断 是否相等 。

附上代码:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = new ListNode();
        ListNode curB = new ListNode();
        int lengthA = 0;
        int lengthB = 0;

        curA = headA;
        curB = headB;

        while (curA != null) {
            lengthA++;
            curA = curA.next;
        }

        while (curB != null) {
            lengthB++;
            curB = curB.next;
        }
        
        int sub = Math.abs(lengthA - lengthB);
        curA = headA;
        curB = headB;

        if (lengthA > lengthB) {
            while (sub-- != 0) {
                curA = curA.next;
            }
        } else {
            while (sub-- != 0) {
                curB = curB.next;
            }
        }

        while (curA != null) {
            if (curA == curB) {
                return curA;
            } else {
                 curA = curA.next;
                curB = curB.next;
            }
        }
        return null;
    }
}

leetcode 142 环形链表 II

142.环形链表II

算是链表比较有难度的题目,需要多花点时间理解 确定环和找环入口,建议先看视频。

题目链接/文章讲解/视频讲解:https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html

本题关键

  1. 判断有没有环?
  2. 判断环的入口?

思路

  • 判断有没有环?
    使用快慢指针判断有无环, 快指针每次两个单位, 慢指针每次一个单位, 如果快慢指针相遇就是有环. 因为快指针相对于慢指针就是一个单位一个单位的去逼近,就不会跳过他的。
    这里注意如果快指针每次三个单位, 可能会出现有环但是不相遇的情况
    用快慢指针判断有无环是一个常用手段, 我在南京大学软件工程考研历年真题里见到过.
  • 判断环的入口?
    这里就是一个数学计算的过程了, 找到环内相遇点之后, index1head出发, index2从相遇点出发, 每次都走一个单位, 就会相遇在环的入口. 这个公式推导的时候注意slow在环内走不满一圈的时候就会和fast相遇, 不用考虑slow走了几圈之后再相遇, 因为fast的速度是slow的2倍, 如果slow走了1圈, fast就走了2圈了, 他们一定有一点相遇过. 我觉得想象成,我和你去操场跑圈,你跑一圈的时间我能跑两圈,那我一定套你也一圈 追上过你一次啊,换成生活里的感觉就好理解了。

代码如下 :

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = new ListNode();
        ListNode slow = new ListNode();

        fast = head;
        slow = head;

        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;

            if (slow == fast) {
                ListNode index1 = new ListNode();
                ListNode index2 = new ListNode();
                
                index1 = head;
                index2 = fast;

                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }    
        }
        return null;    
    }
}

leetcode 142 环形链表 II 第二次做感悟

第一印象:
  我就记得当初这道题很折磨,需要找数学规律,现在再看也是不会,直接看视频讲解了。

看完题解的思路:
  茅塞顿开了又,懒得实现一遍了,思路确实值得学习啊。

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