力扣算法题笔记——链表简单

876. 链表的中间结点

给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
示例 1:  输入:[1,2,3,4,5]  输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:  输入:[1,2,3,4,5,6]  输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
首先计算链表有多少个节点,然后可以计算出中间节点的位置。最终返回这个指针就好

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        cur=head
        count=0
        while cur!=None:#循环计算链表长度
            count+=1
            cur=cur.next
        mid=count//2#计算中间节点的位置
        i=0
        p=head
        while i<mid:#再次遍历直到目标节点
            p=p.next
            i+=1
        return p

官方答案:
快慢指针法:慢指针一次向右走一个,快指针一次走两个,这样每一次循环,快指针所在的节点位置都是慢指针的两倍,当快指针走到终点时,慢指针刚好位于中间节点

class Solution(object):
    def middleNode(self, head):
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow

206. 反转链表

反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路:对于是空链表的情况,单独处理,返回原链表
  对于非空链表,设置一个指针,从前往后遍历,每次递归记录上一次的结点,用当前结点的元素构造一个单独的结点,指向上一次的结点。

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head==None:
            return head
        cur=head
        last=None
        while cur!=None:
            now=ListNode(cur.val)
            now.next=last
            last=now
            cur=cur.next
        return now
        

83.删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2  输出: 1->2
示例 2:
输入: 1->1->2->3->3  输出: 1->2->3
双指针(快慢指针法):
   错误点,我第一次写的代码,没有照顾到

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if head==None:
            return head
        pre,cur=head,head.next
        while cur:#快指针不为空的情况
            if cur.val==pre.val:
                cur=cur.next#让前一个结点直接指向下一个结点。断掉前一个节点与本节点的连续。
                pre.next=cur
            else:
                pre=cur
                cur=cur.next
        return head

我的错误:链表基础薄弱:①不了解对于一个链表,如果有个指针指向它,指针向后走的过程中如果对链表的某个节点做了改动,那么最开始的链表就会被改动,最终只需要返回头结点即可。②忘记了删除节点的方法就是让该节点的上一个结点指向该结点的下一个结点。而我是让指针直接跳过了该结点,这样做对链表没有做任何改动。

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
解释:链表中没有环。
在这里插入图片描述  在这里插入图片描述  在这里插入图片描述
     示例1           示例2     示例3
超笨的方法。时间复杂度很高,但是这是为想了很久想到的:利用一个列表存储每个结点,对链表进行遍历的过程中,每次判断该结点是否与曾经的某个节点相同,如果相同则是环形链表,如果遍历结束都不存在,说明不是环形链表

class Solution(object):
    def hasCycle(self, head):
        if head== None:#
            return False
        if head.next==head:
            return True
        
        cur=head
        p=[]
        while cur:
            if cur not in p:
                p.append(cur)
                cur=cur.next
            else:
                return True
        return False

简化版

class Solution(object):
    def hasCycle(self, head):      
        cur=head
        p=[]
        while cur:
            if cur not in p:
                p.append(cur)
                cur=cur.next
            else:
                return True
        return False

别人的方法:
好像两个人在一个操场上跑步,速度快的人一定会和速度慢的相遇(环),虽然可能走好几圈才会相遇,但是迟早会相遇,空间复杂度低。

def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False
作者:powcai
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/kuai-man-zhi-zhen-by-powcai-3/

160.相交链表

编写一个程序,找到两个单链表相交的起始节点。如下面的两个链表:
在这里插入图片描述
输入: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 个节点。
自己的方法:哈希表,遍历B链表中的所有节点,存储在哈希表中,然后遍历B链表,将B链表中的节点与哈希表中的节点对比。

