玩指针是许多算法的精髓,就是要像六脉神剑一样,指针乱指一通之后,内存中的数据变得井然有序。
操作指针真的是太好玩了。下面出几道题。
一、不用额外空间翻转单链表
给定一个单链表,要求翻转之。
import random
"""
O(1)空间复杂度翻转链表
方法:one->two->three 三元组构成一个窗口,每次都翻转窗口内的元素,然后将
窗口从左往右滑动
"""
list_length = 4
class Node:
def __init__(self, value, nex):
self.value = value
self.next = nex
def generate_list():
a = Node(0, None)
now = a
for i in range(list_length):
x = Node(random.randint(0, 100), None)
now.next = x
now = x
return a.next
def print_list(x):
while x:
print(x.value, end=' ')
x = x.next
print()
def reverse(x):
one = x
two = one.next
one.next = None
# 如果只有一个元素,那么直接返回
if not two: return x
three = two.next
while 1:
two.next = one
if not three: break
temp = three.next
three.next = two
one = two
two = three
three = temp
return two
l = generate_list()
print_list(l)
print_list(reverse(l))
二、两个无环单链表求第一个公共结点
方法非常简单:求出两个链表长度之差,记为x;然后让较长的链表先走x步;最后让两个链表携手并进。
时间复杂度:O(N+M)
空间复杂度:O(1)
三、判断单链表中是否有环,如果有环,找出环的第一个结点
有一个著名的ro算法,甲乙两个人同时从起点出发,甲每次走1格,乙每次走两格。如果甲乙二人有一人走到了终点,那么说明无环;如果甲乙二人都没有走到终点,而是经过s次走路之后,甲乙二人相遇了,说明有环,并且环的长度必然是s。因为在x次走路中,甲比乙少走了s步,也就是说环的长度是s。
----x--|----y----|
|____z____|
如上图所示,将链表三部分的长度记为x,y,z,甲乙二人在y线段的最右侧相遇。那么甲走了x+y步,乙走了x+y+z+y步,显然(x+y)乘以2等于x+y+z+y。所以z=x。要想找到环中的第一个结点,只需要发现一个规律:让乙回到起点处(x最左侧)且将乙的步伐改为每次走一格,甲继续从y的右侧(相遇点)出发,两人下次相遇的地方就是环中的第一个结点,因为x=z!
有一道类似的趣题:无限一维空间中有两个生物,这两个生物之间无法进行交流,现在让你用如下四条汇编指令编程:
(1)move left
(2)move right
(3)if this position is visited,then goto 语句标号
(4)goto语句(程序中可以使用标号)
现在你需要开发一套汇编代码,给这两个生物安装上这套程序,保证这两个生物一定会相遇。
代码如下:
loop:
if this position is visited,then goto movefast
moveleft
goto loop
movefast:
moveleft
moveleft
goto loop
原理是:两个生物一前一后同时往一个方向走,速度相同。如果看见了另一个生物的轨迹,就开始提速。这样就肯定能够追上前面的生物。
四、给定两个单链表,判定它们是否拥有共同结点
--------- \________
/
_________/
这个问题比较复杂,需要分类讨论。
1、如果两个链表都是无环单链表,那么只需要判断最后一个结点是否相同即可!
2、如果一个链表是无环单链表,而另一个链表是有环的,那么这两个链表必然无公共结点!
3、如果两个链表都是有环的:
(1)假如两个链表相交,那么环必然是这两个链表的公共部分,只需要找到链表1环上的某个结点x,看看链表2是否会经过结点x
(2)假如两个链表不相交,那么它们的环必然不相交
五、求两个有环单链表的第一个公共结点(两个链表保证有公共结点)
首先用ro算法求出环上的某个点,把这个点当做链表的结尾,相当于把这个结点的next设为null。从而问题转换成了两条无环单链表求第一个公共结点。这个问题复杂度也是O(M+N)