python链表

python 复制代码
class Node:
      """链表中的一个节点"""
      def __init__(self,node_data):
            self._data = node_data
            self._next = None

      @property
      def data(self):
            return self._data
      
      @data.setter
      def data(self,node_data):
            self._data = node_data

      @property
      def next(self):
            return self._next
      
      @next.setter
      def next(self,next_node):
            self._next = next_node


class UnOrderedList:
      """链表"""
      def __init__(self):
            self.head = None

      def is_empty(self):
            return self.head is None
      
      def prepend(self,data):
            """在链表头部添加一个节点"""
            temp = Node(data)
            temp.next = self.head
            self.head = temp

      def append(self,data):
            """在链表尾部添加一个节点"""
            temp = Node(data)
            if self.head is None:
                  self.head = temp
                  return
            current = self.head
            while current.next is not None:
                  current = current.next
            current.next = temp

      def remove_front(self):
            """删除链表头部的节点"""
            if UnOrderedList.is_empty(self):
                  raise TypeError('can not remove from an empty unorderedlist')
            self.head = self.head.next

      def remove_rear(self):
            if self.is_empty():
                  raise TypeError('can not remove from an empty unorderedlist')
            current = self.head
            previous = None
            while current.next is not None:
                  previous = current
                  current = current.next
            # current.data = None # 这样并没有删除该节点,只是把这个节点的数据重置为None
            previous.next = current.next

      def display(self):
            current = self.head
            while current is not None:
                  print(current.data,end=' --> ')
                  current = current.next
            print('None')
            
linklist = UnOrderedList()
linklist.prepend('小雨')
linklist.prepend('and')
linklist.prepend('小王')
linklist.append(1314)
linklist.remove_front()
linklist.remove_rear()
linklist.display()


输出:and --> 小雨 --> None
python 复制代码
您的无序链表实现整体结构合理,但存在几个关键问题需要修复。以下是详细的评价和改进建议:

---

### 主要问题

#### 1. **`remove_rear` 方法无法处理单节点链表**
   - **问题描述**:当链表只有一个节点时,`previous` 始终为 `None`,执行 `previous.next = current.next` 会抛出 `AttributeError`。
   - **修复方案**:单独处理单节点情况,直接设置 `self.head = None`。
   - **修正后的代码**:
     ```python
     def remove_rear(self):
         if self.is_empty():
             raise ValueError("Cannot remove from an empty list")
         if self.head.next is None:  # 单节点情况
             self.head = None
             return
         current = self.head
         previous = None
         while current.next is not None:
             previous = current
             current = current.next
         previous.next = None  # 断开倒数第二个节点的链接
     ```

#### 2. **`remove_front` 方法中的错误调用**
   - **问题描述**:`UnOrderedList.is_empty(self)` 应改为 `self.is_empty()`,避免不规范的类方法调用。
   - **修复方案**:使用实例方法调用。
   - **修正后的代码**:
     ```python
     def remove_front(self):
         if self.is_empty():
             raise ValueError("Cannot remove from an empty list")
         self.head = self.head.next
     ```

#### 3. **异常类型不准确**
   - **问题描述**:删除操作在空链表时应抛出 `ValueError` 或 `IndexError`,而非 `TypeError`。
   - **修复方案**:统一使用 `ValueError`。
   - **修改示例**:
     ```python
     raise ValueError("Cannot remove from an empty list")
     ```

---

### 其他优化建议

#### 1. **变量命名优化**
   - 将 `temp` 改为 `new_node`,提高可读性。
   - **示例修改**:
     ```python
     def prepend(self, data):
         new_node = Node(data)  # 替代 temp
         new_node.next = self.head
         self.head = new_node
     ```

#### 2. **`append` 方法的简化**
   - 可以复用 `prepend` 处理空链表的情况:
     ```python
     def append(self, data):
         if self.is_empty():
             self.prepend(data)  # 直接调用 prepend
             return
         new_node = Node(data)
         current = self.head
         while current.next is not None:
             current = current.next
         current.next = new_node
     ```

#### 3. **边界条件测试**
   - **测试单节点删除**:
     ```python
     ul = UnOrderedList()
     ul.append(1)
     ul.remove_rear()
     assert ul.is_empty()  # 应返回 True
     ```
   - **测试空链表删除**:
     ```python
     ul = UnOrderedList()
     try:
         ul.remove_front()
     except ValueError as e:
         assert str(e) == "Cannot remove from an empty list"
     ```

