LeetCode刷题(十七)-----链表-------easy部分(Java、C++)

160. 相交链表

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

在这里插入图片描述
在节点 c1 开始相交。
示例 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 个节点。

示例 2:
在这里插入图片描述
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
在这里插入图片描述
输入:intersectVal = 0, listA = [2,6,4],listB =[1,5],skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

思路一:
方法一: 暴力法
对链表A中的每一个结点ai,遍历整个链表B并检查链表B中是否存在结点和ai相同。
复杂度分析
• 时间复杂度 : (mn)。
• 空间复杂度 : O(1)。
方法二: 哈希表法
遍历链表A并将每个结点的地址/引用存储在哈希表中。然后检查链表 B 中的每一个结点 bi是否在哈希表中。若在,则 bi为相交结点。
复杂度分析
• 时间复杂度 : O(m+n)。
• 空间复杂度 : O(m)或O(n)。

方法三:双指针法
1.创建两个指针pA和pB,分别初始化为链表 A 和 B 的头结点。然后让它们向后逐结点遍历。
2.当pA到达链表的尾部时,将它重定位到链表 B的头结点 (你没看错,就是链表 B); 类似的,当 pB到达链表的尾部时,将它重定位到链表 A 的头结点。
3.若在某一时刻 pA和pB 相遇,则 pA/pB为相交结点。
4.想弄清楚为什么这样可行,可以考虑以下两个链表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于结点 9。由于 B.length (=4) < A.length (=6),pB比 pA少经过 2个结点,会先到达尾部。将 pB重定向到 A 的头结点,pA 重定向到 B 的头结点后,pB要比 pA多走 2 个结点。因此,它们会同时到达交点。
如果两个链表存在相交,它们末尾的结点必然相同。因此当pA/pB到达链表结尾时,记录下链表 A/B 对应的元素。若最后元素不相同,则两个链表不相交。
复杂度分析
• 时间复杂度 : O(m+n)O(m+n)。
• 空间复杂度 : O(1)O(1)。
方法三很好理解,就是a + all +b = b + all + a
all 是两个链表相交部分的个数, a 是 A 链表不相交的个数, b 是 B 链表不相交的个数.

作者:LeetCode
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/xiang-jiao-lian-biao-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:
解法一
思路
暴力法,快慢指针,一个指针在A链表,一个指针在B链表,逐一遍历两个链表,比较节点的位置,若位置相交,返回任意一个地址(两者指向位置相同) 这个方法比较容易想到,但是使用了两个while循环,时间复杂度非常高O(n^2)。
在这里插入图片描述
解法二
思路
这个方法比较巧妙,时间复杂度较低O(n)
1.假设两条链表有交点,可知相交部分等长
2.那么交点位置距离链表尾的距离必小于等于较短的链表
3.先将较长的链表剪去前面部分,使其的长度等于较短的链表
4.此时将指针从当前的headA 和headB同时向后移动,且对比指针是否相同,若相同则输出指针。
在这里插入图片描述
作者:youlookdeliciousc
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/cxiang-xi-ti-jie-by-youlookdeliciousc-12/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我的:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        if(!headA || !headB)
        {
            return nullptr;
        }    
        ListNode *countA = headA;
        ListNode *countB = headB;
        int longA = 0;
        int longB = 0;
        while(countA)
        {
            ++longA;
            countA = countA -> next;
        }
        while(countB)
        {
            ++longB;
            countB = countB -> next;
        }
        int s = max(longA,longB) - min(longA,longB);
        if(longA > longB)
        {
            for(int i = 0;i < s;i++)
            {
                headA = headA -> next;
            }
        }
        else
        {
            for(int i=0;i<s;i++)
            {
                headB = headB -> next;
            }
        }
        while(headA)
        {
            if(headA == headB)
            {
                return headA;
            }
            headA = headA -> next;
            headB = headB -> next;
        }
        return nullptr;
    }
};

141. 环形链表

给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是-1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

在这里插入图片描述
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
在这里插入图片描述
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
在这里插入图片描述
进阶:你能用O(1)(即,常量)内存解决此问题吗?
思路一:
方法一:哈希表

思路
我们可以通过检查一个结点此前是否被访问过来判断链表是否为环形链表。常用的方法是使用哈希表。
算法
我们遍历所有结点并在哈希表中存储每个结点的引用(或内存地址)。如果当前结点为空结点 null(即已检测到链表尾部的下一个结点),那么我们已经遍历完整个链表,并且该链表不是环形链表。如果当前结点的引用已经存在于哈希表中,那么返回 true(即该链表为环形链表)。
在这里插入图片描述
复杂度分析
时间复杂度:O(n),对于含有n个元素的链表,我们访问每个元素最多一次。添加一个结点到哈希表中只需要花费 O(1)的时间。
空间复杂度:O(n),空间取决于添加到哈希表中的元素数目,最多可以添加 n个元素。

方法二:双指针
思路

想象一下,两名运动员以不同的速度在环形赛道上跑步会发生什么?
算法

通过使用具有 不同速度 的快、慢两个指针遍历链表,空间复杂度可以被降低至O(1)。慢指针每次移动一步,而快指针每次移动两步。
如果列表中不存在环,最终快指针将会最先到达尾部,此时我们可以返回 false。

