首先看一个python的小例子。在python中,如果我们想交换两个数的值的时候我们可以这样操作:
如:
a = 10
b = 20
如果此时我们想交换a,b的值可以直接执行 :
a, b = b, a
这样就可以实现a, b值得交换,说明在python中 “=” 表示的是指向的功能,a = 10 表示 a 指向的是10这个数的地址,b = 20指的是20这个数的地址,这样我们在执行a, b = b, a时相当于交换了a和b所指向的地址,所以python中的 “=” 我们可以理解成是C语言中的指针,下面我们简单说一下单链表的概念。
链表是由多个节点顺序串起来的一种数据结构,每个节点分为两个部分:数据区和链接区,数据区存放当前节点所存储的数据,链接区指向下一个节点,如下图所示:
节点(Node):,elem代表数据区,next代表链接区指向下一个节点,如果不存在下一个节点,则指向None。
单向链表:
head表示头节点,如果链表为空,则head指向的就是None;最后一个节点,也就是尾节点的链接区指向None。
上面我们已经分析过,python中的 “=” 相当于指向的意思,所以我们在链表中表示指向功能时可以用 “=” 表示。比如我们在创建节点时,因为是单个的节点,所以需要把节点的链接区next指向空,此时我们就可以用下面这段代码表示next指向的是None:
self.next = None
下面给个完整的代码实现,有我自己的注释方便大家阅读:
# -*- coding: utf-8 -*-
class Node(object):
"""单链表节点类"""
def __init__(self, elem):
self.elem = elem
self.next = None
class SingleLinkList(object):
"""单链表类"""
# 创建链表之后,为了使链表对象能够与节点对象产生联系,需要给链表定义头节点
# 如果没有头节点,则默认为空,所以node的默认值应为None
def __init__(self, node=None):
self.__head = node
def is_empty(self):
"""判断链表是否为空"""
# 判断链表是否为空只需要看头节点是否为空
# 或者用(self._head == None)
return self.__head is None
def length(self):
"""链表长度"""
# 判断链表的长度,其实就是计算链表中的节点数,因为链表中的元素都是以节点的形式存储
# 首先通过head节点找到第一个节点的位置,然后依次往后数就行了
# 需要一个中间变量cur作为游标,cur本身也是个节点对象,记录当前跑到哪个节点了,
# cur一开始的位置就是头节点head的位置
# count记录节点数量
cur = self.__head
count = 0
while cur is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
# 遍历和计算长度类似
# 为了让显示的更好看,我们打印的时候不进行换行处理,赋值参数end=' '即可
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
print('') # 这句语句的作用是为了停止换行
def add(self, item):
"""在头部添加元素"""
# 想在头部添加元素N,那么就要使原始链表的头节点指向N,而N指向原始链表的第一个节点
# 为了不使原始链断掉,所以先是把N节点的链接区指向原始头节点,再把N节点的地址复制给头节点
node = Node(item)
node.next = self.__head
self.__head = node
def append(self, item):
"""在尾部添加"""
# 因为传入进来的是个真真切切的数据,而不是节点,所以需要把数据定义成节点
# 首先要判断链表是否为空,如果是空,就可直接把头节点赋值为需要添加的节点node
# 否则我们需要先遍历到链表的尾部,让链表的最后一个节点指向我们要添加的节点
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
def insert(self, pos, item):
"""指定位置添加元素"""
# pre也是一个游标,只不过代表的是我们需要添加的位置的前一个节点
# 比如我们执行insert(2, 11),表示在索引为2的位置插入11,即我们需要在原始链表索引为2的位置前面插入11
# 游标需要移动的次数就是pos-1 当pos小于0时默认是头部插入,大于链表长度默认尾部插入
# 因为pos表示的也是索引位置,链表的最后一个节点的索引是链表长度减1,所以大于的应该是length()-1
if pos <= 0:
self.add(item)
elif pos > (self.length()-1):
self.append(item)
else:
node = Node(item)
pre = self.__head
count = 0
while count < (pos-1):
count += 1
pre = pre.next
node.next = pre.next
pre.next = node
def remove(self, item):
"""删除元素"""
# 这里需要用到两个游标,删除操作只需要将前一个节点的next指向当前节点的next就行了
# 首先让pre是个空节点,cur指向头节点,然后cur每往后移一位,pre跟着移动一位
# 考虑特殊情况,如果cur当前指向的是头节点,说明需要删除的就是头节点,所以直接将头节点指向下一个节点
# 一旦执行完删除就要退出循环
pre = None
cur = self.__head
while cur is not None:
if cur.elem == item:
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, item):
"""查找元素是否存在"""
# 需要不断遍历链表,如果cur当前所在位置对应的元素就是想查找的元素,则返回True
# 否则就不断移动游标 知道所有链表元素都遍历完还没有找到,则返回False
# 程序只要遇到return就会停止,所以当前面找到元素时就会执行return True,程序就会直接停止
# 就不会再执行下面的return False了
cur = self.__head
while cur is not None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
sll = SingleLinkList()
print(sll.is_empty()) # 首先看看是不是为空
print(sll.length())
sll.append(1) # 添加一个元素之后看看是不是为空
print(sll.is_empty())
print(sll.length())
# 下面执行多段代码之后来验证功能是否可以用
sll.append(2)
sll.add(10)
sll.insert(-1, 200)
sll.append(3)
sll.append(4)
sll.insert(20, 100) # 这里我们插入的位置是20,大于了链表的长度,默认是尾部插入
sll.append(5)
sll.insert(2, 11)
sll.remove(100)
sll.travel()
运行之后结果如下:
True
0
False
1
200 10 11 1 2 3 4 5
共同学习,理解不到之处,欢迎指正。