---

### 改进后的完整代码

```python
class Node:
    """链表中的一个节点"""
    def __init__(self, node_data):
        self._data = node_data
        self._next = None

    @property
    def data(self):
        return self._data
    
    @data.setter
    def data(self, node_data):
        self._data = node_data

    @property
    def next(self):
        return self._next
    
    @next.setter
    def next(self, next_node):
        self._next = next_node

class UnOrderedList:
    """链表"""
    def __init__(self):
        self.head = None

    def is_empty(self):
        return self.head is None
    
    def prepend(self, data):
        """在链表头部添加节点"""
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node

    def append(self, data):
        """在链表尾部添加节点"""
        if self.is_empty():
            self.prepend(data)
            return
        new_node = Node(data)
        current = self.head
        while current.next is not None:
            current = current.next
        current.next = new_node

    def remove_front(self):
        """删除头部节点"""
        if self.is_empty():
            raise ValueError("Cannot remove from an empty list")
        self.head = self.head.next

    def remove_rear(self):
        """删除尾部节点"""
        if self.is_empty():
            raise ValueError("Cannot remove from an empty list")
        if self.head.next is None:  # 处理单节点情况
            self.head = None
            return
        current = self.head
        previous = None
        while current.next is not None:
            previous = current
            current = current.next
        previous.next = None

    def display(self):
        """打印链表"""
        current = self.head
        while current is not None:
            print(current.data, end=' --> ')
            current = current.next
        print('None')
```

---

### 测试用例验证
```python
# 测试删除尾部节点
ul = UnOrderedList()
ul.append(1)
ul.append(2)
ul.append(3)
ul.remove_rear()
ul.display()  # 预期输出: 1 --> 2 --> None

# 测试单节点删除
ul = UnOrderedList()
ul.append(1)
ul.remove_rear()
assert ul.is_empty()  # 应返回 True

# 测试空链表删除
try:
    ul = UnOrderedList()
    ul.remove_front()
except ValueError as e:
    assert str(e) == "Cannot remove from an empty list"
```

---

通过上述修复和优化,您的无序链表能够正确处理所有边界情况,代码健壮性和可读性均得到提升。

2025.4.19再次实现时发生的错误

python 复制代码
# 完整的链表实现
class Node:
      
      def __init__(self,data:any) -> None:
            self.data = data
            self.next = None

      @property
      def data(self) -> any:
            return self.data
      
      @data.setter
      def data(self,new_data:any) -> None:
            self.data = new_data

      @property
      def next(self) -> any:
            return self.next
      
      @next.setter
      def next(self,new_node:any) -> None:
            self.next = new_node


class UnorderedList:
      
      def __init__(self) -> None:
            self.head = None

      def is_empty(self) -> bool:
            return self.head is None
      
      def prepend(self,data:any) -> None:
            new_node = Node(data)
            new_node.next = self.head
            self.head = new_node

      def append(self,data:any) -> None:
            new_node = Node(data)
            if self.head is None:
                  new_node.next = self.head
                  self.head = new_node
                  return
            current = self.head
            previous = None
            while current is not None:
                  previous = current
                  current = current.next
            previous.next = new_node
            new_node.next = current

      def insert(self,pos:int,data:any) -> None:
            new_node = Node(data)
            if pos == 0:
                  new_node.next = self.head
                  self.head = new_node
                  return
            current = self.head
            previous = None
            loc = 0
            while current is not None:
                  if loc == pos:
                        previous.next = new_node
                        new_node.next = current
                        return
                  previous = current
                  current = current.next

      def display(self) -> str:
            current = self.head
            while current is not None:
                  print('[',current.data,']',end=',')
                  current = current.next


# 测试
my_list = UnorderedList()
print(my_list.is_empty())
my_list.prepend(1)
my_list.prepend(2)
my_list.display()

报错:RecursionError: maximum recursion depth exceeded

超出最大递归深度

递归错误(RecursionError)通常发生在函数或方法无限调用自身时,导致程序的调用栈溢出。在您提供的原始代码中,递归错误的具体原因是 **属性(`@property`)和实例变量同名**,引发了一个隐蔽的无限循环。以下是详细解释:


