python链表指针的理解_Python没有指针,如何解算法题?

花下猫语:今天一大早,读者群里又讨论了 Python 的“指针”问题。恰好今天还看到青南同学刚发布的一篇文章,它从实用的层面介绍了怎么用 Python 解“指针”类的题目,正好分享一下~

c2fdba3df093dde82ae17112c4f141a8.png

剧照 | 《如懿传》

原标题:《举一反三:三种问题,两个指针,一种方法》

在我们做算法题的时候,如果大家多总结解题方法,就会发现很多题目的解题方法实际上是完全一样的。今天我们就来看三道链表相关的题目。可以使用同一种方法来解决。

虽然题目中用到了指针,但我们知道,Python 是没有指针的,所以在 Python 里面,这里实际上指的是引用。不过由于“指针”这个词更加形象,所以下文我们还是会用指针来表示对一个对象的应用。

先来看我们将要解决的三道题目:

题目1:只扫描一次链表,O(1)空间复杂度,返回链表倒数第 k 个节点。 题目2:只扫描一次链表,O(1)空间复杂度,返回链表中间的节点。 题目3:空间复杂度(1),查询链表是否有环。

其中前两道题要求只能扫描链表一次。但是大家可能会有疑问,例如对于第2题,都不知道链表一共有多少个节点,怎么可能知道中间的节点是哪个?但如果提前把链表扫描一遍,知道一共有多少个节点了,又不能再次扫描链表,那么就必须把每个节点和序号都存下来,这样空间复杂度就不可能是O(1)了。

我们先来看看第2题,找到链表中间的节点。

从下图的两个链表可以看到:

5440de3f49db5750ce32d90e35c953a4.png

链表有 n 个节点,如果 n 为奇数,那么中间的节点在第(n + 1) / 2个节点。如果 n 为偶数,那么中间的节点在第n / 2个节点。

这个信息怎么使用呢?我们看下面一个表格:

1598dd2717bcbc98ea006b4bf0d599fc.png

既然如此,如果我们在链表里面有两个指针(引用),其中一个每次移动2个节点,另一个每次移动一个节点。这样当快的指针移动到了末尾,慢的指针刚刚好指向中间的节点。

用代码来表示:

def find_mid(node):

if not node:

return None

if not node.next:

return node

fast = slow = node

while fast.next and fast.next.next:

slow = slow.next

fast = fast.next.next

return slow

返回的 slow 就是最中间的节点。

再来看第3道题。跟第二题一样,也是一快一慢两个指针,如果链表有环,那么快的指针会绕到慢的指针的后面,然后追上来。只要看快的指针是否跟慢的指针重合,就知道是否有环了:

def find_cycle(node):

if not node:

return False

slow = fast = node

while fast:

fast = fast.next

if not fast: # 快的指针到了链表末尾,说明没有环

return False

if fast is slow: # 快的指针追上了慢的指针,说明有环

return True

fast = fast.next

if fast is slow:

return True

slow = slow.next

return False

再来看第一题。跟第二题实际上也是一样的。只不过,这次两个指针是移动速度是一样的。但是,一种一个指针先移动 k 个节点,然后两个指针再开始同时移动。这样两个指针中间始终会间隔 k 个节点。这样一来,当先走的指针到了None,后走的指针刚刚好走到倒数第 k 个节点。

不过,在解决这道题的时候,需要考虑,k 如果大于链表长度的时候,应该要返回错误信息。对应的代码如下:

def find_reverse_k(node, k):

if not node or k == 0:

return None

front = behind = node

window = 0

while front:

window += 1

front = front.next

if window == k:

break

else: # while ... else 语法,如果循环正常结束,就会进入 else

raise Exception('k 比链表长度还长!')

while front:

front = front.next

behind = behind.next

return behind

如果大家观察上面三个问题的解决代码,会发现他们都是使用了两个指针,通过两个指针之间的节点差来解决问题的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值