Variável local que referencia para o mesmo objeto de uma variável global é alterada, mas a global não

Tenho o seguinte problema: Estou fazendo uma Árvore Rubro-Negra em Python, e quando vou excluir uma raíz que tenha um só filho, acontece o problema, abaixo segue meu código
`

BLACK = 0
RED = 1

class No():

    def __init__(self, valor, pai=None, esquerda=None, direita=None, cor=RED):
        self.valor = valor
        self.esquerda = esquerda
        self.direita = direita
        self.pai = pai
        if pai == None:
            self.cor = BLACK
        else:
            self.cor = cor

    def filho_nao_nulo(self):
        return (self.direita if self.direita != None else self.esquerda)

    def remover(self, valor):
        #global raiz
        no = self
        if no == None:
            return None
        if valor < no.valor:
            no.esquerda.remover(valor)
        elif valor > no.valor:
            no.direita.remover(valor)
        else:
            #o nó encontrado só tem um filho
            if no.direita != None or no.esquerda != None:
                if no.cor == BLACK:
                        #se o nó é a raiz
                        if no.pai == None:
                            if no.esquerda == no.filho_nao_nulo():
                                no = no.esquerda
                                no.cor = BLACK
                            else:
                                no = no.direita
                                no.cor = BLACK                          

raiz = No(12)
raiz.direita = No(14, raiz)
raiz.remover(12)

`
Ao chegar na linha

no = no.direita
o debug está dessa maneira:

1

Ao executar essa linha, apenas a variável local “no” é alterada, mas a global “raiz” não, sendo que ambas se referenciam ao mesmo endereço de memória

2

o nó passa a ter o endereço de memória da direita da raiz, como deveria ser feito com a raiz, mas a raiz não é alterada.

O mais estranho é que isso só acontece com a raiz, se o nó, por exemplo fosse igual a raiz.direita.esquerda, não haveria problema nenhum com qualquer operação que eu quisesse fazer sobre esse nó, ele alteraria tanto a variável local, como a global.

Tive um problema semelhante na árvore AVL.

A solução temporária que tive foi declarar o código

global raiz

no início do método, e mudar a raiz diretamente. Mas li sobre, e vi que isso não é uma boa prática. Alguém poderia me ajudar ? Obrigado.

É meio chato de explicar isso por escrito, mas olha esse exemplo e tenta entender o que tá acontecendo:

    primeiro = No(1)
    segundo = primeiro
    print(f'Valor primeiro {primeiro.valor}')
    print(f'Valor segundo {segundo.valor}')
    
    # ao colocar um diferente valor para primeiro, segundo continua apontando para No(1)
    primeiro = No(2)
    print(f'Valor primeiro {primeiro.valor}')
    print(f'Valor segundo {segundo.valor}')

    primeiro.direita = No(3)
    terceiro = primeiro
    print(f'Valor direita primeiro {primeiro.direita.valor}')
    print(f'Valor direita terceiro {terceiro.direita.valor}')

    # como terceiro aponta pra primeiro, ao mudar o valor de uma propriedade de primeiro, terceiro ainda aponta pro mesmo valor
    primeiro.direita = No(4)
    print(f'Valor direita primeiro {primeiro.direita.valor}')
    print(f'Valor direita terceiro {terceiro.direita.valor}')

Para resolver seu problema, você precisa abordar a mesma estratégia entre primeiro e terceiro.
O que seu código está fazendo hoje, é a mesma coisa entre primeiro e segundo (primeiro = raiz, segundo = no)

Repare que usando a variável global raiz não soluciona seu problema, se você começar a criar mais nós dentro de nós na árvore.

1 curtida

Entendi, então a solução para meu problema seria fazer em vez de: no = no.direita, eu devia fazer uma troca de propriedades, ou seja, copiar todos os atributos de no.direita para no, e em seguida remover o no.direita ?

E só para resumir para ver se eu entendi tudo.
Quando eu tenho duas variáveis A e B apontando para o mesmo objeto, E eu altero a variável A para apontar para outro objeto isso não se reflete em B, certo ? Apenas reflete se eu alterar os atributos do objeto para qual as variáveis apontam ?
Então no meu caso eu tenho:
raiz
no = raiz
no = no.direita
o no (A) agora aponta para o no.direita, ou seja mudei o objeto que a variável se referencia, e isso não se refletiu na variável raiz (B), certo ?
Obrigado desde já!

Exatamente.

Uma estratégia é você passar não apenas o nó, mas o pai do nó… daí você sempre altera uma propriedade do pai, ao invés de mudar a referência do nó em si.

1 curtida

Muito obrigado pelo seu tempo em me responder.
Infelizmente essa estratégia não é possível para a raiz, visto que a característica dela é que o seu pai é None, mas essa estratégia eu já utilizo no restante do código para os nós que não são raíz. De qualquer forma, muito obrigado!