链表
- 定义:链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(即空指针)。
- 链接的入口节点称为链表的头结点也就是head。
- 链表类型:
- 单链表:如上图
单链表中的指针域只能指向节点的下一个节点 - 双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点
既可以向前查询也可以向后查询。
- 循环链表:链表首尾相连
-
链表的存储方式:
链表通过指针域的指针链接在内存中的各个节点。
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。 -
链表的定义
class ListNode:
def __init__(self, val, next=None):
self.val = val
self. next = next
*链表的操作:
删除:
C++里最好是再手动释放这个D节点,释放这块内存。其他语言例如Java、Python,就有自己的内存回收机制,就不用自己手动释放了。
添加:
链表的增添和删除都是O(1)操作,也不会影响到其他节点。
但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)。
链表与数组:
数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。
链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。
203.移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
链表移除头节点操作的两种方式:
- 直接使用原来的链表来进行删除操作。
# 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]:
# 删除头节点
#print(head.val)
while head is not None and head.val == val:
head = head.next
# 删除非头节点
cur = head
while cur is not None and cur.next is not None:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return head
- 设置一个虚拟头结点在进行删除操作——这样原链表的所有节点就都可以按照统一的方式进行移除
# 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) # 设置一个虚拟头节点
cur = dummy_head
while(cur.next!=None):
if(cur.next.val == val):
cur.next = cur.next.next #删除cur.next节点
else:
cur = cur.next
return dummy_head.next
707.设计链表 ——重难点
题目描述:设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
- 这一节我在《python数据结构与算法》这本书里讲链表的时候实现过,这本书还是可以看看的。
class Node:
def __init__(self, val:int):
self.val = val
self.next = None
class MyLinkedList:
def __init__(self):
self._head = Node(0) # 虚拟头节点
self._count = 0
def get(self, index: int) -> int:
"""
Get the value of the index-th node in the linked list. If the index is invalid, return -1.
"""
if 0 <= index < self._count:
node = self._head
for _ in range(index+1):
node = node.next
return node.val
else:
return -1
def addAtHead(self, val: int) -> None:
"""
Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
"""
self.addAtIndex(0, val)
def addAtTail(self, val: int) -> None:
"""
Append a node of value val to the last element of the linked list.
"""
self.addAtIndex(self._count, val)
def addAtIndex(self, index: int, val: int) -> None:
"""
Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
"""
if index < 0:
index = 0
elif index > self._count:
return
self._count += 1
add_node = Node(val)
prev_node, current_node = None, self._head
for _ in range(index + 1):
prev_node, current_node = current_node, current_node.next
else:
prev_node.next, add_node.next = add_node, current_node
def deleteAtIndex(self, index: int) -> None:
"""
Delete the index-th node in the linked list, if the index is valid.
"""
if 0 <= index < self._count:
# 计数-1
self._count -= 1
prev_node, current_node = None, self._head
for _ in range(index + 1):
prev_node, current_node = current_node, current_node.next
else:
prev_node.next, current_node.next = current_node.next, None
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
206.反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
- 迭代法
代码注释写的很清楚
# 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]:
cur = head # 初始化cur为头节点
pre = None # pre 为 None
while cur is not None:
temp = cur.next # 存放节点cur的指针指向的下一个节点
cur.next = pre # cur的指针反向指向pre,即完成一次反转操作
pre = cur # 移动pre的cur
cur = temp # 移动cur到之前存放的下一个节点temp
# 接着下一次循环,直到最后一个节点,也就是temp = cur.next (==None)
# 最后一次循环temp为NoneNone,然后移动cur到temp
# 此时cur不满足while循环条件,结束循环
return pre
- 递归法
# 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: ListNode) -> ListNode:
def reverse(pre,cur):
if not cur: # cur=None,递归结束,返回pre
return pre # 此时pre相当于原链表的最后一个节点,也即反转后的头节点
tmp = cur.next # 存放当前节点指针指向的下一个节点
cur.next = pre # 讲cur的指针反向指向前一个节点
return reverse(cur,tmp) # 递归调用,相当于迭代法里的移动步骤;pre=cur,cur=temp
return reverse(None,head) # 递归调用reverse,
# 此处参数值相当于初始化 cur=head,pre=None