一、节点的定义
一个节点包含元素区域和链接区域
class ListNode:
def __init__(self, val, next=None):
self.val = val
self.next = next
# node = ListNode(2) 只有这样 才能把元素2 设置为节点
# next 就是指针,这个地方指向了None 为空指针
二、移除链表元素
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummy_head = ListNode(next=head) # 构造虚拟头节点,并指向头节点。
""" 头节点已经给出,所以虚拟头节点的指针 就指向 head(头节点) """
cur = dummy_head # 设置一个游标,进行移动,可以理解为一个指针 也是节点
while (cur.next != None): # cur是可以移动的,判断下个节点是否为空
if cur.next.val == val: # 如果cur的下一个节点的值(val)是 目标 val
cur.next = cur.next.next
# 让当前cur的指针(cur.next) 等于 下一个节点(cur.next)的指针 即 cur.next.next
else:
cur = cur.next # 没找到目标值 就将cur往后移一个节点
return dummy_head.next # 返回头节点
next 指向的是节点 ,节点包含两部分(重要)
单链表最后指向的是None
cur 是节点 ,cur.next 是下一个节点 ,cur.next.val是下一个节点的数值,
当前节点储存的是当前的数值和下一个节点的地址。
三、设计链表
疑问
为什么在构建虚拟头节点后(self.head=Node()),虚拟头节点就指向头节点(self.head.next)。在addAtHead 函数中 就默认了 这一事实
以下构造的函数都默认了这一事实
class Node(object): # 定义节点
def __init__(self,val=0,next = None):
self.val = val
self.next = next
class MyLinkedList(object):
def __init__(self):
self.head = Node() # 创建虚拟头节点
self.size = 0 # 最开始的数量为0 本身是空的 后面会更新
# 获取链表中第 index 个节点的值。如果索引无效,则返回-1
def get(self, index: int) -> int:
cur = self.head.next # 创建一个游标指针 可以移动
if index < 0 or index >= self.size: # 需要先判断是否为无效索引
return -1
while (index): # 循环
cur = cur.next # 让指针移动
index -= 1
return cur.val # 返回节点的值
# 在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
def addAtHead(self, val: int) -> None:
new_node = Node(val) # 创建新的节点
new_node.next = self.head.next # 让新节点指向 虚拟头节点的下一个节点(头节点)
self.head.next = new_node # 让虚拟头节点 指向新的节点
self.size += 1 # size + 1
# 将值为 val 的节点追加到链表的最后一个元素。
def addAtTail(self, val: int) -> None:
new_node = Node(val) # 创建新的节点
cur = self.head # 创建一个游标指针 可以移动
while (cur.next):
cur = cur.next # 让指针移动
cur.next = new_node # 此时cur对应最后一个节点,让其指向新节点
self.size += 1
"""
在链表中的第 index 个节点之前添加值为 val 的节点。
如果 index 等于链表的长度,则该节点将附加到链表的末尾。
如果 index 大于链表长度,则不会插入节点。
如果index小于0,则在头部插入节点。
"""
def addAtIndex(self, index: int, val: int) -> None:
if index > self.size:
return
elif index < 0 : # 根据题目要求设置判断条件,利用之前写好的函数
self.addAtHead(val)
return
elif index == self.size:
self.addAtTail(val)
return
else: # 正常情况
new_node = Node(val) # 创建新节点
cur = self.head # 创建一个游标指针 可以移动
while (index):
cur = cur.next
index -=1 # 循环结束后,cur对应是index的前一个节点
new_node.next = cur.next # 让新节点指向 cur的下一个节点 即 index节点
cur.next = new_node # 让cur的下一个节点指向 新节点
self.size += 1
# 如果索引 index 有效,则删除链表中的第 index 个节点
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size: # 判断
return
cur = self.head
while (index):
cur = cur.next
index -= 1 # 循环结束后,cur对应 index 的前一个节点
cur.next = cur.next.next # 让cur的下一个节点指向index的下一个,即跳过了index
self.size -= 1 # 长度减一
四、反转链表
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur = head
pre = None # 由于要反转,头变尾,pre 需要指向 None pre为前指针 在head之前。最初
while(cur!=None): # 正向循环时,cur为None 结束循环
# 反转方法
tem = cur.next # 需要先保存cur的下一个节点,不然会丢失后面的节点
cur.next = pre # 让cur指向pre(前) 箭头方向就反转过来了
pre = cur # 然后让双指针往前,pre挪到下一个节点
cur = tem # cur挪到下一个节点 tem保存了cur的下一个节点
return pre
重难点
需要判断循环终止的条件,或者明确cur或者pre停在哪一个节点。