其实我没学过哈希表,但是看起来和列表相似。我最开始用的是列表,遍历其中一个链表的所有节点,存储到列表中,但是运行出来的结果显示时间超过限制,将列表换成哈希表之后,问题得到解决,暂时不知道原理。

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        item=set()#创建哈希表
        p1=headA
        while p1:#存储A链表中的所有节点
            item.add(p1)
            p1=p1.next
        p2=headB
        while p2:
            if p2 in item:
                return p2
            p2=p2.next
        return None

大牛的方法:拼接法
如下图所示,当链表A走到结尾时,将结尾指向链表B的头部。
对B链表做相同处理,那么两者连接成的环形链表是一样的。从任何一个链表的头部到两者相交的节点的长度相同。因此会同步到达相交节点(但我觉得这其中存在一个问题,链表的结构被改变了)

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        ha, hb = headA, headB
        while ha != hb:
            ha = ha.next if ha else headB
            hb = hb.next if hb else headA
        return ha
作者:jyd
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/intersection-of-two-linked-lists-shuang-zhi-zhen-l/

在这里插入图片描述   在这里插入图片描述

203. 移除链表元素

删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
双节点法,只有一个节点以及链表为空是两种特殊情况,

class Solution(object):
    def removeElements(self, head, val):
        if head==None:
            return head
        pre,cur=head,head
        while cur:#不包含单节点的情况
            if cur.val==val:
                pre.next=cur.next#不能处理头结点是目标结点的情况
                cur=cur.next
            else:
                pre=cur
                cur=cur.next
        if head.val==val:#头结点是目标结点的情况
            return head.next
        return head

虚拟指针法:在head的前面加一个结点。这样就可以包含空链表或头结点的值就是目标值的情况

class Solution(object):
    def removeElements(self, head, val):
        dummy=ListNode(0)
        pre=dummy
        pre.next=head
        cur=pre.next
        while cur:#没有检查第一个节点
            if cur.val==val:
                pre.next=cur.next
                cur=cur.next
            else:
                pre=cur
                cur=cur.next
        return dummy.next

21. 合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
    	#if l1 is None:
    	#	return l2
    	#elif l2 is None:
    	#	return l1
        p1=l1
        p2=l2
        l=ListNode(0)#先构造一个虚拟结点
        cur=l
        while p1 and p2:
            if p1.val>p2.val:
                cur.next=p2
                p2=p2.next
                cur=cur.next
            else:
                cur.next=p1
                p1=p1.next
                cur=cur.next
        '''下面是考虑到两个链表长度不同的情况'''
        if p1:#第一个链表更长的情况
            cur.next=p1
        else:#第二个链表更长或者两者相同的情况
            cur.next=p2            
        return l.next#虚拟结点的头结点是无效的,要省去

234. 回文链表

请判断一个链表是否为回文链表。
示例 1:
输入: 1->2   输出: false
示例 2:
输入: 1->2->2->1  输出: true
回文链表指的是无论按照从前往后还是从后往前,读出来的都一样
思路:对于只有一个结点或空链表的情况单独处理,对于其他的链表,遍历每个结点存储到一个列表中(本来想用哈希表的,但是不知道怎么索引哈希表中的元素),之后再双向遍历列表中的结点。在遍历的过程中,如果存在两个结点对应的值不同就结束遍历,返回False。遍历结束如果还没有遇到不相等的情况,就返回True。

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if head is None:
            return True
        if head.next is None:
            return True
        p=[]#构造空列表,用于存储各个结点
        cur=head
        while cur:
            p.append(cur)
            cur=cur.next
        n=len(p)
        i=0
        j=n-1
        while i<=j:
            if p[i].val!=p[j].val:
                return False
            i+=1
            j-=1
        return True

利用反转链表的方法:

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if head is None:
            return True
        if head.next is None:
            return True
        cur=head
        last=None
        while cur!=None:
            now=ListNode(cur.val)
            now.next=last
            last=now
            cur=cur.next
        
        while head:
            if head.val!=now.val:
                return False
            head=head.next
            now=now.next
        return True

对比发现,第一种方法的时间复杂度可空间复杂度都更低

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值