964 字
5 分钟
代码随想录算法训练营第三天 | 链表 Part01
链表基础
- 链表节点的存储是非连续的,每个节点包含一个指针域和一个数据域。
- 链表的缺点是访问元素的时间复杂度为 O(n),而数组可以在 O(1) 时间内访问任意元素。
- 链表的优点是插入和删除操作的时间复杂度为 O(1),而数组在这方面较慢。
虚拟头结点
- 虚拟头结点是一个额外的节点,通常用于简化链表操作,避免处理空链表或单节点链表的特殊情况。
- 在链表操作中,使用虚拟头结点可以使得代码更简洁,减少边界条件的判断。
203. 移除链表元素
class Solution: def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]: # 设置虚拟头结点,next 指向原链表头节点 dummy = ListNode(next=head) cur = dummy # 遍历链表,删除值等于 val 的节点 while cur.next: if cur.next.val == val: # 删除节点:跳过当前节点的下一个节点 cur.next = cur.next.next else: # 只有不删除时才移动指针 cur = cur.next return dummy.next
双向链表
- 双向链表的每个节点包含两个指针:一个指向前一个节点,一个指向后一个节点。
- 双向链表允许从任意方向遍历,适用于需要频繁插入和删除操作的场景。
707. 设计链表
# 定义双向链表节点class ListNode: def __init__(self, val=0, prev=None, next=None): self.val = val self.prev = prev self.next = next
class MyLinkedList: def __init__(self): # 初始化为空链表 self.head = None self.tail = None self.length = 0
def get(self, index: int) -> int: # index 的范围 [0, length - 1] if index < 0 or index >= self.length: return -1 # 从头节点开始遍历 cur = self.head for _ in range(index): cur = cur.next return cur.val
def addAtHead(self, val: int) -> None: new_node = ListNode(val) if self.length == 0: # 空链表,head 和 tail 都指向新节点 self.head = self.tail = new_node else: new_node.next = self.head self.head.prev = new_node self.head = new_node self.length += 1
def addAtTail(self, val: int) -> None: new_node = ListNode(val) if self.length == 0: # 空链表,head 和 tail 都指向新节点 self.head = self.tail = new_node else: new_node.prev = self.tail self.tail.next = new_node self.tail = new_node self.length += 1
def addAtIndex(self, index: int, val: int) -> None: # index 的范围 [0, length] if index < 0 or index > self.length: return if index == 0: self.addAtHead(val) elif index == self.length: # self.length - 1 指向尾节点,而是 self.length 指向新节点的尾部 self.addAtTail(val) else: # 插入到中间位置 cur = self.head for _ in range(index - 1): cur = cur.next new_node = ListNode(val) new_node.next = cur.next new_node.prev = cur cur.next.prev = new_node cur.next = new_node self.length += 1
def deleteAtIndex(self, index: int) -> None: # index 的范围 [0, length - 1] if index < 0 or index >= self.length: return if self.length == 1: # ⚠️ 容易被忽视,删除唯一节点 self.head = self.tail = None elif index == 0: # 删除头节点 self.head = self.head.next self.head.prev = None elif index == self.length - 1: # 删除尾节点 self.tail = self.tail.prev self.tail.next = None else: # 删除中间节点 cur = self.head for _ in range(index): cur = cur.next cur.prev.next = cur.next cur.next.prev = cur.prev self.length -= 1
链表翻转
- 链表翻转是将链表的方向反转,使得原来的尾节点变为头节点,头节点变为尾节点。
206. 反转链表(迭代法)
class Solution: def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: prev, cur = None, head # 当 cur 为 None 时,prev 指向链表的最后一个节点,即新的头节点 while cur: next_node = cur.next # 暂存下一节点 cur.next = prev # 当前节点指向前一节点(反转) prev = cur cur = next_node return prev
206. 反转链表(递归法)
class Solution: def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: return self.reverse(head, None)
def reverse(self, cur: Optional[ListNode], prev: Optional[ListNode]) -> Optional[ListNode]: if not cur: return prev temp = cur.next # 暂存下一节点 cur.next = prev # 当前节点指向前一节点(反转) # 新头节点是由最深层的递归调用产生的。如果不用 return 将它层层向上传递,最终就会丢失 return self.reverse(temp, cur)
代码随想录算法训练营第三天 | 链表 Part01
https://fuwari.vercel.app/posts/code_musings_day_three/