数据结构-链表
链表是实现了数据之间保持逻辑顺序,但存储空间不必按顺序的方法。可以用一个图来表示这种链表的数据结构:
链表中的基本要素:
- 结点(也可以叫节点或元素),每一个结点有两个域,左边部分叫:值域,用于存放用户数据;右边叫:指针域,一般是存储着到下一个元素的指针
- head结点,head是一个特殊的结节,head结点永远指向第一个结点
- tail结点,tail结点也是一个特殊的结点,tail结点永远指向最后一个节点
- None,链表中最后一个结点指针域的指针指向None值,因也叫:接地点,所以有些资料上用电气上的接地符号代表None
链表的常用方法:
- LinkedList() 创建空链表,不需要参数,返回值是空链表
- is_empty() 测试链表是否为空,不需要参数,返回值是布尔值
- append(data) 在尾部增加一个元素作为列表最后一个。参数是要追加的元素,无返回值
- iter() 遍历链表,无参数,无返回值,此方法一般是一个生成器
- insert(idx,value) 插入一个元素,参数为插入元素的索引和值
- remove(idx)移除1个元素,参数为要移除的元素或索引,并修改链表
- size() 返回链表的元素数,不需要参数,返回值是个整数
- search(item) 查找链表某元素,参数为要查找的元素或索引,返回是布尔值
节点类
python用类来实现链表的数据结构,节点(Node)是实现链表的基本模块,每个节点至少包括两个重要部分。首先,包括节点自身的数据,称为“数据域”(也叫值域)。其次,每个节点包括下一个节点的“引用”(也叫指针)
下边的代码用于实现一个Node类:
1 class Node: 2 '''定义一个节点类''' 3 def __init__(self, data): 4 self.data = data 5 self.next = None
此节点类只有一个构建函数,接收一个数据参数,其中next表示指针域的指针,实例化后得到一个节点对象,如下:
1 node = Node(4)
此节点对象数据为4,指针指向None。
这样一个节点对象可以用一个图例来更形象的说明,如下:
链表类
先来看LinkedList类的构建函数:
1 class LinkedList: 2 '''构建LinkedList类''' 3 def __init__(self): 4 self.head = None 5 self.tail = None
此类实例后会生成一个链表对象,初始化了head和tail节点,且两节点都指向None,实例化代码如下:
1 link_list = LinkedList()
也可以用图形象的表示这个链表对象,如下:
is_empty方法实现
is_empty方法检查链表是否是一个空链表,这个方法只需要检查head节点是否指向None即可,代码如下:
1 def is_empty(self): 2 return self.head is None
如果是空列表返回True,否则返回False
append方法实现
append方法表示增加元素到链表,这和insert方法不同,前者使新增加的元素成为链表中第一个节点,而后者是根据索引值来判断插入到链表的哪个位置。代码如下:
1 def append(self, data): 2 node = Node(data) 3 if self.head is None: 4 self.head = Node 5 self.tail = Node 6 else: 7 self.tail.next = node 8 self.tail = node
既然要新增加节点,首先把Node类实例化得到一个node对象。这里有两种情况需要考虑,一是链表是一个空链表时怎样append一个节点;二是当链表不是空链表时又怎样append一个节点?
当if self.head is None: -> True
时,把链表的head和tail都指向了node,假如我们执行了
1 link_list(append(4))
此时的链表结构如下图:
当if self.head is None: -> False
时,说明链表已经增加了一个节点了,再增加一个节点时head已经指向了第一个节点,所以不为None,比如增加的第二个节点为:
1 link_list(append(5))
增加第二个节点的操作需要分两步完成,第一步:self.tail.next = node
,即把上一个节点的next指针指向当前node;第二步:self.tail = node
,把tail移动到node,如下图:
移动完成后就成这样了:
当增加第三个、第四个等节点时,按照上边的操作依次类推。
iter方法实现
iter方法表示遍历链表。在遍历链表时也要首先考虑空链表的情况。遍历链表时从head开始,直到一个节点的next指向None结束,代码如下:
1 def iter(self): 2 if not self.head: 3 return 4 cur = self.head 5 yield cur.data 6 while cur.next: 7 cur = cur.next 8 yield cur.data
当是遍历一个空链表时,if not self.head : -> True
,直接返回None;如果不是空链表就让一个局部变量cur指向head,并把head的data属性yield出来,再对cur的next指针指向的对象做while循环,直到next指向None,这样就遍历了链表。
insert方法实现
假如采取append方法又增加了两个节点,增加完成后如下图:
如果想在数据域为6的那节点处插入一个节点,需要做的操作有两步:
- 把新节点的next指针指向数据域为6的这个节点,即为数据域为5节点的next指向指向的对象
- 把数据域为5节点的next指针指向新加的节点
注: 这两个步骤不能颠倒,如果颠倒,数据域为6的节点会被丢失,数据域为7的节点不再是链表的节点。
示意图如下:
还要额外考虑两种情况:
- 空链表时
- 插入位置超出链表节点的长度时
- 插入位置是链表的最后一个节点时,需要移动tail
当是在链表最后一个节点插入时,示意图如下:
要在指定的索引位置插入一个节点,前提是需要找到这个位置,在链表中只有采用遍历的方式,具有O(n)的速度,最糟糕时会遍历链表的所有节点,而当找到插入点时,我们并不需要当前节点的信息,而是需要前一个节点的信息,所以代码中巧妙的使用了while cur_idx < idx-1:的方式,这样能使用cur这个变量能指向插入点上一个节点对象。
实现insert方法的代码如下:
1 def insert(self, idx, value): 2 cur = self.head 3 cur_idx = 0 4 if cur is None: 5 raise Exception('The list is an empty list') 6 while cur_idx < idx - 1: 7 cur = cur.next 8 if cur is None: 9 raise Exception('list length less than index') 10 cur_idx += 1 11 node = Node(value) 12 node.next = cur.next 13 cur.next = node 14 if node.next is None: 15 self.tail = node
remove方法实现
remove方法接收一个idx参数,表示要删除节点的索引,此方法要考虑以下几种情况:
- 空链表,直接抛出异常
- 删除第一个节点时,移动head到删除节点的next指针指向的对象
- 链表只有一个节点时,把head与tail都指向None即可
- 删除最后一个节点时,需要移动tail到上一个节点
- 遍历链表时要判断给定的索引是否大于链表的长度,如果大于则抛出异常信息
请看下边图例:
以下为remove函数的代码:
1 def remove(self, idx): 2 cur = self.head 3 cur_idx = 0 4 if self.head is None: # 空链表时 5 raise Exception('The list is an empty list') 6 while cur_idx < idx - 1: 7 cur = cur.next 8 if cur is None: 9 raise Exception('list length less than index') 10 cur_idx += 1 11 if idx == 0: # 当删除第一个节点时 12 self.head = cur.next 13 cur = cur.next 14 return 15 if self.head is self.tail: # 当只有一个节点的链表时 16 self.head = None 17 self.tail = None 18 return 19 cur.next = cur.next.next 20 if cur.next is None: # 当删除的节点是链表最后一个节点时 21 self.tail = cur
size函数实现
size函数不接收参数,返回链表中节点的个数,要获得链表的节点个数,必定会遍历链表,直到最后一个节点的next指针指向None时链表遍历完成,遍历时可以用一个累加器来计算节点的个数,如下代码:
1 def size(self): 2 current = self.head 3 count = 0 4 if current is None: 5 return 'The list is an empty list' 6 while current is not None: 7 count += 1 8 current = current.next 9 return count
search函数实现
search函数接收一个item参数,表示查找节点中数据域的值。search函数遍历链表,每到一个节点把当前节点的data值与item作比较,最糟糕的情况下会全遍历链表。如果查找到有些数据则返回True,否则返回False,代码如下:
1 def search(self, item): 2 current = self.head 3 found = False 4 while current is not None and not found: 5 if current.data == item: 6 found = True 7 else: 8 current = current.next 9 return found
Node类与LinkedList类完整代码
最后把Node类和LinkerList类的完整代码整理如下:
Node类:
1 class Node: 2 def __init__(self, data): 3 self.data = data 4 self.next = None
LinkedList类及调度代码:
1 class LinkedList: 2 def __init__(self): 3 self.head = None 4 self.tail = None 5 6 def is_empty(self): 7 return self.head is None 8 9 def append(self, data): 10 node = Node(data) 11 if self.head is None: 12 self.head = node 13 self.tail = node 14 else: 15 self.tail.next = node 16 self.tail = node 17 18 def iter(self): 19 if not self.head: 20 return 21 cur = self.head 22 yield cur.data 23 while cur.next: 24 cur = cur.next 25 yield cur.data 26 27 def insert(self, idx, value): 28 cur = self.head 29 cur_idx = 0 30 if cur is None: # 判断是否是空链表 31 raise Exception('The list is an empty list') 32 while cur_idx < idx-1: # 遍历链表 33 cur = cur.next 34 if cur is None: # 判断是不是最后一个元素 35 raise Exception('list length less than index') 36 cur_idx += 1 37 node = Node(value) 38 node.next = cur.next 39 cur.next = node 40 if node.next is None: 41 self.tail = node 42 43 def remove(self, idx): 44 cur = self.head 45 cur_idx = 0 46 if self.head is None: # 空链表时 47 raise Exception('The list is an empty list') 48 while cur_idx < idx-1: 49 cur = cur.next 50 if cur is None: 51 raise Exception('list length less than index') 52 cur_idx += 1 53 if idx == 0: # 当删除第一个节点时 54 self.head = cur.next 55 cur = cur.next 56 return 57 if self.head is self.tail: # 当只有一个节点的链表时 58 self.head = None 59 self.tail = None 60 return 61 cur.next = cur.next.next 62 if cur.next is None: # 当删除的节点是链表最后一个节点时 63 self.tail = cur 64 65 def size(self): 66 current = self.head 67 count = 0 68 if current is None: 69 return 'The list is an empty list' 70 while current is not None: 71 count += 1 72 current = current.next 73 return count 74 75 def search(self, item): 76 current = self.head 77 found = False 78 while current is not None and not found: 79 if current.data == item: 80 found = True 81 else: 82 current = current.next 83 return found 84 85 if __name__ == '__main__': 86 link_list = LinkedList() 87 for i in range(150): 88 link_list.append(i) 89 # print(link_list.is_empty()) 90 # link_list.insert(10, 30) 91 92 # link_list.remove(0) 93 94 for node in link_list.iter(): 95 print('node is {0}'.format(node)) 96 print(link_list.size()) 97 # print(link_list.search(20))