算法二:链表(数据结构部分)
线性表分为:
- 连续存储的顺序表
- 非连续存储的,通过链条串连起来的链表
单向链表:


单链表的实现:

先实现节点:
关于变量与内存的关系:
在其它语言中,定义变量,变量存储的就是变量的内容;
在python中,定义变量,变量是一个内存地址,它并不保存变量的内容,而是保存"变量的内容"所指向的内存地址。
因此,也可以说,python中的变量,是内存地址的引用,而不是直接存储内容。
示例:

a变量对应一个内存地址(假设0x10),在这个内存址址中,存储的并不是10;而是10也有一个内存地址来存储(0x20),a(0x10)存储的就是10所指向的内存地址0x20。
变量a(0x10)存储的是一个内存地址(0x20),而0x20存储的是10.
同样的,对于函数和类,一样是使用一块内存地址来存储;当它函数或类赋值给变量a或node1时,其实是变量a或node1,对函数和类所在内存地址的引用。

对于节点,使用单向链表实现它时,需要保存两个东西,一个是数据本身的内容,一个是下一个节点地址的内存地址。
这两个东西,可以用元组,也可以用列表来保存;这里使用,在其它语言中,也通用的方法,将这两个东西保存为两个对象,如self.node和self.next。节点的具体实现:
class Node: """节点""" def __init__(self, element): self.elem = element # 保存数据的数据域 self.next = None # 保存链接的链接域 node = Node(10)
单链表的具体实现:
class SingleLinkList: """单链表""" def __init__(self, node=None): """每个链表必须存在一个头节点:创建空链表时,头部为None; 创建带头部节点的链表时,头部节点为传递进来的头部节点""" self.__head = node def is_empty(self): """链表是否为空""" return self.__head is None def length(self): """链表的长度""" current = self.__head # 当前指针的位置:用于移动偏历节点的游标 count = 0 # count记录节点的数量 while current is not None: # 遍历,直到current.next指向None(当count实始值为1时),即最后一个节点 # 遍历,直到current等于None(当count实始值为0时),即最后一个节点 count += 1 current = current.next return count def travel(self): """遍历整个链表""" current = self.__head while current is not None: print(current.elem) current = current.next def add(self, item): """链表头部添加元素: item传递数据,而不是节点,这样外部不需要关心节点""" def append(self, item): """链表尾部添加元素: item传递数据,而不是节点,这样外部不需要关心节点""" node = Node(item) if self.is_empty(): # 这里使用is_empty而不是self.__head,会让代码更容易阅读 self.__head = node else: current = self.__head while current.next is not None: current = current.next current.next = node def insert(self, pos, item): """在指定位置添加元素""" def remove(self, item): """删除节点""" def serach(self, item): """查找节点是否存在""" if __name__ == '__main__': # 先实现并验证is_empty,length,travel方法是否正确;如果正确继续剩余方法的实现 sll = SingleLinkList() print(sll.is_empty()) print(sll.length()) print(sll.travel()) sll.append(1) print(sll.is_empty()) print(sll.length()) print(sll.travel()) sll.append(2) sll.append(3) sll.append(4) print(sll.is_empty()) print(sll.length()) print(sll.travel())
链表与节点的关系:
count初始值为0时,判断游标cur直到最后一个节点的下一个节点None

count初始值为1时,判断游标cur直到最后一个节点;此时特殊情况,空链表要处理,且在遍历时要处理最后一个节点。因此这种方式,不太好。

接下来,先实现特殊情况add,然后实现insert
add:

insert:

search、remove:
class Node: """节点""" def __init__(self, element): self.elem = element # 保存数据的数据域 self.next = None # 保存链接的链接域 class SingleLinkList: """单链表""" def __init__(self, node=None): """每个链表必须存在一个头节点:创建空链表时,头部为None; 创建带头部节点的链表时,头部节点为传递进来的头部节点""" self.__head = node def is_empty(self): """链表是否为空""" return self.__head is None def length(self): """链表的长度""" current = self.__head # 当前指针的位置:用于移动偏历节点的游标 count = 0 # count记录节点的数量 while current is not None: # 遍历,直到current.next指向None(当count实始值为1时),即最后一个节点 # 遍历,直到current等于None(当count实始值为0时),即最后一个节点 count += 1 current = current.next return count def travel(self): """遍历整个链表""" current = self.__head while current is not None: print(current.elem) current = current.next print("") def add(self, item): """链表头部添加元素: item传递数据,而不是节点,这样外部不需要关心节点""" node = Node(item) node.next = self.__head self.__head = node # 要分别验证原链表是否为空两种情况 def append(self, item): """链表尾部添加元素: item传递数据,而不是节点,这样外部不需要关心节点""" node = Node(item) if self.is_empty(): # 这里使用is_empty而不是self.__head,会让代码更容易阅读 self.__head = node else: current = self.__head while current.next is not None: current = current.next current.next = node def insert(self, pos, item): """在指定位置添加元素 :param post 从0开始 """ if pos < 0: self.add(item) elif pos > self.length() - 1: self.append(item) else: pre = self.__head count = 0 while count < pos - 1: count += 1 pre = pre.next node = Node(item) # 当循环退出后,pre指向pos-1位置 node.next = pre.next pre.next = node def remove(self, item): """删除节点:删除第一个elem为item的节点""" current = self.__head pre = None # 特殊情况:空链表,不进入循环 while current is not None: # 循环到最后一个节点 # 先移动pre,再移动current if current.elem == item: # 判断特殊情况:current是头节点、只有一个节点、尾节点 # if current == self.__head: if pre is None: self.__head = current.next else: pre.next = current.next break else: pre = current current = current.next def serach(self, item): """查找节点是否存在""" current = self.__head while current is not None: # 循环到最后一个节点 # 使用current还是current.next为None,要看最后一个节点情况 if current.elem == item: return True else: current = current.next return False # 检查特殊情况:空链表 if __name__ == '__main__': # 先实现并验证is_empty,length,travel方法是否正确;如果正确继续剩余方法的实现 sll = SingleLinkList() print(sll.is_empty()) print(sll.length()) sll.travel() sll.append(1) sll.add(8) print(sll.is_empty()) print(sll.length()) sll.travel() sll.append(2) sll.append(3) sll.append(4) # 8 1234 sll.insert(-1, 9) # 9 81234 sll.travel() sll.insert(3, 100) # 981 100 234 sll.travel() sll.remove(100) # 981234 sll.travel() sll.remove(4) # 98123 sll.travel()
以上用链表实现的单向链表,与用顺序表实现的单向顺序表的比较:

链表访问元素的时间复杂度为O(n),而顺序表为O(1),而且多占内存,为什么还用它?
因为顺序表要求存储空间的内存地址必须连续,在扩展顺序表的时侯,也要使得扩展后的内存地址连续,要动态重新申请一段连续内存地址,释放之前的内存地址。
因为链表不要求存储空间连续,相对灵活。
posted on 2018-01-03 13:53 myworldworld 阅读(170) 评论(0) 收藏 举报
浙公网安备 33010602011771号