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 # 同样的问题!
```
递归错误发生的原理
- **属性装饰器(`@property`)的作用**
-
`@property` 将方法转换为一个"虚拟属性",使得访问 `obj.data` 时实际调用的是 `data()` 方法。
-
`@data.setter` 定义了一个"设置属性值"的方法,当执行 `obj.data = value` 时,会调用此方法。
- **实例变量与属性同名导致的递归**
-
在 `@data.setter` 方法中,代码试图将值赋给 `self.data`,但 `self.data` 实际上会再次调用 `@data.setter` 方法。
-
例如:
```python
def data(self, new_data: any):
self.data = new_data # 这里再次触发 @data.setter,无限循环!
```
- 这会导致无限递归调用,直到 Python 的调用栈溢出,最终抛出 `RecursionError`。
- **同理,`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
```
为什么这样修改有效?
- **使用不同的变量名**
-
将实际存储数据的实例变量命名为 `_data` 和 `_next`,与属性名 `data` 和 `next` 区分开。
-
这样,在 `@property` 方法中访问 `self._data` 时,不会触发属性装饰器的递归调用。
- **属性与实例变量的分离**
-
外部通过 `node.data` 访问属性时,实际调用的是 `@property` 方法,返回的是 `self._data`。
-
当设置 `node.data = value` 时,调用 `@data.setter` 方法,修改的是 `self._data`,而非属性自身。
总结
-
**递归错误的核心原因**:属性装饰器的方法中直接操作了与属性同名的实例变量,导致无限递归。
-
**解决方案**:将实际存储数据的实例变量与属性名区分开(如使用 `_data` 和 `_next`),并在属性方法中操作这些实例变量。