代码随想录第 4 天 - 24 两两交换链表中的节点 & 19 删除链表的倒数第N个节点 & 02.07 链表相交 & 142 环形链表 II

链表

24 两两交换链表中的节点

原题链接

解题思路:

题解:

递归法

时间复杂度:O(n):操作 n 个节点
空间复杂度:O(n):递归栈深度为 n

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode node2 = head.next;
        ListNode next = node2.next;
        node2.next = head;
        head.next = swapPairs(next);
        return node2;
    }
}

迭代法

时间复杂度:O(n):操作 n 个节点
空间复杂度:O(1)

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        while (cur.next != null && cur.next.next != null) {
            ListNode node1 = cur.next;
            ListNode node2 = cur.next.next;
            ListNode next = node2.next; // 保存后面节点
            cur.next = node2;   // 开始交换
            node2.next = node1;
            node1.next = next;
            cur = node1;    // 更新 cur 下标
        }
        return dummyHead.next;
    }
}

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

原题链接

解题思路:
快慢指针,链表常用思路。让快指针先走 n 步或 n + 1 步(取决于你终止条件怎么写),然后快慢指针一起走。慢指针指向的就是倒数第 n 个节点。

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode slow = dummyHead;
        ListNode fast = dummyHead;
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return dummyHead.next;
    }
}

02.07 链表相交

原题链接

解题思路:
很经典的题目,自己第一次去想有点难。核心就是二者如果相交的话,它们的后半部分一定是一样的。将两条链表拉直,如果长度不一致,则交点至少是从较短链表的头节点开始。所以可以先将长链表节点遍历到较短链表头节点的长度位置。然后二者再一起向后遍历,如果二者相等则说明相交,直接返回。

题解:
时间复杂度:O(n + m):链表A + 链表B 的长度
空间复杂度:O(1)

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) return null;
        int lengthA = 1;
        int lengthB = 1;
        ListNode temp1 = headA;
        ListNode temp2 = headB;
        // 将两个节点都遍历到最后一个
        while (temp1.next != null) {
            temp1 = temp1.next;
            lengthA++;
        }
        while (temp2.next != null) {
            temp2 = temp2.next;
            lengthB++;
        }
        // 如果两个节点到最后一个节点都不相等的话则二者不相交
        if (temp1 != temp2) return null;

        // 让 A 始终为较短的那个
        if (lengthA > lengthB) {
            // 将二者长度交换
            int tempLen = lengthA;
            lengthA = lengthB;
            lengthB = tempLen;
            // 将二者节点交换
            ListNode tempNode = headA;
            headA = headB;
            headB = tempNode;
        }
        // 计算二者长度差
        int gap = lengthB - lengthA;
        // 双方从各自的倒数第n个开始向后遍历
        while (gap-- > 0) {
            headB = headB.next;
        }
        while (headA != headB) {
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }
}

142 环形链表II

原题链接

解题思路:
这道题需要自己在草稿纸上模拟下,然后进行计算。

  1. 首先要判断是否有环,可以用快慢指针来解决。快指针每次移两个位置,慢指针每次移一个位置。二者如果能够相等则证明有环,或者快指针指向空了,也能证明无环。
  2. 其次就是要判断环的入口了。这里直接参考 Carl 的计算过程。核心就是快指针速度是慢指针的两倍,然后始终记得要求的是入口,所以 当 x = z 证明出来的时候就可以让两个指针一块移动了。

题解:
时间复杂度:O(n)
空间复杂度:O(1)

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
        	// 设置快慢指针,二者相对速度为1
            fast = fast.next.next;
            slow = slow.next;
            // 二者相遇则证明存在环
            if (fast == slow) {
            	// 通过数学计算,二者此时到交点的距离相等
                while (slow != head) {
                    slow = slow.next;
                    head = head.next;
                }
                return head;
            }
        }
        // 如果能遍历到null则证明无环
        return null;
    }
}

今日心得

链表这些题都是技巧性很强,也都是需要熟练掌握。多刷,默写就完了。

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值