链表概念的理解

链表(一) 涉及对链表概念的理解,以及python是如何引用的

21. 合并两个有序链表

# 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 使得 pnode1 指向同一个 ListNode 对象。

引用的操作:通过 p 可以访问和修改 node1 的属性和方法,因为它们引用的是同一个对象。

引用的灵活性:这种引用机制使得在Python中操作链表等数据结构变得非常直观和灵活。

引用赋值:在Python中,赋值操作将对象的引用(内存地址)赋给变量,而不是对象本身。


node1 = ListNode(-1)

在内存中执行的操作如下:

  1. 创建对象
    • 内存中分配空间来存储一个 ListNode 对象。
    • 初始化对象的 val 属性为 -1next 属性为 None
  2. 存储引用
    • 变量 node1 存储这个 ListNode 对象的内存地址(引用)。

此时,node1 并不是直接存储对象的数据,而是存储对象的内存地址,通过这个地址可以访问和操作对象。python的性质,就是可以通过地址直接访问和操作对象。

总结下:当需要创造一条新链表的时候,可以使用虚拟头结点简化边界情况的处理

86. 分隔链表

# 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 

23. 合并 K 个升序链表

导入了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` 是这些链表的节点总数**

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值