问题代码分析

原始 `Node` 类的定义:

```python

class Node:

def init(self, data: any) -> None:

self.data = data # 触发 @data.setter

self.next = None # 触发 @next.setter

@property

def data(self) -> any:

return self.data # 递归调用自身,无限循环!

@data.setter

def data(self, new_data: any) -> None:

self.data = new_data # 递归调用自身,无限循环!

@property

def next(self) -> any:

return self.next # 同样的问题!

@next.setter

def next(self, new_node: any) -> None:

self.next = new_node # 同样的问题!

```


递归错误发生的原理

  1. **属性装饰器(`@property`)的作用**
  • `@property` 将方法转换为一个"虚拟属性",使得访问 `obj.data` 时实际调用的是 `data()` 方法。

  • `@data.setter` 定义了一个"设置属性值"的方法,当执行 `obj.data = value` 时,会调用此方法。

  1. **实例变量与属性同名导致的递归**
  • 在 `@data.setter` 方法中,代码试图将值赋给 `self.data`,但 `self.data` 实际上会再次调用 `@data.setter` 方法。

  • 例如:

```python

def data(self, new_data: any):

self.data = new_data # 这里再次触发 @data.setter,无限循环!

```

  • 这会导致无限递归调用,直到 Python 的调用栈溢出,最终抛出 `RecursionError`。
  1. **同理,`next` 属性也存在相同的问题**
  • `self.next = new_node` 也会触发 `@next.setter` 方法,形成无限递归。

如何解决递归错误?

关键:**属性名与实例变量名不能相同!**

修改后的 `Node` 类:

```python

class Node:

def init(self, data: any) -> None:

self._data = data # 使用 _data 作为实例变量

self._next = None # 使用 _next 作为实例变量

@property

def data(self) -> any:

return self._data # 返回实例变量 _data,而非属性 data

@data.setter

def data(self, new_data: any) -> None:

self._data = new_data # 修改实例变量 _data,而非属性 data

@property

def next(self):

return self._next # 返回实例变量 _next

@next.setter

def next(self, new_node):

self._next = new_node # 修改实例变量 _next

```


为什么这样修改有效?

  1. **使用不同的变量名**
  • 将实际存储数据的实例变量命名为 `_data` 和 `_next`,与属性名 `data` 和 `next` 区分开。

  • 这样,在 `@property` 方法中访问 `self._data` 时,不会触发属性装饰器的递归调用。

  1. **属性与实例变量的分离**
  • 外部通过 `node.data` 访问属性时,实际调用的是 `@property` 方法,返回的是 `self._data`。

  • 当设置 `node.data = value` 时,调用 `@data.setter` 方法,修改的是 `self._data`,而非属性自身。


总结

  • **递归错误的核心原因**:属性装饰器的方法中直接操作了与属性同名的实例变量,导致无限递归。

  • **解决方案**:将实际存储数据的实例变量与属性名区分开(如使用 `_data` 和 `_next`),并在属性方法中操作这些实例变量。

相关推荐
天天进步201522 分钟前
Python项目--基于机器学习的股票预测分析系统
开发语言·python·机器学习
东方芷兰24 分钟前
Javase 基础入门 —— 02 基本数据类型
java·开发语言·笔记·spring·intellij-idea·idea
不是仙人的闲人30 分钟前
算法之回溯法
开发语言·数据结构·c++·算法
山海青风41 分钟前
智能体(Intelligent Agents)入门自学教程 3 简单反射型智能体(Reactive Agents)
人工智能·python
独行soc1 小时前
2025年渗透测试面试题总结-拷打题库08(题目+回答)
java·linux·运维·服务器·python·面试·职场和发展
神奇侠20241 小时前
基于 PaddleOCR对pdf文件中的文字提取
python·opencv·paddleocr·pdf文件文本提取
程序小K1 小时前
利用Qt创建一个模拟问答系统
开发语言·qt
愚公搬代码1 小时前
【愚公系列】《Python网络爬虫从入门到精通》063-项目实战电商数据侦探(主窗体的数据展示)
开发语言·爬虫·python
碎梦归途1 小时前
23种设计模式-创建型模式之原型模式(Java版本)
java·开发语言·jvm·设计模式·原型模式
巴巴_羊1 小时前
JavsScript 原型链
开发语言·javascript·原型模式