【Leetcode】三道双指针链表题

1. 返回倒数第K个结点

题目描述

题目链接:https://leetcode-cn.com/probl...

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

示例:
输入: 1->2->3->4->5 和 k = 2
输出: 4

快慢指针法

链表经典题,快慢指针,只需一次遍历。时间复杂度O(n),空间复杂度O(1)

int kthToLast(ListNode* head, int k) {
        ListNode* fast = head, *low = head;
        while(k--) {
            fast = fast -> next;
        }
        while(fast) {
            fast = fast -> next;
            low = low -> next;
        }
        return low -> val;
    }

2. 相交链表

题目描述

题目链接:https://leetcode-cn.com/probl...

编写一个程序,找到两个单链表相交的起始节点。

示例:
【Leetcode】三道双指针链表题_第1张图片
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

注:skipA和skipB仅做理解用,并不作为接口的参数

快慢指针

思路:由题目输入所给的提示可以看出,如果两个链表有相同的结点,那么在公共结点之前,两个链表分别有自己的子链表,而这两条子链表的长度差正好是两条链表的长度差(因为从公共结点开始两个链表都是相同的)。知道了这一点,就有了一个思路——假设两个链表长度差为diff,那么让指向较长链表的指针先走diff步,然后两个指针同时走,再消除了长度差后,两个指针必然同时走到公共结点(如果有的话)。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(!headA || !headB) return NULL;
        int len1 = 0, len2 = 0;
        ListNode *node1 = headA, *node2 = headB;
        while(node1) {
            node1 = node1 -> next;
            ++len1;
        }
        while(node2) {
            node2 = node2 -> next;
            ++len2;
        }
        int diff = len1 - len2;
        node1 = headA; node2 = headB;
        if(diff > 0) {
            while(diff--) {
                node1 = node1 -> next;
            }
        }
        else {
            while(diff++ < 0) {
                node2 = node2 -> next;
            }
        }

        while(node1 != node2) {
            node1 = node1 -> next;
            node2 = node2 -> next;
        }
        return node1;
    }

时间复杂度:显然,两个链表都只遍历了两次,时间复杂度为O(m+n)
空间复杂度:O(1)

巧解法

思路:巧解法思路的精髓在于,若两个链表长度为a,b,那么a+b=b+a。这就意味着,让两个链表的末尾都指向对方的开头,形成的两条新链表的长度是一致的。并且,如果两个链表有公共结点,那么形成的两条长链表的结尾必然有公共部分(因为两个原始链表本来就有公共部分,只是原来两个链表的长度可能不同,不好比较,但两条长链表)。画出两个长链表一目了然。基于这个思路,可以得到如下简洁代码:

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(!headA || !headB) return NULL;
        ListNode *node1 = headA, *node2 = headB;
        while(node1 != node2) {
            node1 = node1 ? node1 -> next : headB;
            node2 = node2 ? node2 -> next : headA; 
        }
        return node1;
    }

找到链表环入口

题目描述

题目链接:https://leetcode-cn.com/probl...

给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

【Leetcode】三道双指针链表题_第2张图片

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

剑指offer经典题,快慢指针。

快慢指针法

ListNode *detectCycle(ListNode *head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast != nullptr)
        {
            slow = slow->next;
            // 无环的情况
            if (fast->next == nullptr)
            {
                return nullptr;
            }
            fast = fast->next->next;
            // 找到相交点
            if (fast == slow)
            {
                // 作为新的起点继续走
                ListNode* curr = head;
                while (curr != fast)
                {
                    fast = fast->next;
                    curr = curr->next;
                }
                return curr;
            }
        }

        return nullptr;
    }

时间复杂度O(n), 空间复杂度O(1)。

附快慢指针为什么能成功的数学证明:https://leetcode-cn.com/probl...

你可能感兴趣的:(算法c++)