代码随想录算法训练营Day3
力扣203.移除链表元素、707.设计链表、206.反转链表
一、链表和数组的联系与区别
特性 | 数组 | 链表 |
---|---|---|
存储方式 | 连续存储 | 离散存储 |
随机访问 | O ( 1 ) O(1) O(1) | O ( n ) O(n) O(n) |
插入/删除 | O ( n ) O(n) O(n)头部、中间; O ( 1 ) O(1) O(1)尾部 | O ( 1 ) O(1) O(1)头部、中间,已知位置 |
内存分配 | 一次性分配连续内存 | 按需分配节点内存 |
内存开销 | 无额外开销 | 每个节点有指针开销 |
适用场景 | 随机访问频繁、数据大小固定 | 插入删除频繁、数据大小动态变化 |
二、力扣203-移除链表元素【easy】
题目链接:力扣203-移除链表元素
视频链接:代码随想录
1、思路
- 头节点和非头节点的删除处理方式不一致。
- 引入虚拟头节点,变成统一规则删除。
- 涉及到对链表修改(如插入、删除、移动),都可以使用虚拟头节点。
- 时间复杂度: O ( n ) O(n) O(n)。
2、代码
class Solution:
"""
创建虚拟节点,统一处理删除所有节点规则
时间复杂度:O(n)
"""
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummy_head = ListNode(next = head) #创建一个新的 ListNode 对象,并将指针next初始化为指向另一个链表节点(head)。
current = dummy_head
# 遍历列表并删除值为val的节点
while current.next: #只要当前节点的下一节点存在,循环将会继续遍历
if current.next.val == val:
current.next = current.next.next
else:
current = current.next
return dummy_head.next #返回的不是head,避免了如果head = val 的情况
3、代码的问题
ListNode
:创建一个新的节点,默认它的指针指向None
Optional[ListNode]
:表示head
可以是一个ListNode
类型的对象,也可以是None
(即链表为空)
三、力扣707-设计链表【medium】
题目链接:力扣707-设计链表
> 视频链接:代码随想录
1、思路
-
五种操作
-
获取下标为
index
的节点的值:get
-
头部插入一个值为
val
的节点:addAtHead
-
尾部插入一个值为
val
的节点:addAtTail
-
将一个值为
val
的节点插入到链表中下标为index
的节点之前:addAtIndex
-
删除下标为
index
的节:deleteAtIndex
-
-
index
是从0开始 -
当链表发生变化时,记得更新size值。
-
重点是插入、删除,这个时候需要找到被删除(或者是插入位置)元素的前驱元素,然后修改next的值。
2、代码
class MyLinkedList:
def __init__(self):
self.dummy_head = ListNode() #指向None
self.size = 0
"""
获取节点
"""
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
current = self.dummy_head.next #在链表非空的情况下,self.dummy_head.next 指向链表的第一个实际节点。
for i in range(index):
current = current.next
return current.val
"""
头部插入节点
"""
def addAtHead(self, val: int) -> None:
self.dummy_head.next = ListNode(val , self.dummy_head.next)#更改虚拟节点的指针;创建一个新的 ListNode 节点对象,其值为 val,next 指针指向当前虚拟头节点的下一个节点(即原来的链表头节点)
self.size +=1
"""
尾部插入节点
"""
def addAtTail(self, val: int) -> None:
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val)
self.size +=1
"""
将一个值为 val 的节点插入到链表中下标为 index 的节点之前
"""
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val , current.next)
self.size +=1
"""
删除下标为index的节点
"""
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next =current.next.next
self.size -= 1
3、代码问题
- 除了
get
其他四个的current
=dummy_head
;而
get
的current
=dummy_head.next
- 有
index
的时候要记得判断它是否合法
四、力扣206-反转链表
题目链接:力扣206-反转链表
视频链接:代码随想录
1、思路
2、代码
- 双指针法
"""
双指针法
时间复杂度:O(n)
"""
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
#Optional 用于明确表示某个变量或函数参数可以接受 None 或指定类型的值。
cur = head
pre = None
while cur: #循环的终止条件是cur=None
temp = cur.next #临时储存
cur.next = pre #反转
#是先赋值pre再赋值cur
pre = cur
cur = temp
return pre #最后pre是反转链表的头节点
- 递归法
"""
递归法
时间复杂度:O(n)
"""
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
return self.reverse(head,None) #入口调用启动了递归过程,逐步反转链表
def reverse(self,cur:Optional[ListNode],pre:Optional[ListNode]):
if cur == None:
return pre
temp = cur.next #临时储存
cur.next = pre #反转
return self.reverse(temp,cur)
3、代码问题
Optional
用于明确表示某个变量或函数参数可以接受 None 或指定类型的值。- 是先赋值
pre
再赋值cur
;顺序很重要 - 返回的是
pre
是反转链表的头节点