day4—链表完结

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

思路:用虚拟头结点会方便很多。交换的重点:交换的时候同时涉及到4个结点,我们要让cur指向第一个结点,才能方便得完成交换工作。
在这里插入图片描述操作之后,链表如下:
在这里插入图片描述

//双指针版本
public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode cur = dummy;
        while (cur.next != null && cur.next.next != null){
            ListNode temp1 = cur.next;
            ListNode temp2 = cur.next.next;
            ListNode temp3 = cur.next.next.next;

            cur.next = temp2;
            temp2.next = temp1;
            temp1.next = temp3;

            cur = temp1;
        }
        return dummy.next;
    }

// 递归版本
class Solution {
    public ListNode swapPairs(ListNode head) {
        // base case 退出提交
        if(head == null || head.next == null) return head;
        // 获取当前节点的下一个节点
        ListNode next = head.next;
        // 进行递归
        ListNode newNode = swapPairs(next.next);
        // 这里进行交换
        next.next = head;
        head.next = newNode;

        return next;
    }
} 

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

思路:采用虚拟头结点加双指针轻松拿下,快指针和慢指针相差n个结点,当快指针的next为空时,说明慢指针已经走到待删除结点的前一个结点了,执行删除操作即可。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode fastIndex = dummy;
        ListNode slowIndex = dummy;
        //快指针相差n个结点
        for (int i = 0; i < n; i++) {
            fastIndex = fastIndex.next;
        }
        while (fastIndex.next != null){
            fastIndex = fastIndex.next;
            slowIndex = slowIndex.next;
        }

        slowIndex.next = slowIndex.next.next;
        return dummy.next;
    }
}

面试题 160. 链表相交

思路:这道题求的是两个链表的相交部分的头结点,注意是指针相同的结点而不是数值相同。有一个隐藏内容就是指针相同的话,那意味着该结点的后面部分应该都相同,所以长度也应该相同,我们就可以把两个链表从后往前对齐,然后从相交的第一个结点开始往后遍历,直到找到那个指针相同的结点即可。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lengthA = 0;
        int lengthB = 0;
        ListNode curA = headA;
        ListNode curB = headB;
        while (curA != null){
            curA = curA.next;
            lengthA++;
        }
        while (curB != null){
            curB = curB.next;
            lengthB++;
        }
        curA = headA;
        curB = headB;
        if (lengthA > lengthB){
            for (int i = 0; i < lengthA - lengthB; i++) {
                curA = curA.next;
            }
        }else if (lengthA < lengthB){
            for (int i = 0; i < lengthB - lengthA; i++) {
                curB = curB.next;
            }
        }
        while (curA != null){
            if (curA == curB){
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }

        return null;
    }
}

142. 环形链表II

思路:这道题看了视频才恍然大悟。主要是判断是否有环如何找到环的入口两个问题。

  • 判断是否有环:我们定义快慢两个指针,快指针每次走两个结点,慢指针每次走一个结点,因为快指针相对于慢指针是在一个一个结点地追慢指针,所以有环闭相遇。
  • 如何找到环的入口:找到快慢指针相遇的结点,这个相遇结点和头结点离入口的距离是相等的。再定义两个指针分别从相遇和头结点出发,找到入口即可。至于为什么相等,需要画图进行数学证明,可以再看视频研究。
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        ListNode index1, index2;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast){
                index1 = head;
                index2 = fast;
                while (index1 != index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

链表总结

链表的相关问题,通用的方法有虚拟头结点,可以很大程度方便了我们对于头结点的处理。
双指针是必须掌握的,熟练用快慢指针,进行题目的拆解分析。
递归也是常用的,相较于双指针比较难理解。
还有链表的一些基本操作,核心要领:对于单链表而言,如果要添加或者删除某个结点,我们的指针一定要指向这个结点的前一个结点。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值