请实现函数clone_complex_list()复制一个复杂链表。在复杂链表中,每个节点除了有一个m_pNext指针指向下一个节点,还有一个m_sibling指针指向链表中的任意节点或者nullptr。节点的c++定义如下:
struct ComplexListNode
{
int m_nValue;
ComplexListNode* m_pNext;
ComplexListNode* m_pSibling;
}
python 版
class ComplexListNode(object):
def __init__(self, value, next_node=None, sibling_node=None):
self.value = value
self.next = next_node
self.sibling = sibling_node
如何复制一个复杂链表,首先我们得知道复制一个简单链表的步骤是什么呢?
- 复制节点
- 通过指针将这些节点连接起来
那么复制复杂链表呢?
- 复制节点
- 通过指针将这些节点连接起来
- 复制sibling指针
- 遍历链表,复制链表的所有节点,时间复杂度O(n)
- 通过next指针连接这些节点,时间复杂度O(n)
- 遍历原始链表找到每个节点的sibling指针指向的节点,遍历新链表找到新链表中对应节点的sibling指针指向,时间复杂度O(n^2)
但是思路1的时间复杂度太高了,那么有没有什么方法能够降低时间复杂度呢?方法当然是有的,思路1的时间复杂度高就高在第3步,所以我们只要能够降低第3步的时间复杂度就可以了。思路1的第3步是两层循环,因为要先从原始链表中找到sibling的节点,然后再在复制链表中找到对应的sibling节点,这两步都是通过遍历实现。
考虑使用额外的空间来换取更低的时间复杂度
也就是説,只要我们找到了原始链表中sibling节点,也就能直接找到复制链表的sibling节点,而无需再遍历链表。那么就需要将原始链表中的节点和复制链表中的节点映射到一个字典中。字典根据key取值的时间复杂度是O(1),这样我们就能在O(n)时间复杂度内实现第3步了。
那么问题又来了,能不能不使用额外的字典呢?当然可以了,把复制的节点依次放在原始节点之后,构成一个新的长链表,奇数都是原始链表,偶数都是复制链表。
那具体应该怎么做呢?
其实这个跟思路2是有些类似的。也是将原始节点和复制节点关联了起来,只不过这次不是通过映射来实现的,而是原始节点的next指针指向的下一个节点就是其复制节点。既然有这样的规则,那么同样的也就能在O(1)找到对应的复制节点的sibling节点了。bingo
def clone_node(head):
"""
第1步,复制链表中的节点和next指针指向,时间复杂度为O(n)
:param head: 复杂链表的头节点
:return c_head: 复制链表的头节点
"""
if not head:
return
# 头节点存在
cur = head # 初始化当前节点
c_head = ComplexListNode(head.value)
c_cur = c_head
# 当前节点存在的话
while cur:
# 复制节点
if cur.next:
c_next = ComplexListNode(cur.next.value)
c_cur.next = c_next
c_cur = c_next
cur = cur.next
else:
break
return c_head
def connected_sibling_nodes(head, c_head):
"""
第2步,复制链表中的sibling指针指向,时间复杂度为O(n^2)
:param head: 复杂链表的头节点
:param c_head: 复制链表的头节点
"""
if not head or not c_head:
return
cur = head
c_cur = c_head
# print("what's up")
# print('原始链表',head.value)
# print('复制链表',c_head.value)
while cur:
# print('此时,原始节点是{},复制节点是{}'.format(cur.value, c_cur.value))
# 注意:每次查找sibling时,c_temp必须从链表头开始,因为当前节点的sibling不一定在此节点的后面哦!!
c_temp = c_head
# print('此时c_temp是{}'.format(c_temp.value))
sibling_node = cur.sibling
# 这个指针不指向None
if sibling_node:
while c_temp:
if c_temp.value == sibling_node.value:
c_cur.sibling = c_temp
break
c_temp = c_temp.next
c_cur = c_cur.next
cur = cur.next
return c_head
def clone(head):
"""
主函数
"""
c_head = clone_node(head)
return connected_sibling_nodes(head, c_head)
if __name__ == '__main__':
node1 = ComplexListNode(1)
node2 = ComplexListNode(2)
node3 = ComplexListNode(3)
node4 = ComplexListNode(4)
node5 = ComplexListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node1.sibling = node3
node3.sibling = node2
node4.sibling = node1
#res = clone_node(node1)
res = clone(node1)
node = res
while node:
if node.sibling:
print("节点的值{}, s节点为{}".format(node.value, node.sibling.value))
else:
print("节点的值{}, s节点为None".format(node.value))
node = node.next
class ComplexListNode(object):
def __init__(self, value, next_node=None, sibling_node=None):
self.value = value
self.next = next_node
self.sibling = sibling_node
def clone_node(head):
"""
第1步,复制链表中的节点和next指针指向,时间复杂度为O(n)
:param head: 复杂链表的头节点
:return c_head: 复制链表的头节点
"""
if not head:
return
mapping = {} # 映射原始节点和复制节点
# 头节点存在
cur = head # 初始化当前节点
c_head = ComplexListNode(head.value)
c_cur = c_head
mapping[head] = c_head
# 当前节点存在的话
while cur:
# 复制节点
if cur.next:
mapping[cur] = c_cur
c_next = ComplexListNode(cur.next.value)
c_cur.next = c_next
c_cur = c_next
cur = cur.next
else:
break
return c_head, mapping
def connected_sibling_nodes(head, c_head, mapping):
"""
第2步,复制链表中的sibling指针指向,使用一个字典,映射原始节点和复制节点,时间复杂度为O(n)
:param head: 复杂链表的头节点
:param c_head: 复制链表的头节点
"""
if not head or not c_head:
return
#mapping = {}
cur = head
c_cur = c_head
while cur:
#mapping['cur'] = c_cur
if cur.sibling:
# 从字典中直接找到,原始链表中节点的sibling节点对应的那个sibling节点
c_cur.sibling = mapping.get(cur.sibling)
cur = cur.next
c_cur = c_cur.next
return c_head
def clone(head):
"""
主函数
"""
c_head, mapping = clone_node(head)
return connected_sibling_nodes(head, c_head, mapping)
if __name__ == '__main__':
node1 = ComplexListNode(1)
node2 = ComplexListNode(2)
node3 = ComplexListNode(3)
node4 = ComplexListNode(4)
node5 = ComplexListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node1.sibling = node3
node3.sibling = node2
node4.sibling = node1
#res = clone_node(node1)
res = clone(node1)
node = res
while node:
if node.sibling:
print("节点的值{}, s节点为{}".format(node.value, node.sibling.value))
else:
print("节点的值{}, s节点为None".format(node.value))
node = node.next
class ComplexListNode(object):
def __init__(self, value, next_node=None, sibling_node=None):
self.value = value
self.next = next_node
self.sibling = sibling_node
def clone_node(head):
"""
第1步,将复制的节点放到其对应的复杂链表中的节点的后面,构成一个新的长的链表,
时间复杂度O(n)
:param head: 复杂链表的头节点
"""
# 头节点存在
cur = head # 初始化当前节点
# 当前节点存在的话
while cur:
# 1.首先复制节点
c_cur = ComplexListNode(cur.value)
# 2.调整next指针的指向
# 保存原始节点的next, 原始节点的next指向该节点的复制节点,复制节点的next指向原始节点的next
temp = cur.next
cur.next = c_cur
c_cur.next = temp
# cur指向下一个原始节点
cur = c_cur.next
return
def connected_sibling_nodes(head):
"""
第2步,复制链表中的sibling指针指向,时间复杂度为O(n)
:param head: 复杂链表的头节点
"""
cur = head
while cur:
# 获得每一个原始节点的复制节点
c_cur = cur.next
# 若原始节点有sibling那么,原始节点的sibling节点的next节点就是复制节点的sibling
if cur.sibling:
c_cur.sibling = cur.sibling.next
# cur指向下一个原始节点
cur = c_cur.next
return
def reconnect_nodes(head):
"""
将这个长的链表按奇偶分割成两个链表
:param head: 链表的头节点
"""
cur = head # 原始链表中的当前节点
c_head = None # 复制链表的头节点
c_cur = None # 复制链表中的当前节点
# 初始化,获得复制链表的头节点,c_cur指向第一个复制节点,cur指向
if cur:
c_head = c_cur = cur.next
cur.next = c_head.next
cur = cur.next
while cur:
c_cur.next = cur.next
c_cur = c_cur.next
cur.next = c_cur.next
cur = cur.next
return c_head
def clone(head):
"""
主函数
"""
clone_node(head)
connected_sibling_nodes(head)
return reconnect_nodes(head)
if __name__ == '__main__':
node1 = ComplexListNode(1)
node2 = ComplexListNode(2)
node3 = ComplexListNode(3)
node4 = ComplexListNode(4)
node5 = ComplexListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node1.sibling = node3
node3.sibling = node2
node4.sibling = node1
#res = clone_node(node1)
res = clone(node1)
node = res
while node:
if node.sibling:
print("节点的值{}, s节点为{}".format(node.value, node.sibling.value))
else:
print("节点的值{}, s节点为None".format(node.value))
node = node.next
找出限制代码效率的部分,优化这部分即可提升代码的效率。映射和指针,只要能在O(1)时间找到对应的节点,就降低了这一步的时间复杂度。
[1] 剑指offer丛书