用Python玩转数据结构
链表
节点类
根据在前学过的数据结构,那么必须有节点,Python里面没有指针的说法,所以我们用引用来实现
节点类的方法
节点类最基本的功能包括:更新数据,查询数据,更新后继节点和查询后继节点。
class Node(object):
def __init__(self, data):
self.data = data
self.next_node = None
def get_data(self):
"""
获取当前节点的数据
:return: 返回当前节点的数据
"""
return self.data
def set_data(self, data):
"""
设置节点的数据为传入的数据
:param data:
:return: 无返回
"""
self.data = data
def get_next(self):
"""
获取当前节带点的指向
:return: 返回指向的对象
"""
return self.next
def set_next(self, next_node):
"""
修改节点的指向
:param next_node:
:return:无返回
"""
self.next_node = next_node
链表类
我们把节点类先实现,然后再来实现链表类。我们定义一个链表类,并实现初始化方法。在初始化方法中定义了两个属性,分别是head 和length属性。head属性表明头节点,在初始化的时候指向None。length属性表明链表的长度,方便判断列表是否为空和返回链表的长度。
class Linked_list(object):
def __init__(self):
"""
初始化方法,定义一个长度属性方便统计和判断
"""
self.head = None
self.length = 0
链表类的方法
然后我们一个想想我们的链表类需要哪些方法,这是有链表的特性觉得的不是吗?大概列一个列表吧:
在末尾插入数据
在开头插入数据
在指定位置插入 数据
修改指定位置的数据
获取指定位置的数据
查找数据在链表中的数据
删除指定位置的数据
展示整个链表
删除整个链表
判断这个链表是否为空
大概就是这些看起来是不是有点多,但是不用担心,我们一点一点的来实现。
判断链表是否为空
首先是判断一个链表是否为空,这个很好实现,我们先看代码:
def isEmpty(self):
"""
判断链表是否为空,如果为空返回True,否则为False
:return:
"""
return self.length == 0
除去注释也就一行代码的事情,用链表的长度和0做一个比较,不相等返回False相等返回True然后就能很好的判断了。
获取链表的长度
同样的获取链表的长度也很简单:
def get_length(self):
return self.length
在链表末尾插入数据
然后就是在末尾插入数据:
def append(self, data_or_node):
"""
在链表的末尾增加一个节点
:param data_or_node:
:return:
"""
item = None
# 这一个判断是为了判断传进来的数据是什么类型的
if isinstance(data_or_node, Node):
item = data_or_node
else:
item = Node(data_or_node)
if not self.head:
self.head = item
self.length += 1
else:
node = self.head
while node.next_node:
node = node.next_node
node.next_node = item
self.length += 1
首先是判断传入进来的数据是什么类型,如果已经是一个节点类的话我们就不用转换,如果不是,我们就将其转换为节点类。这里用到了isinstance()方法,这个方法的作用是判一个对象是否是一个已知的类型,考虑继承的情况。然后判断这个链表的头结点是否存在,也可以通过判断链表的长度来实现。如果头节点不存在,就将这个节点变为头节点。所以有了这个引用,将item的值赋值给head。如果不是的话我们首先就要找到尾节点,通过头节点开始循环,一直找到尾节点,然后引用。不管是那种情况都不能忘记将链表的长度加上1
在链表的头部插入数据
在头部插入数据也很简单,
def add(self, data_or_node):
"""
在链表的最前方插入一个数据
:param data_or_node:
:return:
"""
if isinstance(data_or_node, Node):
item = data_or_node
else:
item = Node(data_or_node)
if not self.head:
self.head = item
self.length += 1
else:
next_node = self.head
self.head = item
item.next_node = next_node
self.length += 1
大部分都差不多,只是在判断不是空链表之后的处理有点不一样。我们将之前头节点的值赋值给一个变量,然后将我们传入的变量指向给原头节点,同时将头节点指向我们传入的变量。
在指定位置插入节点
我们可以通过指定位置插入节点这个是很必要的一个方法,先看代码:
def insert(self, index, data_or_node):
"""
根据给定的索引在链表中插入一个节点
:param index: 索引
:param data_or_node: 数据
:return: 无返回
"""
if self.isEmpty():
print("this Linked list is Empty")
return
if index < 0 or index >= self.length:
print("error: out of index ")
return
item = None
if isinstance(data_or_node, Node):
item = data_or_node
else:
item = Node(data_or_node)
if index == 0:
self.add(item)
return
j = 0
node = self.head
prev = self.head
while j < index:
prev = node
node = node.next_node
j += 1
prev.next_node = item
item.next_node = node
self.length += 1
对于所有给出索引的方法来说,我们都需要判断这个链表是否为空和给定的索引是否越界。然后再把传入的节点通过判断转换为节点类。如果索引的值是0的话就是在头部插入,我们直接调用在前面定义的内部方法就好,然后记得return结束函数,不然那你会将你的所有的节点的值都改变。然后就是关键的步骤,我们定义三个变量,作用分别是:计数器、记录前置节点和记录当前节点。最开始的节点都为头节点,然后开始循环,当j小于索引值的时候呢,循环,同时将node的值赋值给prev,将节点记录下来。当找到插入节点的位置的时候,我们将prev指向我们传入的节点,然后将传入的节点再指向给item就完成在指定位置插入。只要是涉及到节点个数的变化的操作的时候一定要记得对length进行对应的操作。
删除指定位置的节点
说完了增加,按照惯例就是删除了。首先是删除指定位置的节点:
def delete(self, index):
"""
根据给定的索引删除一个节点
:param index: 索引
:return: 无返回
"""
if self.isEmpty():
print("this Linked list is Empty")
return
if index < 0 or index >= self.length:
print("error: out of index ")
return
if index == 0:
self.head = self.head.next_node
self.length -= 1
j = 0
node = self.head
prev = self.head
while j < index:
prev = node
node = node.next_node
j += 1
prev.next_node = node.next_node
self.length -= 1
首先还是几个判断,然后如果是删除头节点,我们将头节点的值指向头节点的下一个。如果不是,就和插入很像了,找到索引对应的位置,然后将prev直接指向node的下一个,然后链表长度减1。
删除整个链表
删除了一个节点就像删除整个链表,这个代码也超级简单:
def clear(self):
"""
清除链表
:return: 无返回
"""
self.head = None
self.length = 0
我们只需要将链表的头节点再指向为空,然后长度变为0就好。
修改指定位置的节点的值
删除也很简单,下面来说修改,修改指定位置节点的值:
def updata_node_item(self, index, data):
"""
根据给出的索引更新一个节点的值
:param index: 索引
:param data: 更新的值
:return: 无返回
"""
if self.isEmpty():
print("this Linked list is Empty")
return
if index < 0 or index >= self.length:
print("error: out of index ")
return
j = 0
node = self.head
while j < index:
node = node.next_node
j += 1
node.data = data
就只是常规的判断然后是循环找到节点后修改值就好。
根据索引获得对应节点的值
查找是一个很重要的功能,有两种需要我们实现,先来第一种:根据索引查找对应的值:
def get_node_item(self, index):
"""
根据索引获取获取节点的值
:param index: 索引
:return: 返回索引对应节点的值
"""
if self.isEmpty():
print("this Linked list is Empty")
return
if index < 0 or index >= self.length:
print("error: out of index ")
return
j = 0
node = self.head
while j < index:
node = node.next_node
j += 1
return node.data
和修改很类似,不同的是我们直接返回值就好
根据值查对应的节点的索引
我们也可以传一个值然后找对应的索引,这样就能做到:
def get_node_index(self, data):
"""
查找一个数据的索引
:param data:数据
:return:索引
"""
node = self.head
j = 0
while node.next_node:
if node.data == data:
return j
else:
node = node.next_node
j += 1
if j == self.length:
print("%s is not found " % str(data))
其实都差不多,但是呢有一种情况需要注意,就是如果当j的值都等于链表的长度的时候说明,链表当中没有这个值。
展示链表
其实到这里就差不多了,然后就是一个展示整个链表的方法:
def show(self):
"""
展示链表
:return:
"""
if self.isEmpty():
print("this is a empty linked list")
n = 0
node = self.head
while n < self.length:
print("node %d : %s" % (n, node.data))
node = node.next_node
n += 1
试试效果
mylinked = Linked_list()
for i in range(10):
mylinked.append(i)
mylinked.show()
print(mylinked.get_length())
print(mylinked.get_node_index(4))
print(mylinked.get_node_item(5))
print(mylinked.updata_node_item(4, 20))
print(mylinked.delete(8))
print(mylinked.insert(0, 420))
mylinked.show()
结果:
node 0 : 0
node 1 : 1
node 2 : 2
node 3 : 3
node 4 : 4
node 5 : 5
node 6 : 6
node 7 : 7
node 8 : 8
node 9 : 9
10
4
5
None
None
None
node 0 : 420
node 1 : 0
node 2 : 1
node 3 : 2
node 4 : 3
node 5 : 20
node 6 : 5
node 7 : 6
node 8 : 7
node 9 : 9
Process finished with exit code 0