python3怎么创建一个链表_不找对象,怎么实现单向链表(3)

c212c0002d9520c4fe1d1fcdeb020ab3.png

a4dd0ffcb828ccf228c5033511820cb4.png

就在昨天,抖音也步了 B 站的后尘——转做学习网站了。

所以啊同学们,学习才符合如今的核心价值观,学习才是当下最潮流!

04abcc7cbcebcb6ddc8c23e0c5f241d6.gif

继续努力!干巴爹!

939d6314c2dd69148585aa6f55076231.png

上期回顾

之前已经实现了这些函数:

# 创建节点
create_node(data, next)

# 为 cur_node 设置下一节点为 next_node
set_next_node(cur_node, next_node)

# 获取当前节点的下一节点
get_next_node(cur_node)

如何表示链表?

链表是一个一个节点构成的,不使用面向对象技术,如何表示链表呢?

可以想象有一根铁链,我们怎么把它拎起来呢?如果我们将一个中间节点作为抓手,可以么?

2749ed35e271a2846160a77fe81c6f3f.png

如果将单向链表的中间节点作为链表入口,我们知道单向链表的节点不允许向前查找节点,也就是链表中 head 节点左侧的所有数据都将丢失。

显然我们不能允许这种情况发生,所以,对于单向链表,需要一个没有左侧节点的 Node 作为链表的头部,也就是链表的第一个节点。

22da478bb5727cb9d63da9aeebda6ef7.png

由于没有比第一个节点更早的数据,所以这样识别一个单向链表就不会丢失数据。

对于空的链表,它没有任何节点,为了可以表示这种链表,我们将 head 也作为一个链表节点,这样即使是空链表,也会占有一个空间。head 节点的数据是 None,因为它不会被用来保存数据,只是用来表示这个单向链表。以后我们就用一个单向链表的 head 节点,来指代整个链表,我将 head 节点直接命名为 chain_entry,意思就是这里是链表的入口。

99e4b4c84723897d59fd683ca614d0ae.png

创建链表

下面我们可以开始编写链表相关的函数了。

第一个,创建链表,上面已经提到,我们会直接用一个特殊的节点(数据为 None)来表示链表的入口。所以创建链表实际上是创建一个空链表,这个链表里只有 chain_entry 节点。

def create_chain():
	entry = create_node() # 默认 data 是 None,所以不填参数也可以
	# 为了方便,我们为 entry 增加一个 length 字段,保存链表的长度,刚开始是 0
	entry['length'] = 0
	return entry

这样我们就创建了一个 chain_entry,我们还为它增加了记录长度的字段 “length”。

chain_entry 被用来指代整个链表,它刚被创建的时候,看起来是这样的:

473daea13b2e00eb1cf52bfd18f59c25.png

插入操作

由于我们对链表的访问,都是从 chain_entry 开始,依次访问第一个、第二个··· 节点,单向链表操作最快的,就是对第一个节点的增删,以为从 chain_entry 进来,第一个看到的就是它。

我们使用链表的时候,关注的是 “数据”:

  1. 我们将一个数据插入了链表,
  2. 而不会考虑先创建一个包含数据的节点,再把节点插入到链表头部,

第 1 种说法简短、清晰,是第 2 种说法的抽象。第 2 种说法是第 1 种的细节。

“抽象” 对于构建大型系统是有益的,这种抽象是代码分层和设计逻辑分层的目标。

继续探讨向链表插入数据的问题,第 2 种表述就是插入节点的基本操作:

  1. 创建一个包含数据的节点。
  2. 将节点插入链表头部。

创建包含数据的节点,我们会使用 create_node 创建,包含新数据的节点已经准备好!

023c0be81a29213310e68b53c70faf21.png

如何向链表头部插入数据呢?

f3e80507b8a45ec3ccb581fa9de86475.png
  1. 将第一个节点设置为新节点的下一个节点。
  2. 将新节点设置为 entry 的下一个节点。

一定要注意步骤,如果颠倒过来,就需要使用额外的变量保存第一个节点的引用,否则新节点设置为 entry 的下一个节点时,第一个节点的引用就被覆盖丢失了。

上代码,顺便实现了一些有用的功能:

# 获取链表长度
def get_chain_length(entry):
	return entry['length']

# 链表是否为空
def is_chain_empty(entry):
	return get_chain_length(entry) == 0

# 获取节点数据
def get_node_data(node):
	return node['data']

def push_chain(entry, data):
	new_node = create_node(data)
	# 为了清晰,我们单独把第一个节点保存在 first_node
	first_node = get_next_node(entry)
	set_next_node(new_node, first_node)
	set_next_node(entry, new_node)

	# 别忘了增加链表节点计数
	entry['length'] = entry['length'] + 1

# 遍历链表,便于我们查看链表内容
def chain_all(entry):
	node = entry
	while get_next_node(node):
		node = get_next_node(node)
		print(get_node_data(node))
	

可以看到,整个链表的数据都是由 push_chain 插入的,这个函数还维护了链表长度信息,所以链表的数据增删一定要使用指定函数,不要直接访问原始链表数据增删,这会造成 entry 的信息错误。

痛苦的领悟
没有面向对象技术,数据访问权限问题解决起来不是很方便。只能靠使用者自觉了。这个问题在后面面向对象版本中会得到解决。

简单测试一下

chain = create_chain()

push_chain(chain, "hello")
push_chain(chain, "world")
push_chain(chain, "!")

print("length: {}".format(get_chain_length(chain)))

print("all chain nodes data:")
chain_all(chain)

输出是这样的:

$ python test3.py
length: 3
all chain nodes data:
!
world
hello

怎么输出顺序跟插入的时候不一样呢?

因为我们每次插入的数据,都是从头部插入的,读取的时候会先读到最后插入的数据,最早插入的数据最后读到,所以就成倒序了。

下节预告

目前已经实现了链表的构造和遍历,并且将连接节点的操作隐含在链表的有关接口里,是不是很棒!

下一节我们将会继续完善链表的操作函数,比如删除节点,那将会是“不使用面向对象,实现链表” 的最后一节。

有了这一系列的基础,之后我们将迅速的实现一个面向对象的版本。

关注我的公众号,了解程序员的烧脑日常,还有开源的视频教程。

19cb71880b632b9623dc5af1864f9a0b.png
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值