爱情不是编程,婚姻更不是算法!
今天在微博上看到这句话,突然感觉很有道理,刚好一直在看数据结构,发现其实实现一个算法,其中的逻辑思维异常巧妙,前段时间总结的ListHelper就感觉很low,不过都是一种思想,今天总结的都是一些关于数据结构的内容,数据结构这块很难理解,相对而言比较抽象,但要将这种抽象的具体化,需要不断的练习。
首先看一下单链表,将线性列表中的元素分布在储存器的不同存储块,称为结点,每个结点中都持有下一个结点的引用,这样的存储结构为链表结构(尾部结点除外)。可以这样理解,我们有一个列表,每个元素都存有下一个元素的地址,这样我们就能从这个元素找到下一个元素。我们对列表的有一定的操作了,包括增删改查,那么链表当然也一样,不过实现起来要比列表复杂。
接下来我们看看对链表的操作,是如何实现增删改查的。
创建结点类,其中self.val是一个元素
class Node(object):
def __init__(self, val, next=None):
self.val = val # 有用数据
self.next = next
接下来我们对这个链表进行操作,我们创建了一个LinkList的类,让这个类继承自父类,如果我们创建的类没有继承我们就定义这个类继承object这个父类。这里用到之前的类,我们初始化这个方法,定义head为一个空值,在定义init_list方法的时候,我们传入data这个元素,让self.head=Node(None)这个就是一个链表的开头,它包含下一个数据的next地址,我们通过这个就可以拿到下一个元素的val。
class LinkList(object):
def __init__(self):
self.head = None
def init_list(self, data):
self.head = Node(None) # 链表的开头
p = self.head # 可移动变量p
for i in data:
p.next = Node(i)
p = p.next
我们在定义一个方法来打印这个链表,在这个方法中,我们创建self.head.next的对象,循环遍历,直到这个链表的值为空,只要这个对象不是None我们就打印这个对象的val。
def show(self):
p = self.head.next
while p != None:
print(p.val, end=' ')
p = p.next
print()
那么以上就是对单链表遍历的操作了。
接下来我们对链表进行一些其他操作,第一个在尾部插入新节点,那我们遍历到最后一个元素时,此时这个元素的next应该是一个None,循环遍历这个链表,如果不是空,就让这个next等于表头,如果是个空此时传入我们的数据。
同样定义一个方法,用的时候直接调用就可以了
# 尾部插入新的结点
def append(self, item):
p = self.head
while p.next is not None:
p = p.next
p.next = Node(item)
第二个获取这个链表的长度,获取链表长度这里引入之前学循环时的计数器思想,同样循环遍历next,如果不是空我们就让计数器加1如果他是一个人空值我们就返回这个数,结束当前的循环。为什么要一直遍历next呢?因为只有next中保存了下一个元素的地址,那这个next是一个空值的时候,也就是说这个链表结束了他没有保存下一个元素的地址了!
# 获取链表长度
def get_length(self):
n = 0
p = self.head
while p.next is not None:
n += 1
p = p.next
return n
第三个判断链表是否是一个空来链表,我们在拿到一个链表的时候我们也不知道这个链表里边有没有数据,那么此时我们就要先判断一下这个链表,如果是空我们就返回一个True,如果不是我们就返回一个False,这时我们调用刚刚写好的判断链表长度的方法就很简单了。
# 判断链表是否为空
def is_empty(self):
if self.get_length() == 0:
return True
else:
return False
第四个清空链表,当我们要清空一个链表的时候的,我们其实只需要做一件事就可以了,就是让表头的next等于一个空值,其他的链表自然也就断了我们就不能拿到剩余的元素了,这样我们就认为我们已经清空这个链表了!
# 清空链表
def clear(self):
self.head.next = None
第五个获取索引值,比如说我们要获取某个索引值的元素,要对这个值进行操作的时候,我们就要获取到这个元素的索引值才可以,同样定义方法,也引用到计数器的思想,首先我们让这个表头的next等于一个对象,在循环遍历这个链表的时候,计数器必须小于要索引的值,同时next不能为一个空值,如果是一个空那就等于是最后一个元素了,那么在循环遍历的时候,就让这个对象等于下一个next,同时我们还需判断这个对象,如果是一个空就抛出一个错误,如果不是我们就返回这个元素。
# 获取索引值
def get_item(self, index):
p = self.head.next
i = 0
# 没有到对应索引号并且遍历索引没有到最后就循环
while i < index and p is not None:
i += 1
p = p.next
# 如果因为p到最后了则说明越界
if p is None:
raise IndexError("list index out of range")
# i 不小于inedx说明找到索引结点了
else:
return p.val
第六个插入元素,我们要在某索引位置上插入一个元素,首先判断这个索引是不是大于0或者大于链表的长度,如果不能满足其中任何一个我们就没必要在做剩下的事了,此时就抛出错误。先对表头创建一个对象,同样引入计数器思想,这数据结构中经常会使用到计数器的思想,循环让对象一直移动到带插入元素位置的前一个,然后我就创建一个新的结点,这个方法前边也有写,接下来就插入结点,并切让这个对象的next的关于node,此时我们就插入你想要插入的元素了。
def insert(self,index,item):
if index < 0 or index > self.get_length():
raise IndexError("list index out of range")
# 让p移动到待插入位置的前一个
p = self.head
i = 0
while i < index:
p = p.next
i += 1
node = Node(item) # 创建新的结点
node.next = p.next # 插入结点
p.next = node
最后删除某一个元素,同样创建一个表头对象,循环遍历,这个对象的next,此时判断这个对象的next.val等于我们的元素,就让这个next等于这个元素的下一个next,切断了这个元素的next,此时这个元素就相当于消失了,因为我们这个是一个链表,这个元素勾这下一个元素的地址,一但切掉,这个元素就相当于消失了。如果我们遍历完了仍然没有找到,我们就要抛出错误了,证明你想要删除的这个元素没有在这个链表中。
def delete(self,item):
p = self.head
while p.next is not None:
# 如果值相等则越过中间的结点
if p.next.val == item:
p.next = p.next.next
break
p = p.next
else:
raise ValueError("x not in list")
这些是我整理的一些对链表的操作,数据结构中还有很多操作,后边有时间会慢慢整理出来!
可谓一入编程深似海,从此君郎是路人啊!此时已是深夜,分享一段话“满地都是六便士,他却抬头看见了月亮”,献给每一位在看见月亮的路上不断奔跑的人!