数据结构与算法:线性表的链式表示和实现(单链表)

部署运行你感兴趣的模型镜像

单链表的定义、表示与基本操作实现

。上一节我们讨论了顺序表,它是一种连续存储的数据结构,但插入和删除操作效率较低。今天,我们将探索单链表——一种动态、非连续的线性表结构,它能高效地处理插入和删除。我会用通俗易懂的语言,结合代码示例,详细介绍单链表的定义、表示和基本操作。最后,我会自然引出双向链表的概念,为后续学习铺垫。


一、单链表的定义和表示

单链表是一种线性数据结构,由一系列节点组成,每个节点存储数据元素和一个指向下一个节点的指针(称为“后继指针”)。这就像一串珠子,每个珠子(节点)包含自己的内容(数据)和一根线(指针)连接到下一个珠子。整个链表通过一个头指针(head)指向第一个节点,如果链表为空,则头指针为None。

  • 节点结构:每个节点有两部分:
    • 数据域(data):存储实际数据,可以是整数、字符串等。
    • 指针域(next):指向下一个节点的地址。如果当前节点是最后一个,则指针为None(表示链表的结尾)。
  • 表示方式:在代码中,我们用一个类来表示节点。例如,在Python中:
    class Node:
        def __init__(self, data):
            self.data = data  # 存储数据
            self.next = None   # 指向下一个节点的指针,初始为None
    

    整个链表通过头指针管理:头指针指向第一个节点,后续节点通过指针逐个连接。

单链表的优势在于:

  • 动态分配内存:节点在运行时创建和删除,无需预先分配固定大小。
  • 高效插入/删除:只需修改指针,无需移动大量元素(像顺序表那样)。
  • 内存利用率高:节点可以分散存储。

但缺点也很明显:

  • 只能单向遍历:从头部开始,只能向后访问,不能向前。
  • 查找效率较低:需要从头开始逐个遍历。

现在,我们进入核心部分——单链表的基本操作实现。我会覆盖创建、插入、删除、查找、遍历等所有常见操作,确保全面详细。所有代码用Python实现,我会逐行解释。


二、单链表基本操作的实现

为了便于理解,我定义一个完整的单链表类 LinkedList,包含所有基本操作。每个操作我都会用通俗语言解释原理、步骤和代码。

1. 创建链表(初始化)
  • 原理:创建一个空链表,头指针指向None。
  • 代码实现
    class LinkedList:
        def __init__(self):
            self.head = None  # 头指针初始为None,表示空链表
    

    • 解释__init__ 是构造函数,self.head 是头指针。当创建链表对象时,如 my_list = LinkedList(),头指针指向None,表示没有节点。
2. 插入操作

插入操作有三种常见方式:头插法(在链表头部插入)、尾插法(在尾部插入)和在指定位置插入。我会详细说明每种方式。

  • 头插法(插入到头部)

    • 原理:新节点成为链表的第一个节点。步骤:创建新节点 → 新节点的指针指向原头节点 → 更新头指针指向新节点。
    • 代码实现
      def insert_at_head(self, data):
          new_node = Node(data)  # 创建新节点
          new_node.next = self.head  # 新节点的指针指向原头节点
          self.head = new_node     # 更新头指针指向新节点
      

      • 解释:例如,链表原为 1 -> 2,插入数据 3:新节点 3 的指针指向 1,然后头指针指向 3,结果 3 -> 1 -> 2。时间复杂度是 $O(1)$。
  • 尾插法(插入到尾部)

    • 原理:新节点成为链表的最后一个节点。步骤:创建新节点 → 如果链表为空,直接设为头节点 → 否则,遍历到最后一个节点 → 将最后一个节点的指针指向新节点。
    • 代码实现
      def insert_at_tail(self, data):
          new_node = Node(data)  # 创建新节点
          if self.head is None:   # 如果链表为空
              self.head = new_node  # 直接设为头节点
          else:
              current = self.head
              while current.next:  # 遍历到最后一个节点(current.next 为 None)
                  current = current.next
              current.next = new_node  # 最后一个节点的指针指向新节点
      

      • 解释:例如,链表 1 -> 2,插入 3:遍历到节点 2(其 next 为 None),然后将 2.next 指向 3,结果 1 -> 2 -> 3。时间复杂度是 $O(n)$,因为需要遍历整个链表。
  • 在指定位置插入

    • 原理:在链表的特定索引处插入节点。索引从0开始(头节点索引为0)。步骤:创建新节点 → 找到指定位置的前一个节点 → 修改指针连接新节点。
    • 代码实现
      def insert_at_position(self, data, position):
          if position < 0:
              raise ValueError("位置不能为负")
          new_node = Node(data)
          if position == 0:  # 如果插入位置是0(头部)
              self.insert_at_head(data)
          else:
              current = self.head
              index = 0
              # 找到位置 position-1 的节点(新节点的前一个节点)
              while current and index < position - 1:
                  current = current.next
                  index += 1
              if current is None:  # 如果位置超出链表长度
                  raise IndexError("位置超出链表范围")
              new_node.next = current.next  # 新节点指针指向原位置节点
              current.next = new_node       # 前一个节点指针指向新节点
      

      • 解释:例如,链表 1 -> 2 -> 4,在索引1处插入 3:找到索引0的节点(数据为1),其 next 原指向 2,现在新节点 3 的 next 指向 2,然后 1.next 指向 3,结果 1 -> 3 -> 2 -> 4。时间复杂度是 $O(n)$。