现在考虑一个环形链表,把慢指针和快指针想象成两个在环形赛道上跑步的运动员(分别称之为慢跑者与快跑者)。而快跑者最终一定会追上慢跑者。这是为什么呢?考虑下面这种情况(记作情况 A)- 假如快跑者只落后慢跑者一步,在下一次迭代中,它们就会分别跑了一步或两步并相遇。
其他情况又会怎样呢?例如,我们没有考虑快跑者在慢跑者之后两步或三步的情况。但其实不难想到,因为在下一次或者下下次迭代后,又会变成上面提到的情况 A。
在这里插入图片描述
复杂度分析
时间复杂度:O(n),让我们将n设为链表中结点的总数。为了分析时间复杂度,我们分别考虑下面两种情况。
链表中不存在环:
快指针将会首先到达尾部,其时间取决于列表的长度,也就是 O(n)O(n)。
链表中存在环:
我们将慢指针的移动过程划分为两个阶段:非环部分与环形部分:

1.慢指针在走完非环部分阶段后将进入环形部分:此时,快指针已经进入环中迭代次数=非环部分长度=N
2.两个指针都在环形区域中:考虑两个在环形赛道上的运动员 - 快跑者每次移动两步而慢跑者每次只移动一步。其速度的差值为 1,因此需要经过,,,循环后,快跑者可以追上慢跑者。这个距离几乎就是 “\text{环形部分长度 K}环形部分长度 K” 且速度差值为 1,我们得出这样的结论迭代次数=近似于 “环形部分长度 K”.

因此,在最糟糕的情形下,时间复杂度为 O(N+K),也就是 O(n)。
空间复杂度:O(1),我们只使用了慢指针和快指针两个结点,所以空间复杂度为 O(1)。

作者:LeetCode
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:
方法三:快慢指针

初始化 slow = head->next,每次走一步
初始化 fast =head->next->next,每次走两步,每走一步判断一次
存在环 fast 和 slow 会相遇
在这里插入图片描述
作者:chenlele
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-gpe3dbjds1/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) 
    {
        if(head == NULL || head->next == NULL)
        {
            return false;
        }  
        ListNode *slow = head;
        ListNode *fast = head -> next;
        while(fast)
        {
            slow = slow -> next;
            for(int i = 0;i<2;i++)
            {
                if(fast -> next == NULL)
                {
                    return false;
                }
                fast = fast -> next;
                if(fast == slow)
                {
                    return true;
                }
            }
        }
        return false;
    }
};

234. 回文链表

请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

思路一:
思路:

此题基本思路就是讲链表分为两段,并且把前面一段进行倒置,然后再比较前后两段是否相同即可,最简单的方法就是先计算链表的长度,然后将前面一半用头插法的办法进行倒置,最后再比较,但是由于需要的是一半这个特殊的位置,所以我们可以考虑快慢指针,何谓快慢指针,其实就是两个指针,一根指针一次移动一个位置,另一个指针一次移动两个位置,就好比两个人走路一个速度为v,一个速度为2v,当速度快的人走到底了设长度为x,这个速度快的人所话的时间就为x/(2v)而对于相同的时间而言对于那个走得慢的就走到的距离为d. d = v* t,而t为x/(2v),代入就为d = x/2所以当一个人走到底了另一个人正好走到了一半。所以快慢指针适合用于有中点相关的场景,同时为了更加简便,我们可以采用一边移动一边将其倒置,这个倒置的顺序只需要在图上画一画就凑出来了,同时需要注意链表个数为奇数和为偶数对于后一半的头结点位置有所不同。所以此题最关键就是可以在找到中点的同时就进行倒置了,也要记住有中点或倍数的时候可以考虑快慢指针来解决问题。
在这里插入图片描述
作者:heroine-yun
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/wei-shi-yao-yong-kuai-man-zhi-zhen-by-vailing/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:
整体思路是先用快慢指针找中点(慢指针的最终位置)
然后再反转中点指向的后半部分链表
前半部分再与后半部分逐元素比较

时间复杂度为O(n)
空间复杂度关键看反转链表是迭代还是递归,我这里用迭代所以是O(1),递归则是O(n)
为了行数,牺牲了代码可读性,可能看起来有点费劲…
在这里插入图片描述
作者:mei-de-gan-qing-de-fu-du-ya
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/cji-jian-xing-shu-jie-fa-by-mei-de-gan-qing-de-fu-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我的:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) 
    {
        if(!head || !head -> next)
        {
            return true;
        }
        ListNode*pre = NULL;
        ListNode*slow = head;
        ListNode*fast = head;
        ListNode*s = NULL;
        while(fast != NULL && fast -> next != NULL)
        {
            pre = slow;
            slow = slow -> next;
            fast =  fast -> next -> next;
            pre -> next = s;
            s = pre;
        }
        ListNode*temp = slow;
        if(fast != NULL)
        {
            temp = temp -> next;
        }    
        slow = pre;
        while(temp != NULL)
        {
            if(temp -> val != slow -> val)
            {
                return false;
            }
            else
            {
                temp = temp -> next;
                slow = slow -> next;
            }
        }
        return true;
    }
};

发布了47 篇原创文章 · 获赞 21 · 访问量 7225

猜你喜欢

转载自blog.csdn.net/qq_18315295/article/details/103774178