Leetcode刷题python版第一周

刷题网址:https://leetcode.cn/problemset/

Day2

LeetCode 26、删除有序数组中的重复项

给你⼀个 升序排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现⼀次 ,返回删除后数组的新⻓度。元素的相对顺序应该保持⼀致 。

由于在某些语⾔中不能改变数组的长度,所以必须将结果放在数组nums的第⼀部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插⼊ nums 的前 k 个位置后返回 k 。

不要使⽤额外的空间,你必须在 原地 修改输⼊数组并在使⽤ O(1) 额外空间的条件下完成。

示例:

代码:

python 复制代码
def removeDuplicates(nums):
    if not nums:             #空列表为flase,not flase则为ture
        return 0
    slow = 0                # 仅2个int变量,固定空间
    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]
    return slow + 1

标准快慢指针解法:

slow:慢指针,指向去重后有效数组的最后一位下标

fast:快指针,逐个遍历原数组每一个元素

只开了两个 int 变量,没有新建列表,满足原地修改要求。

LeetCode 27、移除元素

给你⼀个数组 nums 和⼀个值 val ,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新⻓度。

不要使⽤额外的数组空间,你必须仅使⽤ O(1) 额外空间并 原地 修改输⼊数组。

元素的顺序可以改变。你不需要考虑数组中超出新⻓度后⾯的元素。

示例:

代码:

python 复制代码
def removeElement(nums, val):
    slow = 0
    for fast in range(len(nums)):
        if nums[fast] != val:
            nums[slow] = nums[fast]
            slow += 1
    return slow

同样快慢指针解法。

LeetCode 283、移动零

给定⼀个数组 nums ,编写⼀个函数将所有 0 移动到数组的末尾,同时保持⾮零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进⾏操作。

示例:

代码:

python 复制代码
class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        slow=0
        for fast in range(0,len(nums)):
            if nums[fast]!=0:
                nums[slow]=nums[fast]
                slow+=1
        for i in range(slow,len(nums)):
            nums[i]=0

同样快慢指针解法。

LeetCode 485、最⼤连续 1 的个数

给定⼀个⼆进制数组 nums , 计算其中最⼤连续 1 的个数。

python 复制代码
class Solution(object):
    def findMaxConsecutiveOnes(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        i = 0
        max_len = 0
        for j in range(len(nums)):
            if nums[j] == 1:
                i += 1
            else:
                # 碰到0,更新最大值,重置计数器
                if i > max_len:
                    max_len = i
                i = 0
        # 循环走完,最后一串连续1单独比较一次
        if i > max_len:
            max_len = i
        return max_len

难度不大,稍微想想就能解。

Day3

LeetCode 160、相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0

listA - 第一个链表

listB - 第二个链表

skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数

skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

python 复制代码
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        pA, pB = headA, headB
        while pA != pB:
            # pA走完A就切到B头;pB走完B就切到A头
            pA = pA.next if pA else headB
            pB = pB.next if pB else headA
        return pA

采用双指针拼接遍历思路求解相交链表,初始化指针 pA、pB 分别指向两条链表表头 headA、headB。两指针同步向后逐个移动节点,若 pA 遍历完链表 A 走到空,就跳转至链表 B 表头继续遍历;pB 遍历完链表 B 走到空,则跳转至链表 A 表头继续遍历。由于两指针走过的总路程都等于两条链表长度之和,路程完全相等,若链表存在相交节点,二者必然会在相交起始节点处相遇,循环终止并返回该节点;若两链表无交点,两个指针会同时走到空节点,相等后退出循环,最终返回 None。

全程仅使用两个指针,原地遍历不改动原链表结构。

LeetCode 19、删除链表的倒数第 N 个结点

给你⼀个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

代码:

python 复制代码
class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: Optional[ListNode]
        :type n: int
        :rtype: Optional[ListNode]
        """
     # 添加表头
        dummy = ListNode(-1)   #创建一个新节点
        dummy.next = head       #新节点指向表头

        # 寻找需要删除的节点节点
        cur = head

        # 指针 latter 指向虚拟头结点
        latter = dummy

        former = head

        # 让 former 指针先向前走 n 步
        for i in range(n):
            # former 指针向后移动
            former = former.next

        # 接下来,让这两个指针 former 和 latter 同时向前移动,直到前指针 former 指向 NULL
        while former is not None:
            # former 指针向后移动
            former = former.next

            # latter 来到 cur 的位置
            latter = cur

            # cur 指针向后移动
            cur = cur.next

        # 删除 cur 这个位置的节点
        latter.next = cur.next

        # 返回虚拟头结点的下一个结点
        return dummy.next   

用快慢双指针结合虚拟头结点实现链表倒数第 n 个节点删除。先新建虚拟头结点 dummy 并指向原链表表头,规避删除首节点的边界特殊处理;former 快指针先行向后移动 n 步,和后续慢指针组拉开固定间隔。之后 former、latter、cur 同步同步后移,直至 former 走到链表末尾 null,此时 cur 精准定位到待删节点,latter 停在它的前驱节点。修改 latter 的 next 指针直接跳过 cur 节点完成原地删除,最后返回 dummy.next,也就是新链表的真实表头,全程只移动指针不新建链表,空间复杂度 O (1),且不会改动原链表其余节点结构。

LeetCode 203、移除链表元素

给你⼀个链表的头节点 head 和⼀个整数 val ,请你删除链表中所有满⾜ Node.val == val 的节点,并返回新的头节点 。

代码:

python 复制代码
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: Optional[ListNode]
        :type val: int
        :rtype: Optional[ListNode]
        """
        dummy = ListNode(-1)
        dummy.next = head

        former = dummy
        latter = head

        while latter != None:
            if latter.val == val:
                former.next = latter.next
            else: 
                former = former.next
            latter = latter.next
        return dummy.next

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

给你⼀个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进⾏节点交换)。

