一:
链表(顺序表)和列表的区别:
比如在电影院,你们10个非常要好的朋友去看电影。因为你们10个的关系非常好所以决定要坐在一起。然后你们去电影院一看,发现没有10个连在一起的座位了。此时会有两种情况:
1:你们仗着人多势众,把其他人赶走,硬是腾出了10个连在一起的座位。你们正准备坐下观看,然后警察来了,然后你们就被带走了。
结果:你们因扰乱社会治安被处罚。
2:其中一个朋友说:要不我们分开坐吧。然后你们分开各找了一个座位,为了方便走的时候还一起,你们还分享了自己的位置。
结果:你们美滋滋的看完了电影,然后又一起去网吧开黑。
列表选择了第一种处理方式。链表选择了第二种处理方式。
结果:
选择了列表的程序出现了错误。o(╥﹏╥)o
而选择了链表的程序美滋滋的成功运行。(*^▽^*)
总结如下:
列表长度固定。存储空间连续。
链表长度不固定。存储空间可以不连续(但其中每个节点都有下个节点的位置)。
优缺点:
列表:查找方便。但删除和插入一个元素需要移动大量元素。
链表:删除和插入数据方便(直接修改节点所指向的位置)。但查找元素需要遍历大量元素。
使用列表还是链表要根据具体情况而定。
二:
链表,顾名思义,表中的每个节点都保存有下一个节点的位置,所有节点串成一条链。
根据指针的不同,可以分为:
单链表:每个节点都有一个指向下一节点的指针(末尾节点除外)
双链表:每个节点都有一个指向上一节点的指针和指向下一节点的指针。(头结点无指向上一节点的指针。尾节点无指向下一节点的指针)
循环链表:为尾节点增加一个指向头节点的指针(对单链表而言)。将头结点上指针指向尾节点,将尾节点的下指针指向头结点(对双链表而言)。
实现链表中的节点:
class Node:
"""节点类"""
def __init__(self, data, next = None):
self._data = data; # 当前节点保存的数据
self._next = next; # 所指向的下一个节点
def __repr__(self):
return str(self._data);
实现链表:
1:初始化方法
class ChainTable:
"""链表类"""
def __init__(self):
self._head = None; # 表示链表的头节点
self._length = 0; # 表示链表的长度
self._index_next = 0; # 用于_next()函数中的计数
2:判断链表是否为空的方法(重要)
# 判断链表是否为空
def _isEmpty(self):
return (self._length == 0);
3:向链表中追加元素
# 向链表末尾追加元素
def _append(self, DataOrNode):
"""
:param DataOrNode: 数据或Node类的对象
:return: None
"""
# 将数据的类型统一为Node类
item = None;
if isinstance(DataOrNode, Node):
item = DataOrNode;
else:
item = Node(DataOrNode);
if not self._head: # 判断头结点是否为空
self._head = item;
self._length += 1;
else:
node = self._head; # 存储当前节点
while node._next: # 遍历所有节点,直到最后一个节点。
node = node._next;
node._next = item;
self._length += 1;
4:根据传入的索引删除元素
# 根据索引删除数据
def _delete(self, index):
"""
:param index: 要删除元素的索引
:return: None
"""
if self._isEmpty():
print("链表为空!!!");
return;
if index < 0 and index >= self._length:
raise IndexError("索引错误!!!");
if index == 0:
self._head = self._head._next;
self._length -= 1;
else:
j = 0; # 辅助定位
prev = self._head;
node = self._head; # 表示当前节点
while node._next and j < index: # 遍历所有节点
prev = node;
node = node._next;
j += 1;
if j == index:
prev._next = node._next;
self._length -= 1;
5:在指定位置插入元素
# 在指定位置插入数据
def _insert(self, index, DataOrNode):
"""
:param index: 要插入的位置
:param DataOrNode: 要插入的数据
:return: None
"""
if self._isEmpty():
print("链表为空!!!");
return;
if index < 0 and index >= self._length:
raise IndexError("索引错误!!!");
# 将数据的类型统一为Node类
item = None;
if isinstance(DataOrNode, Node):
item = DataOrNode;
else:
item = Node(DataOrNode);
if index == 0: # 如果插入的位置为0
item._next = self._head;
self._head = item;
self._length += 1;
else:
j = 0; # 辅助定位
prev = self._head; # 表示当前节点的上一节点
node = self._head; # 表示当前节点
while node._next and j < index: # 保证当前节点不是最后一个节点
prev = node;
node = node._next;
j += 1;
if j == index:
prev._next = item;
item._next = node;
self._length += 1;
6:修改某一索引的元素
# 修改某一索引的元素
def _update(self, index, newData):
"""
:param index: 要修改元素的索引
:param newData: 新的元素值
:return: None
"""
if self._isEmpty():
print("链表为空!!!");
return;
if index < 0 and index >= self._length:
raise IndexError("索引错误!!!");
if index == 0:
self._head._data = newData;
else:
j = 0; # 辅助定位
node = self._head;
while node._next and j < index:
node = node._next;
j += 1;
if j == index:
node._data = newData;
7:根据索引获得元素
# 根据索引来查找节点的数据
def _getItem(self, index):
"""
:param index: 要查找的索引
:return: Item: 该索引对应的节点
"""
if self._isEmpty():
print("链表为空!!!");
return;
if index < 0 or index >= self._length:
raise IndexError("索引错误!!!");
j = 0; # 辅助定位
node = self._head; # 表示当前节点
while node._next and j < index:
node = node._next;
j += 1;
return node;
8:根据传入的值,找到该元素在链表中的位置(若有多个同值元素,则只返回第一个元素的位置)
# 根据元素找到改元素的索引, 只返回第一个相同元素的位置
def _getIndex(self, data):
"""
:param data:要查找位置的元素
:return: Index: 该元素在链表中的位置
"""
if self._isEmpty():
print("链表为空!!!");
return;
j = 0; # 辅助定位
node = self._head; # 表示当前节点
while node:
if node._data == data:
return j;
else:
node = node._next;
j += 1;
if j == self._length:
return None;
9:清空链表
#清空链表的方法
def _clear(self):
"""
:return: None
"""
self._head = None;
self._length = 0;
10:像生成器一样逐个返回链表中的元素
# 像生成器一样逐个返回链表中的元素
def _next(self):
"""
:return: every_data: 链表中的节点
"""
every_data = self._getItem(self._index_next);
self._index_next += 1;
return every_data;
11:返回链表长度(len()方法)
# 使用 len(ChainTable) 时返回链表的长度
def __len__(self):
return self._length;
12:直接输出链表对象时,返回链表中所有数据组成的字符串
# 直接输出链表对象时,返回链表中所有数据组成的字符串
def __repr__(self):
"""
:return:None
"""
if self._isEmpty():
print("链表为空!!!");
return;
result = "";
j = 0; # 辅助定位
while j < self._length:
result += str(self._getItem(j)) + " ";
j += 1;
return result;
完整代码可参考:https://github.com/shyorange/CommonlyUsedToolsProjects/blob/master/ChainTable.py
三:
要实现循环链表只要将尾节点的 _next 指向头结点即可。这里不再进行讲解。
关于双链表的实现原理与单链表相似,具体分析请看我的下一篇博客。