You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

链表中dummy与tail同步更新的底层内存原理问询

问题:合并有序链表中dummy与tail节点同步更新的底层原理

近期开始学习链表,采用LeetCode链表题目中的ListNode实现方案,参考现有解法完成了「合并两个有序链表」的代码实现。为便于理解,额外定义了t2变量,调试时发现dummy节点与tail指针会同步更新——修改tail的next属性时,dummy的后续节点也随之变化。对此存在疑惑:dummy与tail不是不同的变量吗?为何会出现同步更新的情况?希望了解该操作在底层内存中的具体运行机制。

ListNode定义

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

合并链表代码

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        # 创建dummy节点存储结果链表
        dummy = ListNode()
        tail = dummy
        t2 = tail
        print('PREV')
        print(dummy, tail, t2)
        print('------------------')
        while list1 and list2: # 循环直到其中一个链表遍历完毕
            # 如果list1当前节点值更小
            if list1.val<list2.val:
                # 将list1节点接在tail后面,并移动list1指针
                tail.next = list1
                list1 = list1.next
                print('if', tail, '||' ,dummy)
            else:
                # 否则接list2节点,并移动list2指针
                tail.next = list2
                list2 = list2.next
                print('else', tail, '||', dummy)
            # 移动tail指针到新的尾部
            tail = tail.next
            print("UPDATED TAIL:", tail)
            print(dummy)
            print('###########')

        # 处理剩余未遍历的节点
        if list1:
            tail.next = list1
        elif list2:
            tail.next = list2
        print('------------------------')
        print(list1, list2)
        print(tail)
        print(dummy)
        print(t2)
        # 返回合并后的链表(跳过dummy头节点)
        return dummy.next

调试日志

PREV
ListNode{val: 0, next: None} ListNode{val: 0, next: None} ListNode{val: 0, next: None}
------------------
else ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}} || ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}}
UPDATED TAIL: ListNode{val: 1, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}}
###########
if ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}}} || ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}}}}
UPDATED TAIL: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}}}}
###########
if ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}} || ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}}}}
UPDATED TAIL: ListNode{val: 2, next: ListNode{val: 4, next: None}}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 4, next: None}}}}}
###########
else ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}} || ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}}}}
UPDATED TAIL: ListNode{val: 3, next: ListNode{val: 4, next: None}}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}}}
###########
else ListNode{val: 3, next: ListNode{val: 4, next: None}} || ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}}}
UPDATED TAIL: ListNode{val: 4, next: None}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: None}}}}}
###########
------------------------
After loop
ListNode{val: 4, next: None} None
ListNode{val: 4, next: ListNode{val: 4, next: None}}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: ListNode{val: 4, next: None}}}}}}
ListNode{val: 0, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: ListNode{val: 4, next: None}}}}}
解答

核心原因是Python中对象和变量的关系是「引用」而非「拷贝」,具体拆解如下:

  1. 初始阶段的引用指向
    当执行 dummy = ListNode() 时,内存中会创建一个ListNode对象(假设内存地址为0x123),dummy这个变量其实是存储了该对象的内存地址,相当于一个指针指向0x123处的对象。
    紧接着 tail = dummy 和 t2 = tail,这两个操作只是把dummy里存储的内存地址(0x123)拷贝给了tail和t2,此时三个变量都指向同一个内存地址的ListNode对象。

  2. 修改对象属性的影响
    当执行 tail.next = list1 或 tail.next = list2 时,并不是修改tail变量本身,而是通过tail的引用,找到它指向的内存地址(0x123)处的ListNode对象,修改该对象的next属性。
    因为dummy同样指向这个0x123的对象,所以当你查看dummy的next时,自然会看到和tail修改后的结果一致——本质是同一个对象的属性被修改,所有指向它的变量都会感知到这个变化。

  3. tail = tail.next的本质
    这个操作是改变tail变量的引用指向:从原来的0x123对象,改为指向0x123对象的next属性对应的那个ListNode对象(比如0x456)。此时dummy仍然指向0x123的头节点,而tail已经指向链表的下一个节点,但0x123对象的next链已经被之前的操作修改,所以dummy的后续节点会跟着整个链表的构建过程更新。

简单来说,dummy、tail、t2一开始是「共享同一个对象的不同指针」,修改对象的属性会影响所有指针;而tail = tail.next是让tail这个指针换一个对象指向,但原来的对象已经被修改过,dummy作为头指针自然能看到完整的链表结构。

内容的提问来源于stack exchange,提问作者Aman Savaria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:31:02