首先,让我们来看一下链表是什么。链表是一种数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表的好处是可以动态地添加或删除节点,而不需要像数组一样预先分配足够的空间。
接下来,让我们来看一些Python代码示例,来了解如何使用链表。
首先,我们需要定义一个节点类,它包含数据和指向下一个节点的指针:
class Node:
def __init__(self, data):
self.data = data
self.next = None
然后,我们可以定义一个链表类,它包含指向链表头部的指针和一些基本操作,如添加节点、删除节点等:
class LinkedList:
def __init__(self):
self.head = None
def add_node(self, data):
new_node = Node(data)
if self.head is None:
self.head = new_node
else:
current = self.head
while current.next is not None:
current = current.next
current.next = new_node
def remove_node(self, data):
if self.head is None:
return
if self.head.data == data:
self.head = self.head.next
return
current = self.head
while current.next is not None:
if current.next.data == data:
current.next = current.next.next
return
current = current.next
最后,让我们来看一些实际的用例。比如,我们可以使用链表来实现 LRU 缓存淘汰算法。LRU 缓存淘汰算法是一种常用的缓存淘汰策略,它会删除最近最少使用的缓存项,以释放空间。
以下是一个简单的 LRU 缓存实现,使用了一个字典和一个双向链表:
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = {}
self.head = Node(0)
self.tail = Node(0)
self.head.next = self.tail
self.tail.prev = self.head
def _remove_node(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def _add_node(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key in self.cache:
node = self.cache[key]
self._remove_node(node)
self._add_node(node)
return node.value
return -1
def put(self, key, value):
if key in self.cache:
self._remove_node(self.cache[key])
node = Node(value)
self.cache[key] = node
self._add_node(node)
if len(self.cache) > self.capacity:
del self.cache[self.tail.prev.key]
self._remove_node(self.tail.prev)
让我们结合一些 LeetCode 上的单向链表的题目来说明如何使用单向链表。
LeetCode 上一些关于单向链表的经典题目包括:
-
- 反转链表(Reverse Linked List)
-
- 环形链表(Linked List Cycle)
-
- 合并两个有序链表(Merge Two Sorted Lists)
-
- 删除排序链表中的重复元素(Remove Duplicates from Sorted List)
下面我们分别介绍这些问题以及如何使用单向链表来解决它们。
- 反转链表(Reverse Linked List)
这是一个非常基础的单向链表问题。我们需要将给定链表翻转,使得链表的最后一个节点变为第一个节点,倒数第二个节点变为第二个节点,以此类推。
我们可以使用一个 prev 节点来记录当前节点的前一个节点,一个 curr 节点来记录当前节点,以及一个 next 节点来记录当前节点的下一个节点。我们遍历链表,将当前节点的 next 指向 prev,然后将 prev 和 curr 分别向后移动一个节点即可。
代码如下:
def reverse_list(head):
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
- 环形链表(Linked List Cycle)
这个问题要求我们判断一个单向链表是否存在环,即链表中是否存在一个节点可以一直遍历下去而不会到达尾部。
我们可以使用快慢指针的方法来解决这个问题。我们定义两个指针,一个快指针和一个慢指针,从链表的头部开始遍历。快指针每次向前移动两个节点,而慢指针每次只向前移动一个节点。如果链表中存在环,那么这两个指针最终会相遇。
代码如下:
def has_cycle(head):
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
- 合并两个有序链表(Merge Two Sorted Lists)
这个问题要求我们将两个有序的单向链表合并成一个新的有序链表。
我们可以使用递归来解决这个问题。我们首先比较两个链表的头部节点,将较小的节点作为新链表的头部,并将新链表的 next 指向递归调用合并后的链表。如果有一个链表为空了,我们直接返回另一个链表作为新链表的尾部即可。
代码如下:
def merge_two_lists(l1, l2):
if not l1:
return l2
if not l2:
return l1
if l1.val < l2.val:
l1.next = merge_two_lists(l1.next, l2)
return l1
else:
l2.next = merge_two_lists(l1, l2.next)
return l2
- 删除排序链表中的重复元素(Remove Duplicates from Sorted List)
这个问题要求我们删除一个已经排序的单向链表中的重复节点。
我们可以使用双指针的方法来解决这个问题。我们定义一个 prev 指针和一个 curr 指针,从链表的头部开始遍历。当 prev 和 curr 指向的节点的值相同时,我们将 curr 向后移动一个节点,直到 prev 和 curr 指向的节点的值不相同时,我们将 prev 的 next 指针指向 curr,然后将 prev 和 curr 分别向后移动一个节点,重复以上过程直到遍历完整个链表。
代码如下:
def delete_duplicates(head):
if not head:
return head
prev = head
curr = head.next
while curr:
if curr.val == prev.val:
prev.next = curr.next
else:
prev = curr
curr = curr.next
return head
以上就是几个使用单向链表解决 LeetCode 经典题目的例子。在实际的开发过程中,单向链表常常用于构建各种数据结构,例如队列、栈、图等等。掌握单向链表的基本操作和常用算法,对于编写高效的程序和解决实际问题都非常有帮助。
总之,链表是一种非常有用的数据结构,它可以帮助我们实现许多算法和数据结构。希望这篇文章能够让你对链表有更深入的了解,并且也能带给你一些快乐!