3. 删除操作

删除操作也有三种:删除头节点、删除尾节点和删除指定节点。

  • 删除头节点

    • 原理:移除链表的第一个节点。步骤:如果链表不空,更新头指针指向第二个节点。
    • 代码实现
      def delete_at_head(self):
          if self.head is None:  # 链表为空
              raise Exception("链表为空,无法删除")
          self.head = self.head.next  # 头指针指向第二个节点(原头节点被丢弃)
      

      • 解释:例如,链表 3 -> 1 -> 2,删除头部:头指针从 3 改为指向 1,结果 1 -> 2。时间复杂度 $O(1)$。
  • 删除尾节点

    • 原理:移除最后一个节点。步骤:遍历到倒数第二个节点 → 将其指针设为None。
    • 代码实现
      def delete_at_tail(self):
          if self.head is None:  # 链表为空
              raise Exception("链表为空,无法删除")
          if self.head.next is None:  # 如果只有一个节点
              self.head = None
          else:
              current = self.head
              while current.next.next:  # 遍历到倒数第二个节点(current.next.next 为 None)
                  current = current.next
              current.next = None  # 倒数第二个节点的指针设为None
      

      • 解释:例如,链表 1 -> 2 -> 3,删除尾部:遍历到节点 2(其 next 指向 3),将 2.next 设为 None,结果 1 -> 2。时间复杂度 $O(n)$。
  • 删除指定节点(按值或位置)

    • 原理:删除包含特定数据的节点或指定位置的节点。这里以按值删除为例:找到该节点的前一个节点 → 修改指针跳过该节点。
    • 代码实现
      def delete_by_value(self, value):
          if self.head is None:
              raise Exception("链表为空,无法删除")
          if self.head.data == value:  # 如果要删除头节点
              self.delete_at_head()
              return
          current = self.head
          while current.next:  # 遍历,直到找到值匹配节点的前一个节点
              if current.next.data == value:
                  current.next = current.next.next  # 跳过匹配节点
                  return
              current = current.next
          raise ValueError(f"值 {value} 不在链表中")
      

      • 解释:例如,链表 1 -> 2 -> 3,删除值 2:找到节点 1(其 next 指向 2),将 1.next 指向 3(跳过 2),结果 1 -> 3。时间复杂度 $O(n)$。
4. 查找操作

查找操作包括按值查找和按位置查找。

  • 按值查找

    • 原理:遍历链表,检查每个节点的数据是否匹配目标值。
    • 代码实现
      def search_by_value(self, value):
          current = self.head
          position = 0
          while current:
              if current.data == value:
                  return position  # 返回匹配的索引
              current = current.next
              position += 1
          return -1  # 没找到
      

      • 解释:例如,链表 1 -> 2 -> 3,查找值 2:从头部开始,索引0(数据1)不匹配,索引1(数据2)匹配,返回1。时间复杂度 $O(n)$。
  • 按位置查找

    • 原理:获取指定索引处的节点数据。
    • 代码实现
      def get_at_position(self, position):
          if position < 0:
              raise ValueError("位置不能为负")
          current = self.head
          index = 0
          while current and index < position:
              current = current.next
              index += 1
          if current is None:
              raise IndexError("位置超出链表范围")
          return current.data
      

      • 解释:例如,链表 1 -> 2 -> 3,获取位置1:遍历到索引1(节点数据为2),返回2。时间复杂度 $O(n)$。
