链表是一种基础的数据结构,也是大公司面试中的重点考察内容。链表的实现可以很好地考察出一个人的编程基本功底和未来发展潜能。本文来探讨一下链表常见的五种问题,针对五种问题的实现思想进行深度剖析,并用python代码加以实现。
1、单链表反转问题
2、链表中环的检测问题
3、两个有序链表的合并
4、删除单链表中倒数第n个结点
5、求解单链表的中间节点
单链表反转问题,笔者已经在《leetcode 亲测有效:单链表反转的原理和python代码实现》一文中介绍的较为详细,此处不再赘述。
链表中环的检测存在多种解决思路,此处我们来具体分析一下常见思路及其复杂度。
1、暴力求解法
最简单粗暴的求解方式就是暴力求解法,就是不断寻找已知节点p的下一个节点,p->next(p.next),在规定的时间范围内(比如1s,不能太短)如果节点的next节点始终存在,则可认为该链表存在环。
(此方法并不推荐)
2、集合查询法
创建一个集合(字典)存储已访问的每个节点的地址,并查询当前节点的地址是否已经在集合中存在;如果存在说明有环。
python代码:
class Solution(object):
def findCircle01(self, head):
if not head or not head.next:
return False
st = {}
p = head.next
while P:
if p in st:
return True
st[p] = p.val
p = p.next
return False
3、快慢指针法
同时在链表头指定快慢两个指针,快指针走两步,慢指针走一步;当两个指针能够相遇,说明链表有环;否则无环。
代码实现:
class Solution(object):
def findCircle02(self, head):
if not head or not head.next:
return False
fast = slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
return True
return False
1、循环比值法
循环比较两个已知有序链表的值,将值较小者的结点放入目标链表中进行参与排序。
代码实现:
class Solution(object):
def mergeList(self, l1, l2):
head = ListNode(0)
first = head # 将头结点位置记录下来
while l1 and l2:
if l1.val <= l2.val:
head.next = l1
l1 = l1.next
elif l1.val > l2.val:
head.next = l2
l2 = l2.next
head = head.next
if l1:
head.next = l1
elif l2:
head.next = l2
return first.next
2、递归法
将有序链表l1和l2找出值较小的结点参与排序,然后用递归方式查找两链表剩余结点中的值较小者。
1、暴力求解法
通过两次遍历链表方式实现,第一遍遍历链表查询出链表的总长度n,计算出倒数第k个位置(n-k+1);第二遍遍历链表到第(n-k+1)个位置,执行删除结点操作。此方式时间复杂度为O(n^2)
2、快慢指针法
采用快慢两个指针,让快指针比慢指针提前(k)步,当快指针离开终点时,慢指针到达倒数第k个结点。这样一个循环即可实现删除结点的目的。
代码实现:
class Solution(object):
def deleteknode(self, head, k):
prev = ListNode(0)
prev.next = head
fast = slow = prev
for _ in range(k):
fast = fast.next
while fast.next:
fast, slow = fast.next, slow.next
slow.next = slow.next.next
return prev.next
1、暴力求解法
通过两次遍历链表方式实现,第一遍遍历链表查询出链表的总长度n,计算出中间位置(n//2);第二遍遍历链表到第(n//2)个位置。此方式时间复杂度为O(n^2)
2、快慢指针法
采用快慢两个指针,快指针走两步,慢指针走一步。快指针到终点时,慢指针位置即为中点位置(如果中点存在两个值,取后者。)时间复杂度为O(n).
代码实现:
class Solution(object):
def midNode(self, head):
if not head or not head.next:
return head
prev = ListNode(0)
prev.next = head
fast = slow = prev
while fast and fast.next:
fast, slow = fast.next.next, slow.next
if fast:
slow = slow.next
return slow
参考文献:《极客时间——算法面试通关40讲》