题目:
面试tips:
询问是否需要判断环,可微调下方代码。
思路:
思路一:
判断环是否存在:设定一快一慢指针,均从头节点出发,快指针一次走两步,慢指针一次走一步。若无环,则快指针会先到达空,返回False表示无环;若有环,则快慢指针必定相遇。前者无环快指针先到达空节点好理解,后者有环为什么快慢指针必定相遇呢?这里提供两种理解方式。
理解①:
理解②:
设慢指针刚进入环的第一个节点时,快指针(此时必定在环中)与慢指针中间相差n(n小于环的个数)个节点,则因为快指针每次走两步,慢指针每次走一步,因此快指针相对于慢指针而言快一步,因此在第n次时快指针追上慢指针。因此也是在慢指针的第一圈中被快指针追上。
若环存在,则寻找环的入口:
代码实现:
时复O(N),空复O(1)
class ListNode:
def __init__(self, val, next = None):
self.val = val
self.next = next
def arr2List(arr, pos):
prev = dummy_head = ListNode(0)
entry = None
for i in range(len(arr)):
node = ListNode(arr[i])
prev.next = node
prev = prev.next
entry = node if pos == i else entry
if i == len(arr)-1:
node.next = entry
return dummy_head.next
def findcycle(head):
# 找到环的入口
# 1 先找到快慢指针相遇之处
if not head: # 这里先排除掉空节点 是因为后面要fast.next 不能对空指针操作
return -1
left = fast = slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
# 此时slow记录了相遇位置
break
if not fast.next or not fast.next.next:
return -1
# left和slow同时走直到相遇
while left != slow:
left = left.next
slow = slow.next
return left
if __name__ == '__main__':
arr = [2, 6, 1, 5]
pos = 1
# pos表示环的入口索引,如果pos不为arr中的任一下标,则无环
head = arr2List(arr, pos)
result = findcycle(head)
if result == -1:
print('链表无环')
else:
print('环入口节点为:{}'.format(result.val))
参考资料:
1. 《剑指offer》
2. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台