Exchange pointers list chained - python

0

I created a function to change the pointers of a linked list, the idea is very simple, I change them and update the next element so it does not lose the reference, but the function is losing the reference, eg, I tried a list l = 1, 2, 3, 4, 5, 6, and I want to change position 2 and 4, it returns l = 1, 2, 5, 6, when it should be l = 1, 4, 3 , 2, 5, 6. I do not know what to do, I tried several alternatives and nothing, it follows the code:

def troca_teste(L): 
        p = L.head
        aux = p
        while p:
            if p.element == 2:
                q = p.next
                while q:                  
                    if q.element == 4:          
                        #swap dos ponteiros          
                        aux = q
                        q = p
                        q.next = aux.next                        
                        aux.next = p.next
                        p = aux                        
                        p.next = aux.next                                     
                        break  
                    q = q.next                   
            p = p.next

Class definition:

class LinkList:

    class Node:

        def __init__ (self, element = None, next = None):
            self.element = element
            self.next = next

    def __init__ (self):
        self.head = None
        self.size = 0
    
asked by anonymous 15.10.2017 / 18:46

1 answer

1

You want to do a swap operation. The swap is an operation that, given an indexable set and two indexes, exchanges the elements delayed by those indexes.

Preliminaries

Let's call the indexable list of l . For your case, l is an instance of LinkList .

Due to the definition of LinkList , l is not indexed. To do indexed access, the idea is more or less the following:

def acesso_nodo(l, idx):
    # se o índice for além do que está armazenado, retorna nenhum nó
    if l.size <= idx < 0:
      return None
    atual = l.head
    while idx > 0:
        atual, idx = atua.next, idx - 1
    return atual

Note that this function will return the node (instance of Node , not the element inside the node, and this is purposeful). Thus, we can say that, through an index, we get the associated node. Now we can treat l as an indexed set, since we transform its ability to be indexed in fact (before acesso_nodo , instances of LinkList were indexable, therefore indexable; reality, we can now call LinkList indexed).

Making the exchange, maintaining structure

As we are doing an exchange operation, the topological structure of the list. So, when we get the nodes that point to the index elements k and j (hereinafter referred to as nodo_k and nodo_j ), we need to do only:

nodo_k.element, nodo_j.element = nodo_j.element, nodo_k.element

Then, it is only open to identify nodo_j and nodo_k . On the function described in the previous section, we can do the following (relatively inefficient) function:

def swap_toplologico_1(l, j, k):
    nodo_j = acesso_nodo(l, j)
    nodo_k = acesso_nodo(l,k)
    if nodo_j != None and nodo_k != None:
        nodo_k.element, nodo_j.element = nodo_j.element, nodo_k.element
    return l

Note that to navigate to the highest index, you have to scroll through the lowest index. On top of that, we can do an optimization and decrease access to the reference by at least o(min(j,k)) :

def swap_toplologico_2(l, j, k):
    (m, M) = (j, k) if j < k else (k, j)
    if M >= l.size or m == M or m < 0:
        return l
    atual = l.head
    while M >= 0:
        if m == 0:
            nodo_m = atual
        if M == 0:
            nodo_M = atual
        atual, m, M = atual.next, m - 1, M - 1
    nodo_m.element, nodo_M.element = nodo_M.element, nodo_m.element
    return l
Here, the idea was to do the same iteration made in acesso_nodo only turned to the largest element of the index passed, taking advantage of when you pass in the lowest index to save its value. Note that, in the loop, there are conditions to detect when the desired index is found and to store the variables nodo_m and nodo_M .

In order to have fewer conditional adjustment questions, I put m to be the smallest of the j and k indexes, and M to be the largest. So, if j is the smallest, then nodo_j is equivalent to nodo_m ; otherwise, j is the largest element, then nodo_M that is equal to nodo_j . nodo_k gets the other node in both cases.

I also took advantage of it and made a little effort not to take any action when the index passed to make the change is the same (ie, j == k ), because to trivially make this exchange has the same effect of not doing it.

Topological change

The other alternative is to change the structure itself. The nodes physically change structure. Because the list is simply chained, you can not access the previous element from the current one. So, we need to get the previous element of the indexes passed to make this exchange.

As with the topology exchange, you can write more efficient code, but here I'll focus on logic rather than performance. Feel free to make the release more efficient.

To access elements prior to j and k , we need to access elements j-1 and k-1 . It is also good to ensure that the largest element is a valid element. Another point of attention is that 0 is a special case where it is necessary to change l.head .

To deal more simply with who is the largest and who is the smallest element, I will use the same logic as m,M of the function swap_topologico_2 . So I only check if m is zero.

 def swap_trocando_estrutura(l, j, k):
     (m, M) = (j, k) if j < k else (k, j)
    if M >= l.size or m == M or m < 0:
        return l
    prev_M = acesso_nodo(l, M - 1)
    # tratar primeiro o caso de m ser 0
    if m == 0:
        next_head = l.head.next
        next_M = prev_M.next.next

        # trocando as referências dos elementos seguintes
        prev_M.next.next = next_head
        l.head.next = next_M

        # trocando as referências dos elementos desejados
        l.head, prev_M.next = prev_M.next, l.head
        return l
    else:
        prev_m = acesso_nodo(l, m - 1)

        next_m = prev_m.next.next
        next_M = prev_M.next.next

        # trocando as referências dos elementos seguintes
        prev_m.next.next = next_M
        prev_M.next.next = next_m

        # trocando as referências dos elementos desejados
        prev_m.next, prev_M.next = prev_M.next, prev_m.next
        return l

Note that it was necessary to change the elements of nodo_m.next and nodo_M.next so that it was possible to remove nodo_m and nodo_M from their current places and maintain list coherence.

Recommended reading

15.10.2017 / 21:50