链表
顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
**链表(Linked list)**是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
单向链表
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
python中变量的本质就是指向元素的地址。所以链表类中,没有变量是地址,虽然看起来没有操作地址,但是在python里,变量就是在操作地址。
节点实现
class SingleNode(object):
"""单链表的结点"""
def __init__(self,item):
# _item存放数据元素
self.item = item
# _next是下一个节点的标识
self.next = None
单链表操作
- is_empty() 链表是否为空
- length() 链表长度
- travel() 遍历整个链表
- add(item) 链表头部添加元素
- append(item) 链表尾部添加元素
- insert(pos, item) 指定位置添加元素
- remove(item) 删除节点
- search(item) 查找节点是否存在
class ListNode:
#构造函数
def __init__(self,x):
self.val = x
self.next = None
class SingleLinkList(object):
# 如果先有一个ListNode,可以传入linklist中,也可以不传,构建空链表
#本身需要保存一个头节点属性
#私有属性,_head
def __init__(self,node=None):
self.__head = node
def is_empty(self):
return self.__head == None
def length(self):
#cur初始指向头节点
cur = self.__head
count = 0 #用来数数
while cur != None: #空链表进不了循环,length = 0
count += 1
#指针向下一个移动
cur = cur.next
return count
def travel(self):
cur = self.__head
while cur != None:
print(cur.val, end=" ") # print去掉换行用end=" "
cur = cur.next
def add(self, val): # 头插
node = ListNode(val)
node.next = self.__head
self.__head = node
def append(self,val): # 尾插
node = ListNode(val)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
def insert(self, pos, val): #任意位置插入
if pos <=0: #就是头插法
self.add(val)
elif pos > (self.length()-1):#最后一位的下标为length-1,因为从0开始
self.append(val)
# pos 是从0开始的
node = ListNode(val)
pre = self.__head
count = 0
while count < (pos -1):
count += 1
pre = pre.next
#当循环退出时,指针指向指定位置前一个位置
node.next = pre.next
pre.next = node
def search(self,val):
cur = self.__head
while cur != None:
if cur.val == val:
return True
else:
cur = cur.next
return false
# def remove(self, val):
# pre = self.__head
# while pre != None:
# if pre.val == val:
# # 一个指针但是只能删中间的节点
# pre.val = pre.next.val
# pre.next = pre.next.next
# else:
# pre = pre.next
def remove(self,val):
## 设置双指针
cur = self.__head
pre = None
while cur != None:
if cur.val == val:
if not pre: #cur就是第一个节点,pre为None
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
单链表和顺序表对比
注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。
顺序表优点在于访问某个元素的时候可以一次性定位,但是如果要修改,它的空间是连续的一定的,在改动时有可能需要对内存进行操作,其他元素也需要移动拷贝。
链表对存储空间的利用跟好,可以利用零散的空间,但是查找需要遍历。