两种解法

1-迭代法:

python 复制代码
class Solution(object):
    def swapPairs(self, head):
        """
        :type head: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
    
        # 虚拟头结点
        dummy = ListNode(0, head)
        pre = dummy
        
        # 保证后面至少有2个节点可以交换
        while pre.next and pre.next.next:
            # 取出要交换的两个节点
            first = pre.next
            second = pre.next.next
            
            # 三步完成交换
            pre.next = second       # 前驱指向第二个节点
            first.next = second.next# 第一个节点接上后续链表
            second.next = first     # 第二个节点指向第一个节点
            
            # pre移动到下一组的前驱(当前组的first位置)
            pre = first
        return dummy.next

图解:

2-递归法:

python 复制代码
class Solution(object):
    def swapPairs(self, head):
        """
        :type head: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        # 递归终止:空节点或只剩1个节点,直接返回
        if not head or not head.next:
            return head
        # 取出第二个节点
        second = head.next
        # 递归处理后续链表,作为第一个节点的后继
        head.next = self.swapPairs(second.next)
        # 第二个节点指向第一个节点,完成交换
        second.next = head
        return second

讲解下:

先标注节点:n1(1)、n2(2)、n3(3)、n4(4)

初始链表:n1 → n2 → n3 → n4

一、第一层调用:swapPairs (n1)

终止条件判断:

if not head or not head.next:

return head

head=n1,后面有n2,不触发返回,继续向下执行。

备份第二个节点:

second = head.next

second = n2,此时:second.next 天然指向 n3(后半段链表入口),这个原始引用必须先拿去递归,不能提前覆盖。

关键一行:先递归处理后半段:

head.next = self.swapPairs(second.next)

等价:n1.next = swapPairs(n3)

程序暂停当前这一层代码,跳入新一层函数,去处理子链表 n3→n4。

二、第二层调用:swapPairs (n3)

终止条件:head=n3,后面有n4,继续执行。

second = n3.next → second = n4,second.next 是None。

n3.next = swapPairs(n4.next) → n3.next = swapPairs(None)

再次暂停,跳入第三层。

三、第三层调用:swapPairs (None)

触发终止条件 not head,直接返回 None。

回到第二层断点处继续执行。

第二层回溯执行

接收返回值:n3.next = None

现在 n3 后面为空。

交换本组两个节点:

second.next = head

n4.next = n3,本组链表变为:n4 → n3

  1. return second,向上一层返回本组新表头n4。

四、回到第一层断点继续执行

接收下层结果:n1.next = n4

现在n1已经接上了处理完毕的后半段 n4→n3。

交换当前组节点:

second.next = head

n2.next = n1

当前组拼接完成:n2 → n1 → n4 → n3

  1. return second,返回全局新表头n2。

最终结果:2→1→4→3,完全符合题目要求。

Day4

LeetCode 21、合并两个有序链表

将两个升序链表合并为⼀个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

代码:

python 复制代码
class Solution(object):
    def mergeTwoLists(self, list1, list2):
        # 虚拟头结点,用来统一链表开头插入逻辑
        dummy = ListNode()
        cur = dummy   # cur指向新链表末尾节点

        # 两个链表都没遍历完时循环比较
        while list1 and list2:
            if list1.val < list2.val:
                cur.next = list1
                list1 = list1.next
            else:
                cur.next = list2
                list2 = list2.next
            cur = cur.next  # 新链表尾指针后移

        # 剩下没遍历完的整条链表直接接到尾部
        cur.next = list1 if list1 else list2

        return dummy.next

