更改链表中节点的数据不会更改引用同一内存地址的另一个变量的数据

问题描述

我不知道如何为这篇文章命名。

最初,当我尝试反转链接列表的后半部分时,我在 main 函数中更改原始链接列表时遇到了麻烦,我相信我现在明白了。但是,我现在无法理解在操作链接列表中的数据时发生了什么。我写下了我的代码,但在接近尾声时,我编写了一个单独的函数来解释我对此的思考过程并解释为什么我无法理解。

class Node:
    def __init__(self,value,next=None):
        self.value = value
        self.next = next
    
def find_middle(head):
    slow,fast = head,head

    while fast is not None and fast.next is not None:
        slow = slow.next
        fast = fast.next.next

    return slow

def reverse_second_half(head):
    middle = find_middle(head)
    second_half_reversed = reverse(middle,head)
    

    return '\nfinished'

def reverse(head,original_head):
    prev = None
    iteration = 1
    while head is not None:
        pointer = original_head

        print('\noriginal list iteration = ' + str(iteration) + '\n')
        while pointer is not None:
            print('current pointer value = ' + str(pointer.value))
            pointer = pointer.next

        iteration += 1

        next = head.next
        head.next = prev
        prev = head
        head = next
    
    return prev
    

def main():
    head = Node(1)
    head.next = Node(2)
    head.next.next = Node(3)
    head.next.next.next = Node(4)
    head.next.next.next.next = Node(5)

    print(reverse_second_half(head))

main()

main 中,我调用 reverse_second_half,后者调用 find_middlereverse 函数。在找到链接列表的中间之后,我从在 find_middle 中找到的中间节点开始反转链接列表的后半部分。

我将其传递给反向,但我也将“原始列表”作为第二个参数传递。我这样做是因为我想看看一旦反转开始执行,原始列表是如何改变的。

我看到,对于第一次迭代,但在发生任何逆转之前,原始链表及其所有值都会被打印出来,从而在控制台中打印出以下内容

original list iteration = 1

current pointer value = 1
current pointer value = 2
current pointer value = 3
current pointer value = 4
current pointer value = 5

对于第二次迭代...

original list iteration = 2

current pointer value = 1
current pointer value = 2
current pointer value = 3

依此类推......(每隔一次迭代都会打印出第二次迭代给出的结果作为输出)。

所以我知道我操作的不是列表的副本,而是同一个列表本身。

根据我的理解我们可以看到,在任何逆转发生之前的第一次迭代中的原始链表是

原头 head
值:1,下一个:-> 值:2,下一个:-> 值:3,下一个:-> 值:4,下一个:-> 值:5,下一个:->

然后一步一步地进行第一次迭代

原头 head next
值:1,下一个:-> 值:2,下一个:-> 值:3,下一个:-> 值:4,下一个:-> 值:5,下一个:->
原头 head
值:1,下一个:-> 值:2,下一个:-> 值:3,下一个:->

在这里我看到原始列表已更改,不再包含后半部分。我可以在这里看到我没有某个列表的单独副本,但是我们正在使用相同的列表本身,因为我们只是将变量指向内存地址。这对我来说似乎很奇怪,因为当我这样做时

next = head.next

在第一次迭代期间,从上表中我们看到 next 指向值为 4 的节点,但是在下一行

head.next = prev

其中 prev == None 应该将列表更改为

1 -> 2 -> 3 -> None

但是如果 next 指向 head.next 并且 head.next 被更改为 None 怎么办

head = next

获取列表4 -> 5

这对我来说似乎不对,我创建了一个函数显示我的想法

def change_value_in_linked_list(head):
    middle = find_middle(head)

    middle.value = 'oops'

    while head is not None and head.next is not middle:
        head = head.next

    print(head.next.value)
    print(middle.value)

    return '\nfinished'

如果我们使用其余代码运行上述代码并在 main 中调用函数而不调用 reverse_second_half 仅用于测试目的,我会看到 head.next.valuemiddle.value 包含相同的数据将 middle.value 更改为字符串 oops 后。我的理解是中间点指向一个内存位置,当我们改变它的数据并试图通过遍历链表来找到相同的内存位置时,我们看到现在在原始链表中我们找到了改变的数据

1 -> 2 -> 'oops' -> 4 -> 5

但是,如果我将函数改为这样

def change_value_in_linked_list(head):
    middle = find_middle(head)

    middle.value = 'oops'

    while head is not None and head.next is not middle:
        head = head.next

    head.next = None
    print(head.next)
    print(middle.value)

    return '\nfinished'

更改 middle.value = 'oops' 后,我假设 head.next.valuemiddle.value 是“哎呀”,根据我们之前看到的内容,这是正确的。但是,我现在添加

head.next = None

我再次假设 head.next 指向一个内存位置,我们正在将该数据更改为 Nonemiddle 指向同一个内存位置,但是当打印出包含在 head.nextmiddle 中的数据时,中间仍然包含一个值为 oopshead.next 的有效节点现在是None

也许我遗漏了一些明显的东西,但这让我很失望。就好像参数正在声明更改由不同指针引用的内存位置中的数据将数据更改为每个指针的更改数据,因为这些指针没有被分配该数据,而是仅指向内存位置,但是通过执行 head.next = None 并看到 head.nextmiddle(指向同一内存位置的两个指针)包含不同的数据,与此参数相矛盾。

解决方法

如果我正确理解了您的问题,我认为您的困惑在于如何使用 = 将变量/引用绑定到对象。也许这些图形会有所帮助:

初始状态:

next = head.next

head.next = prev

prev = head

head = next

您的链表已被切断。