刷题网址: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
- return second,向上一层返回本组新表头n4。
四、回到第一层断点继续执行
接收下层结果:n1.next = n4
现在n1已经接上了处理完毕的后半段 n4→n3。
交换当前组节点:
second.next = head
n2.next = n1
当前组拼接完成:n2 → n1 → n4 → n3
- 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)。完成操作!