5. 遍历操作
  • 原理:从头节点开始,逐个访问每个节点并打印数据。
  • 代码实现
    def traverse(self):
        current = self.head
        while current:
            print(current.data, end=" -> ")  # 打印当前节点数据
            current = current.next
        print("None")  # 表示链表结束
    

    • 解释:例如,链表 1 -> 2 -> 3,调用 traverse() 输出 1 -> 2 -> 3 -> None。时间复杂度 $O(n)$。
6. 其他常用操作

为了全面,我还添加了获取链表长度和判断链表是否为空的辅助操作。

  • 获取链表长度

    • 原理:遍历链表计数节点数。
    • 代码实现
      def get_length(self):
          count = 0
          current = self.head
          while current:
              count += 1
              current = current.next
          return count
      

      • 解释:时间复杂度 $O(n)$。
  • 判断链表是否为空

    • 原理:检查头指针是否为None。
    • 代码实现
      def is_empty(self):
          return self.head is None
      

      • 解释:时间复杂度 $O(1)$。
完整代码示例

为了方便测试,这里是完整的单链表类定义:

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

class LinkedList:
    def __init__(self):
        self.head = None
    
    def insert_at_head(self, data):
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node
    
    def insert_at_tail(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = new_node
    
    def insert_at_position(self, data, position):
        if position < 0:
            raise ValueError("位置不能为负")
        new_node = Node(data)
        if position == 0:
            self.insert_at_head(data)
        else:
            current = self.head
            index = 0
            while current and index < position - 1:
                current = current.next
                index += 1
            if current is None:
                raise IndexError("位置超出链表范围")
            new_node.next = current.next
            current.next = new_node
    
    def delete_at_head(self):
        if self.head is None:
            raise Exception("链表为空,无法删除")
        self.head = self.head.next
    
    def delete_at_tail(self):
        if self.head is None:
            raise Exception("链表为空,无法删除")
        if self.head.next is None:
            self.head = None
        else:
            current = self.head
            while current.next.next:
                current = current.next
            current.next = None
    
    def delete_by_value(self, value):
        if self.head is None:
            raise Exception("链表为空,无法删除")
        if self.head.data == value:
            self.delete_at_head()
            return
        current = self.head
        while current.next:
            if current.next.data == value:
                current.next = current.next.next
                return
            current = current.next
        raise ValueError(f"值 {value} 不在链表中")
    
    def search_by_value(self, value):
        current = self.head
        position = 0
        while current:
            if current.data == value:
                return position
            current = current.next
            position += 1
        return -1
    
    def get_at_position(self, position):
        if position < 0:
            raise ValueError("位置不能为负")
        current = self.head
        index = 0
        while current and index < position:
            current = current.next
            index += 1
        if current is None:
            raise IndexError("位置超出链表范围")
        return current.data
    
    def traverse(self):
        current = self.head
        while current:
            print(current.data, end=" -> ")
            current = current.next
        print("None")
    
    def get_length(self):
        count = 0
        current = self.head
        while current:
            count += 1
            current = current.next
        return count
    
    def is_empty(self):
        return self.head is None

三、引出双向链表

单链表在插入和删除操作上高效,但它只能单向遍历(从头到尾)。这在某些场景下效率不高,例如:

  • 需要反向遍历链表时(如查找前一个节点),单链表必须从头开始,耗时 $O(n)$。
  • 删除尾节点时,需要遍历整个链表,效率低。

为了解决这些问题,我们引入了双向链表。在双向链表中,每个节点不仅有一个指向下一个节点的指针(next),还有一个指向前一个节点的指针(prev)。这就像珠子之间有两根线,一根向前,一根向后,允许双向遍历。

双向链表的优势:

  • 双向遍历:可以从头到尾或从尾到头遍历。
  • 高效删除尾节点:直接通过尾指针操作。
  • 更灵活的插入/删除:尤其在中间位置。

在下一节课,我们将详细讲解双向链表的定义、表示和实现。现在,你可以尝试基于单链表代码,思考如何修改节点类来支持双向链表(提示:在 Node 类中添加一个 prev 指针)。


您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值