力扣刷题|24. 两两交换链表中的节点、19.删除链表的倒数第 N 个节点、面试题 02.07. 链表相交、142.环形链表 II

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

题目链接🔗:LeetCode 24. 两两交换链表中的节点

思路

  1. 创建一个结点cur,cur的下一个结点指向要交换的两个结点的第一个结点
  2. 创建firstnode和secondnode分别用来存储要交换的两个结点的第一个结点和第二个结点
  3. 创建一个temp结点指向下一次要交换的两个结点的第一个结点
  4. 步骤一:让cur的下一个结点指向secondnode
  5. 步骤二:让secondnode的下一个结点指向firstnode
  6. 步骤三:让firstnode的下一个结点指向temp
    在这里插入图片描述

代码实现

class ListNode{
    int val;
    ListNode next;

    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

public ListNode swapPairs(ListNode head) {
        ListNode temp; //temp用来保存下两个结点中的第一个结点
        ListNode ahead = new ListNode(-1);
        ahead.next = head;
        ListNode cur = ahead;
        ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
        ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点

        while (cur.next != null && cur.next.next != null) {
            temp = cur.next.next.next;
            firstnode = cur.next;
            secondnode = cur.next.next;
            cur.next = secondnode;
            secondnode.next = firstnode;
            firstnode.next = temp;
            cur = firstnode;  //cur移动,准备下一轮交换
        }
        return ahead.next;
    }

时间复杂度:O(n)

使用递归实现:

public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode next = head.next;
        //递归
        ListNode newNode = swapPairs1(next.next);

        //进行交换
        next.next = head;
        head.next = newNode;
        return next;
    }

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

题目链接🔗:LeetCode 19.删除链表的倒数第 N 个节点

思路

整体思路是:定义fast指针和slow指针,初始值为虚拟头结点(指向head的前一个结点),fast首先走 n+1 步,然后fast和slow同时移动,直到fast指向末尾,此时slow就会指向要删除结点的前一个结点,如下图:
在这里插入图片描述
那么会什么会这样呢?下面是推导步骤

  • 如果我们用sum表示链表的长度,设一个指针一开始指向虚拟头结点,那么让此指针移动到要删除结点的前一个结点,那么指针需要向前移动 sum-n 步
  • 采用快慢指针的方法实现,首先需要创建一个虚拟的头结点,让fast和slow的下一个结点都指向head
  • 首先让fast向前移动 m 步,那么距离fast移动到链表末尾的后一位还需要 sum+1-m 步
  • 要是slow移动至要删除元素的前一位
  • 那么就需要 sum+1-m = sum-n
  • 得到 m=n+1

代码实现

class ListNode{
    int val;
    ListNode next;

    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode ahead = new ListNode(0);
        ahead.next = head;
        ListNode fast = ahead;
        ListNode slow = ahead;

        //先将fast向前移动n+1步
        for (int i = 0; i < n + 1; i++) {
            fast = fast.next;
        }

        //同时移动fast和slow
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }

        //删除slow的下一个结点
        slow.next = slow.next.next;
        return ahead.next;
    }

LeetCode 面试题 02.07. 链表相交

题目链接🔗:面试题 02.07. 链表相交

思路

刚拿到题目是第一种想法就是双层循环,然后逐个比较结点是否相同(注意这里比较的不是结点内的值是否相同,而是比较结点的地址)
其实也可以用双指针的解法:

  • 目前curA指向链表A的头结点,curB指向链表B的头结点
    在这里插入图片描述
  • 先求出两个链表的长度,然后求出两个链表长度的差值,然后让链表A与链表B末尾对齐,curA移动到与curB对齐的位置
    在这里插入图片描述
  • 此时开始就可以比较curA与curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点,否则返回null

代码实现

直接方法

class ListNode{
    int val;
    ListNode next;

    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode temp;
        while(headA != null){
            temp = headB;
            while (temp != null){
                if (headA == temp){
                    return headA;
                }
               temp = temp.next;
            }
            headA = headA.next;
        }
        return null;
    }

时间复杂度:O(mn)
双指针法

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int countA = 0;
       int countB = 0;
       ListNode  curA = headA;
       ListNode  curB = headB;

       //求链表A的长度
       while ( curA != null){
           countA++;
            curA =  curA.next;
       }

       //求链表B的长度
        while ( curB != null){
            countB++;
             curB =  curB.next;
        }

        int n = Math.abs(countA - countB);

        curA = headA;
        curB = headB;

        if (countA > countB){
            //移动curA
            for (int i = 0; i < n; i++) {
                curA = curA.next;
            }
        }else if (countB > countA){
            //移动curB
            for (int i = 0; i < n; i++) {
                curB = curB.next;
            }
        }

        //比较
        while (curA != null){
            if (curA == curB){
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }

        return null;
    }

时间复杂度:O(m+n)

LeetCode 142.环形链表 II

题目链接🔗:LeetCode 142.环形链表 II

思路

本题主要有两个关键点

  • 判断链表是否有环
  • 怎么找到这个环的入口
判断链表是否有环

可以使用快慢指针法,快指针以每次移动两步的速度前进,慢指针以每次移动一步的速度前进,如果链表没有环两个指针不可能相遇,如果两个指针相遇表示链表一定有环
但是你可能还会想到快指针会不会存在跳过慢指针的情况?
这种情况是不存在的,因为快指针相对于慢指针的速度是一步,就是快指针以每次一步的速度接近慢指针,因此如果有环两个指针一定会相遇并且是在环中相遇

找到这个环的入口

假设从头结点到环形入口节点 的节点数为x, 环形入口节点到 fast指针与slow指针相遇节点 节点数为y,从相遇节点 再到环形入口节点节点数为 z, 如图所示:
在这里插入图片描述

  • 相遇时慢指针需要走 x+y
  • 快指针需要走 x+y+n(y+z) (n表示相遇时快指针在环中转了多少圈)
  • 因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2
  • 因此存在 (x + y) * 2 = x + y + n (y + z) (这里的n一定是大于等于一的,因为 fast指针至少要多走一圈才能相遇slow指针)
  • x = n (y + z) - y
  • x = (n - 1) (y + z) + z
  • 从式子中可以得到以下结论:头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

代码实现

public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow){
                ListNode temp = head;
                while (temp != slow){
                    temp = temp.next;
                    slow = slow.next;
                }
                return temp;
            }
        }
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值