没啥难度,不讲。

LeetCode 237、删除链表的节点

有⼀个单链表的 head ,我们想删除它其中的⼀个节点 node 。

给你⼀个需要删除的节点 node 。你将 ⽆法访问 第⼀个节点 head 。

链表的所有值都是 唯⼀的,并且保证给定的节点 node 不是链表中的最后⼀个节点。

删除给定的节点。注意,删除节点并不是指从内存中删除它。这⾥的意思是:

给定节点的值不应该存在于链表中。

链表中的节点数应该减少 1。

node 前⾯的所有值顺序相同。

node 后⾯的所有值顺序相同。

代码:

python 复制代码
class Solution(object):
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next

这题怎么说呢?秀一张评论区神评吧:

LeetCode 328、奇偶链表

给定⼀个单链表,把所有的奇数节点和偶数节点分别排在⼀起。请注意,这⾥的奇数节点和偶数节点指的是节点编号的奇偶性,⽽不是节点的值的奇偶性。

请尝试使⽤原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

代码:

python 复制代码
class Solution(object):
    def oddEvenList(self, head):
        """
        :type head: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
          # 边界:空链表或只有1个节点,直接返回
        if not head or not head.next:
            return head
        
        odd = head               # 奇数链尾
        even = head.next          # 偶数链尾
        even_head = even          # 保存偶数链开头
        
        # 循环条件:even后面还有节点,才有下一个奇数节点可处理
        while even and even.next:
            # 刷新奇数的下一个,移动odd指针
            odd.next = even.next
            odd = odd.next
            # 刷新偶数的下一个,移动even指针
            even.next = odd.next
            even = even.next
        
        # 把偶数整体接到奇数链末尾
        odd.next = even_head
        return head      

思路:奇节点串一块,偶节点串一块,然后偶节点的头接上奇节点最后一个。

LeetCode 92 、反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表 。

python 复制代码
class Solution(object):
    def reverseBetween(self, head, left, right):
        """
        :type head: Optional[ListNode]
        :type left: int
        :type right: int
        :rtype: Optional[ListNode]
        """
        if right == left:
            return head
        else:
            # 虚拟头结点,处理left=1的特殊情况
            dummy = ListNode(-1)
            dummy.next = head
            pre = dummy
        
            # 第一步:走到left的前一个节点
            for _ in range(left - 1):
                pre = pre.next
        
            # 第二步:截取要反转的区间起点start,以及遍历用的cur
            start = pre.next
            cur = start
            # 需要反转right-left+1个节点
            prev = None
            for _ in range(right - left + 1):
                nxt = cur.next
                cur.next = prev
                prev = cur
                cur = nxt
        
            # 第三步:拼接前后
            pre.next = prev    # pre接上反转后的新头prev
            start.next = cur   # 原来的区间尾start接上后面剩余的节点
        
        return dummy.next

思路,先判断left是不是等于right,是则不用任何操作。然后同样是双指针法,先设置虚拟表头dummy,让pre指针到需要反转节点的前一个,反转完成后让pre接反转后那段的表头。记录pre.next(需要反转的第一个节点)为start,因为后续start要接right节点的后一个节点(即for循环后的cur)。while则是将需要反转段的链表反转,最后cur落在right节点的后一个节点,prev则是反转区间的表头。最后两句则是让pre.next为反转区间表头,start为反转区间的表尾指向要接right节点的后一个节点(即for循环后的cur)。完成操作!

相关推荐
Cthy_hy1 小时前
斯特林数:组合划分的递归经典,一二两类全解
python·算法·斯特林数
青春:一叶知秋1 小时前
【Python】python基本语法和使用
开发语言·python
不忘不弃1 小时前
计算pi的近似值
算法
码云骑士1 小时前
12-GIL不是性能杀手(下)-绕过GIL的三种方案与决策树
算法·决策树·机器学习
一只齐刘海的猫1 小时前
【Leetcode】无重复字符的最长子串
算法·leetcode·职场和发展
SilentSamsara1 小时前
向量数据库实战:Chroma/Milvus/Qdrant 选型与语义搜索应用
开发语言·数据库·人工智能·python·青少年编程·milvus
行智科技1 小时前
FAST-LIVO2 源码精读(二):环境搭建与编译避坑
算法·ubuntu·自动驾驶·slam
插件开发2 小时前
vs2015 cuda c++ cdpSimplePrint范例,递归功能实现演示
linux·c++·算法
Tisfy2 小时前
LeetCode 2130.链表最大孪生和:转数组 / 快慢指针+链表翻转(O(1))
算法·leetcode·链表·题解