继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

链表入门:从零开始学习链表

慕容森
关注TA
已关注
手记 358
粉丝 183
获赞 649
概述

本文详细介绍了链表入门知识,从链表的基础概念到分类,再到单链表的实现和常用操作,帮助读者全面了解链表。此外,文章还探讨了双向链表和循环链表的特点与实现,并列举了链表在实际应用中的多种场景,包括内存管理和文件系统中的应用。文中不仅阐述了链表的优势,还指出了其局限性,帮助读者更好地理解链表入门的相关内容。

链表基础概念

什么是链表?

链表是一种数据结构,它将数据元素组织成一系列节点,每个节点都包含数据部分和一个指向链表中下一个节点的指针。链表的节点通过指针链接在一起,形成一个链,可以表示线性结构。链表的特点是每个节点只包含数据和指向下个节点的指针,而不会包含指向上一个节点的指针,因此称为单链表。

链表是一种线性表,它与数组在逻辑上非常相似,但是,链表在实现上与数组有很大不同。数组是通过索引来访问元素的,而链表则通过遍历指针来访问元素。链表的每个节点都是独立的,每个节点中的数据可以分散在任意的内存位置,不像数组那样连续存放。

链表与数组的区别

  1. 存储方式:数组中的元素是连续存储的,而链表中的元素则是离散分布的。
  2. 访问方式:数组可以通过索引直接访问任意位置的元素,而链表需要从头节点开始遍历,顺序访问每个元素。
  3. 插入和删除:数组在插入或删除元素时,需要移动后面的元素,而链表则只需要修改相应节点的指针。
  4. 空间需求:数组在初始化时需要分配固定大小的空间,而链表只需要分配节点空间,不需要额外的空间来存储索引。
  5. 动态增长:数组在需要增加容量时需要重新分配更大空间,而链表则可以在运行时动态分配节点。
  6. 内存使用:数组中的所有元素占用连续的内存空间,而链表中的节点会分散在内存中,可能造成内存碎片。

链表的分类

链表可以根据指针的指向方式和结构的形态分为以下几类:

  1. 单链表:每个节点只包含一个指向下一个节点的指针。
  2. 双向链表:每个节点包含两个指针,一个指向下一个节点,另一个指向上一个节点。
  3. 循环链表:单链表的最后节点指向第一个节点,形成一个环形结构。
  4. 多向链表:节点之间可以存在多个不同方向的指针,形成复杂的数据结构。
单链表的实现

单链表节点定义

单链表的基本单元是节点,每个节点都包含数据部分和一个指向下一个节点的指针。为了实现单链表,首先需要定义节点的结构。

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

单链表的创建

单链表的创建通常是从头节点开始,添加新的节点到链表的末尾。下面的代码演示了如何创建一个简单的单链表:

class LinkedList:
    def __init__(self):
        self.head = None  # 初始化头节点为None

    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
            return
        current = self.head
        while current.next:
            current = current.next
        current.next = new_node

单链表的遍历

遍历链表是通过从头节点开始,逐个访问每个节点的数据,直到访问到最后一个节点的下一个节点为None。下面的代码演示了如何遍历单链表:

def traverse(self):
    current = self.head
    while current:
        print(current.data)
        current = current.next

常用链表操作

插入节点

在链表中插入节点需要修改节点之间的指针关系,插入位置可以是链表的头部、尾部或中间。下面的代码演示了如何在单链表的头部插入节点:

def prepend(self, data):
    new_node = Node(data)
    new_node.next = self.head
    self.head = new_node

删除节点

删除链表中的节点需要找到待删除节点的前一个节点,并修改其指针指向删除节点的下一个节点。下面的代码演示了如何删除单链表中的一个节点:

def delete(self, key):
    current = self.head
    prev = None
    while current and current.data != key:
        prev = current
        current = current.next
    if current is None:
        return
    if prev is None:
        self.head = current.next
    else:
        prev.next = current.next

查找节点

查找链表中的节点需要从头节点开始遍历链表,直到找到指定的节点。下面的代码演示了如何查找单链表中的一个节点:

def find(self, key):
    current = self.head
    while current and current.data != key:
        current = current.next
    return current

双向链表简介

双向链表的特点

双向链表每个节点包含两个指针,一个指向下个节点,另一个指向上个节点。双向链表的优点在于可以在O(1)的时间复杂度内同时访问前后节点,但是每个节点需要存储两个指针,所以空间占用更大。

双向链表的节点结构通常包含:前一个节点的指针、数据部分、后一个节点的指针。

双向链表的实现

双向链表的实现与单链表类似,不过需要定义双向节点结构,并在插入和删除操作中处理两个指针的修改。

class DNode:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

class DLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def append(self, data):
        new_node = DNode(data)
        if not self.head:
            self.head = self.tail = new_node
        else:
            new_node.prev = self.tail
            self.tail.next = new_node
            self.tail = new_node
循环链表概述

循环链表的定义

循环链表是一种特殊的单链表,将最后一个节点的指针指向头节点,形成一个环形结构。循环链表的好处在于可以在链表中循环访问,而不需要检查链表的结束条件。

循环链表的实现

循环链表的实现与单链表类似,不过需要在插入和删除操作中处理头节点的指针。

class CLinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
            new_node.next = self.head
        else:
            current = self.head
            while current.next != self.head:
                current = current.next
            current.next = new_node
            new_node.next = self.head
链表应用场景

链表在实际中的应用场景

链表是一种非常灵活的数据结构,它在实际应用中有着广泛的应用场景:

  1. 内存管理:操作系统中的内存管理模块使用链表来管理空闲内存块。

    class MemoryBlock:
       def __init__(self, size):
           self.size = size
           self.next = None
    
    class MemoryManager:
       def __init__(self):
           self.head = None
    
       def allocate(self, size):
           new_block = MemoryBlock(size)
           if not self.head:
               self.head = new_block
           else:
               current = self.head
               while current.next:
                   current = current.next
               current.next = new_block
    
       def deallocate(self, block):
           current = self.head
           prev = None
           while current:
               if current == block:
                   if prev:
                       prev.next = current.next
                   else:
                       self.head = current.next
                   break
               prev = current
               current = current.next
  2. 文件系统:文件系统中使用链表来管理文件目录。

    class FileNode:
       def __init__(self, name):
           self.name = name
           self.next = None
    
    class Filesystem:
       def __init__(self):
           self.head = None
    
       def add_file(self, name):
           new_file = FileNode(name)
           if not self.head:
               self.head = new_file
           else:
               current = self.head
               while current.next:
                   current = current.next
               current.next = new_file
  3. 排序:在某些排序算法中,如归并排序,链表可以很方便地进行合并操作。

    def merge_sort(head):
       if not head or not head.next:
           return head
    
       slow, fast = head, head.next
       while fast and fast.next:
           slow = slow.next
           fast = fast.next.next
    
       mid = slow.next
       slow.next = None
    
       left = merge_sort(head)
       right = merge_sort(mid)
    
       return merge(left, right)
    
    def merge(left, right):
       dummy = Node(0)
       current = dummy
    
       while left and right:
           if left.data < right.data:
               current.next = left
               left = left.next
           else:
               current.next = right
               right = right.next
           current = current.next
    
       if left:
           current.next = left
       if right:
           current.next = right
    
       return dummy.next
  4. 缓存:缓存系统中使用链表来管理和维护缓存中的数据。

    class LRUCache:
       def __init__(self, capacity):
           self.capacity = capacity
           self.head = None
           self.tail = None
           self.cache = {}
    
       def get(self, key):
           if key in self.cache:
               node = self.cache[key]
               self._remove(node)
               self._add_to_head(node)
               return node.data
           return None
    
       def put(self, key, value):
           if key in self.cache:
               self._remove(self.cache[key])
           elif len(self.cache) >= self.capacity:
               self._remove(self.tail)
           new_node = Node(key, value)
           self.cache[key] = new_node
           self._add_to_head(new_node)
    
       def _remove(self, node):
           if node.prev:
               node.prev.next = node.next
           if node.next:
               node.next.prev = node.prev
           if node == self.head:
               self.head = node.next
           if node == self.tail:
               self.tail = node.prev
    
       def _add_to_head(self, node):
           if not self.head:
               self.head = self.tail = node
           else:
               node.prev = None
               self.head.prev = node
               node.next = self.head
               self.head = node
  5. 队列:在实现队列数据结构时,常常使用链表来存储队列中的元素。

    class Queue:
       def __init__(self):
           self.head = None
           self.tail = None
    
       def enqueue(self, data):
           new_node = Node(data)
           if not self.head:
               self.head = self.tail = new_node
           else:
               self.tail.next = new_node
               self.tail = new_node
    
       def dequeue(self):
           if not self.head:
               return None
           data = self.head.data
           self.head = self.head.next
           if not self.head:
               self.tail = None
           return data
  6. 网络协议:在网络协议栈中,链表被用来管理传输的数据包。

    class PacketNode:
       def __init__(self, packet):
           self.packet = packet
           self.next = None
    
    class PacketQueue:
       def __init__(self):
           self.head = None
           self.tail = None
    
       def enqueue(self, packet):
           new_node = PacketNode(packet)
           if not self.head:
               self.head = self.tail = new_node
           else:
               self.tail.next = new_node
               self.tail = new_node
    
       def dequeue(self):
           if not self.head:
               return None
           packet = self.head.packet
           self.head = self.head.next
           if not self.head:
               self.tail = None
           return packet

链表的优势与局限性

优势

  1. 动态增长:链表可以在运行时动态增加或减少节点,非常适合需要频繁增删操作的情况。
  2. 灵活的插入和删除:在链表中插入或删除节点时,只需要修改指针关系,不需要移动节点。
  3. 内存利用率:链表可以节省内存空间,尤其是存储不连续的数据时。

局限性

  1. 访问效率低:在链表中访问节点需要从头节点开始遍历,效率相对较低。
  2. 额外内存开销:每个节点需要额外的指针空间,这会增加内存使用。
  3. 不支持随机访问:链表中的节点不能通过索引直接访问,而需要通过遍历操作访问。

总结,链表作为一种基本的数据结构,在实际应用中有着广泛的应用场景和优势,但同时也有着一定的局限性。在不同的应用场景中,选择合适的数据结构是至关重要的。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP