链表(一) 涉及对链表概念的理解,以及python是如何引用的
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(-1) #dummy指针的意义,最开始的指针一直存放在dummy中 ListNode(-1)是真实存在的节点,可以用dummy和p指代
p = dummy #p,p1,p2始终都只是指针而已
#dummy 现在是一个指向 ListNode 对象的引用。
# dummy 存储的是对象的内存地址,通过这个地址你可以访问 ListNode 对象的属性 val 和 next。
p1 = list1
p2 = list2
while p1 is not None and p2 is not None:
if p1.val > p2.val:
p.next = p2 #设置 p 所指节点的 next 属性为 p2 所指节点。
p2 = p2.next
else:
p.next = p1
p1 = p1.next
p = p.next #使用改变过来结点的指针。
if p1 is not None:
p.next = p1
if p2 is not None:
p.next = p2
return dummy.next
解释指针和对象引用
在 Python 中,对象变量实际上是对象的引用或指针。当你创建一个对象并将其赋值给一个变量时,这个变量实际上存储的是对象的内存地址。通过这个变量,你可以访问和操作对象的属性和方法。也就是说通过指针可以直接操作对象的属性和方法。
引用和对象:在 Python 中,变量存储的是对象的引用或内存地址,通过这些引用我们可以访问和操作对象。
清晰明白python引用的机制
引用的本质:
p = node1
使得p
和node1
指向同一个ListNode
对象。引用的操作:通过
p
可以访问和修改node1
的属性和方法,因为它们引用的是同一个对象。引用的灵活性:这种引用机制使得在Python中操作链表等数据结构变得非常直观和灵活。
引用赋值:在Python中,赋值操作将对象的引用(内存地址)赋给变量,而不是对象本身。
node1 = ListNode(-1)
在内存中执行的操作如下:
- 创建对象:
- 内存中分配空间来存储一个
ListNode
对象。 - 初始化对象的
val
属性为-1
,next
属性为None
。
- 内存中分配空间来存储一个
- 存储引用:
- 变量
node1
存储这个ListNode
对象的内存地址(引用)。
- 变量
此时,node1
并不是直接存储对象的数据,而是存储对象的内存地址,通过这个地址可以访问和操作对象。python的性质,就是可以通过地址直接访问和操作对象。
总结下:当需要创造一条新链表的时候,可以使用虚拟头结点简化边界情况的处理。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
dummy1 = ListNode(-1)
dummy2 = ListNode(-1)
p = head
p1, p2 = dummy1, dummy2 #dummy1是小的,dummy2是大的
while p:
if p.val >= x:
p2.next = p
p2 = p2.next #这个步骤是更新指针,也就是把新结点的地址给p2
else:
p1.next = p
p1 = p1.next
temp = p.next
p.next = None
p = temp #虽然p要移动到下一个位置,要把原来p从原位置断开
p1.next = dummy2.next
return dummy1.next
导入了heapq模块,它是Python标准库的一部分,提供了堆队列算法的实现,也就是我们常说的优先队列。在这段代码中,heapq用于维护一个最小堆,以便我们每次都能快速地访问并取出具有最小值的节点。这里创建了一个空的列表 pq,它将被用作优先队列(最小堆)。
heap 是堆的英文, headq 意思是堆队列
在每一步插入操作后,heapq
模块会调整堆以确保堆顶始终是最小的元素。 (head.val, id(head), head)
的第一个元素是节点的值 head.val
lists
是一个包含多个链表头节点的列表。每个链表头节点表示一个单独的链表的起始节点
链表数组,每个链表链表数组中的每个链表,是按照头结点存储的,这里面是若干个头结点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if not lists:
return None
dummy = ListNode(-1)
p = dummy
pq = []
for head in lists:
if head:#因为前两个元素已经足够区分所有节点,第三个元素head(即节点本身)不会被用作比较。
heapq.heappush(pq, (head.val, id(head), head)) #推入堆的是这个三元组,并且是按照先比较值再比较id,的顺序推进去的
while pq:
node = heapq.heappop(pq)[2] #出来的是三元组的位置为2的这个,也就是出来的是结点
p.next = node
if node.next:
heapq.heappush(pq, (node.next.val, id(node.next), node.next)),
p = p.next
return dummy.next
时间复杂度
优先队列 pq
中的元素个数最多是 k
,所以一次 pull
或者 push 方法的时间复杂度是
O(logk);所有的链表节点都会被加入和弹出
pq,**所以算法整体的时间复杂度是
O(Nlogk),其中
k 是链表的条数,
N` 是这些链表的节点总数**