Lista dwukierunkowa dwustronna, odwracanie w czasie O(1)

0

Próbuję zaimplementować listę dwukierunkową dwustronną i kilka operacji na niej, m.in. odwracanie listy oraz łączenie dwóch, ale natknąłem na problem, którego rozwiązanie zajęło mi już trochę czasu a efektów nie widać. Powyższe operacje (odwracanie, łącznie) wykonuję w czasie stałym. Odwracanie polega na zmianie flagi REVERSE_FLAG oraz przepięciu pierwszego elementu z ostatnim i ostatniego z pierwszym. Wszystko działa do czasu, aż nie stworzę dwóch list, nie odwrócę jednej a następnie nie spróbuję ich połączyć, co przedstawia poniższy przykład:

lista pierwsze: L1 L2 L3 L4 L5
lista druga: X1 X2 X3 X4 X5

  1. wykonuję reverse na liście drugiej i otrzymuję: X5 X4 X3 X2 X1,
  2. następnie union listy pierwszej oraz listy drugiej, co powinno dać L1 L2 L3 L4 L5 X5 X4 X3 X2 X1. Niestety otrzymuję gdzieś nieskończoną pętlę, gdyż program się zawiesza.

Mój typ, to problem z łączeniem w metodzie union. Obie listy to dwa obiekty. Samo przepięcie wskaźników nie rozwiązuje problemu REVERSE_FLAG, które dla każdego obiektu jest inne, co przy trawersowaniu metodą display z listy pierwszej, powoduje błędną interpretację drugiej listy, uprzednio obróconej, czyli ze zmienionym REVERSE_FLAG. To mój typ, ale nie jestem pewien oraz nie wiem w jaki sposób to rozwiązać. Dodam tylko, że przy odwróceniu obu list jednocześnie i późniejszym połączeniu, wyświetlają się poprawnie.

Funkcja łącząca:

    public void union(String t1, String t2) {
        DoublyLinked temp1 = locate(t1); // znajduje listę w zbiorze list
        DoublyLinked temp2 = locate(t2); // znajduje listę w zbiorze list

        temp1 = temp1.next;
        temp2 = temp2.next;


        temp1.setNext(temp1.last, temp2.first);
        temp2.setPrev(temp2.first, temp1.last);
        temp1.last = temp2.last;
        train2.first = temp1.first;
        temp2.last = temp1.last;
        temp2.REVERSE_FLAG = temp1.REVERSE_FLAG;
    }
class Link {

    public Link next;
    public Link prev;
    public String value;

    public Link (String value) {
        this.value = value;
        this.next = null;
        this.prev = null;
    }
}

Lista dwukierunkowa dwustronna:

class DoublyLinked {

    public boolean REVERSE_FLAG;
    public String name;
    public Link first;
    public Link last;
    public DoublyLinked next;

    public DoublyLinked (String name, String w) {
        New(name, w);
    }

    public void New(String name, String w) {
        Link c = new Link(w);
        this.REVERSE_FLAG = false;
        this.name = name;
        this.last = this.first = c;
    }

    public void insertFirst(String w) {
        Link temp = new Link(w);
        setNext(temp, this.first);
        setPrev(this.first, temp);
        this.first = temp;
    }

    public void insertLast(String w) {
        Link temp = new Link(w);
        setPrev(temp, this.last);
        setNext(this.last, temp);
        this.last = temp;
    }

    public String delFirst() {
        if (getNext(this.first) != null) {
            setPrev(getNext(this.first), null);
            String temp = this.first.value;
            this.first = getNext(this.first);
            return temp;
        } else {
            this.first = this.last = null;
            return this.first.value;
        }
    }

    public String delLast() {
        if (this.last != this.first) {
            setNext(getPrev(this.last), null);
            String temp = this.last.value;
            this.last = getPrev(this.last);
            return temp;
        } else {
            this.first = this.last = null;
            return this.last.value;
        }
    }

    public void reverse() {
        REVERSE_FLAG = !REVERSE_FLAG;
        Link swap;
        swap = first;
        first = last;
        last = swap;
    }

    public String display() {

        String output = new String();
        Link curr = first;
        while (curr != null) {
            output += curr.value;
            System.out.print(curr.value);
            if (getNext(curr) != null) {
                output += " ";
                System.out.print(" ");
            }
            curr = getNext(curr);
        }
        System.out.println();
        output += "\n";
        return output;
    }

    public Link getNext(Link c) {
        return (REVERSE_FLAG) ? c.prev : c.next;
    }

    public Link getPrev(Link c) {
        return (REVERSE_FLAG) ? c.next : c.prev;
    }

    public void setNext(Link c, Link next) {
        if (REVERSE_FLAG) {
            c.prev = next;
        } else {
            c.next = next;
        }
    }

    public void setPrev(Link c, Link prev) {
        if (REVERSE_FLAG) {
            c.next = prev;
        } else {
            c.prev = prev;
        }
    }

    public boolean isEmpty() {
        return (first == null && last == null) ? true : false;
    }

    public Link locate(String w) {
        Link curr = first;
        while (curr != null && !curr.value.equals(w)) {
            curr = getNext(curr);
        }
        return curr;
    }
}
0

temp1 = temp1.next; // nie powinno byc temp1.getNext()?
temp2 = temp2.next; // j.w.

0

Zapomniałem dodać, że zbiór list przechowuję w liście jednokierunkowej z głową i metoda union pochodzi z tej właśnie klasy. Obiekty temp1 oraz temp2 po wykonaniu metody locate(String x) zawierają element poprzedzający wyszukiwany w zbiorze list, stąd przypisanie temp1 = temp1.next;

0

Jak ty chcesz łączyć dwie listy w czasie stałym, z której jedna jest "wirtualnie" odwrócona?
Nie możesz mieć pół listy odwróconej a drugie pół niewrócone, nie ma siły musisz dokonać faktycznego odwrócenia (o czasie liniowym) jednej z list.

0

Liczyłem, że jest na to jakiś sposób, ale zwyczajnie nie przyszedł mi do głowy. Mam rozumieć, że metoda poniższa załatwi sprawę:

    public void reverse() {
        Link curr = this.first;
        while(curr!=null) {
            Link temp = curr;
            curr = curr.next;
            Link prev = temp.prev;
            temp.next = temp.prev;
            temp.prev = prev;
        }
        Link last = this.first;
        this.first = this.last;
        this.last = last;
    }
0

W sumie da się to zrobić ale potrzebujesz dodatkowe pole (a nawet dwa) dla Link, które będzie informować o zmianie sposobu traktowania pól next i prev.
Tak samo w DoublyLinked będziesz potrzebował dwóch flag określających jak te pola mają być traktowane na obu końcach listy.

Poza tym poczytaj o czymś takim jak iterator (brakuje ci takiej klasy).

0

Chodzi o to, żeby przenieść flagi do każdego elementu listy podwójnej? W takim przypadku będę miał czas liniowy przy odwracaniu. Nie bardzo rozumiem sposób rozwiązania o jakim piszesz.

Koncepcję i korzyści iteratorów znam, ale w tej konkretnej implementacji pomijam to zagadnienie.

0

wydaje mi się że jest to do zrobienia w jednakowym czasie jeśli każdy element z osobna by przetrzymywał WSKAŹNIK do reverse_flag
przy łączeniu musiałbyś pozbierać wszystkie reverse_flagi, a przy odwracaniu takiej połączonej listy musiałbyś je wszystkie obrócić - naaah za dużo zabawy imho; nie warto

0

Możesz zrobić coś a'la:

class Link<T> {
 
    public Link siblingA;
    public Link siblingB;
    public T value;
 
    public Link (T value) {
        this.value = value;
        this.siblingA = null;
        this.siblingB = null;
    }
}

Następnie nie zapamiętywać nigdzie porządku. Wyciąganie nexta wymagałoby wtedy podania preva, tzn:

Link<T> getOtherSibling(Link<T> knownSibling) {
  if (siblingA == knownSibling) {
    return siblingB;
  } else if (siblingB == knownSibling) {
    return siblingA;
  } else {
    throw new IllegalArgumentException();
  }
}

Żeby to miało sens musiałbyś zrobić iteratory. Straciłbyś funkcjonalność next/ previous dla dowolnego węzła (tzn nie wiedząc który sibling jest prev, a który next), ale w interfejsie List nie ma metody prev, ani next, więc dalej mógłbyś zaimplementować standardowy interfejs List.

0

Zastosowałem się do rad a właściwie wpadłem na podobny pomysł jak poprzednik, choć z realizacją nieco gorzej. Implementuje operacje na liście dwukierunkowej dwustronnej, gdzie wszystkie operacje za wyjątkiem wyświetlania mają być w czasie liniowym. Operacje te, to:

  • Tworzenie nowej listy,
  • Wstawienie elementu na pierwszej pozycji
  • Wstawienie elementu na ostatnią pozycję
  • Usuwanie elementu z pierwszej pozycji i tworzenie nowej listy z usuniętego elementu
  • Usuwanie elementu z ostatniej pozycji i tworzenie nowej listy z usuniętego elementu
  • Odwracanie listy
  • Łączenie list
  • Wyświetlanie listy wg. formatowania (gdzie x to elementy listy): NazwaListy: x x x x

Zbiór list dwukierunkowych przechowuję w liście jednokierunkowej z głową. Niestety coś nie działa przy łączeniu i obracaniu (jak mniemam). Nie mogę się doszukać błędu za nic. Najprawdopodobniej przy pewnych danych natrafiam gdzieś na nulla.

Sama idea, to sprawdzanie poprzednika przy iterowaniu po elementach (w przypadku wyświetlania). Ta sama zasada dotyczy pozostałych operacji gdzie muszę sprawdzać czy lista jest odwrócona czy też nie, np. przy łączeniu gdzie spinam ostatni element pierwszej listy z pierwszym drugiej listy.

Oto kod:

class Link {

    public Link next;
    public Link prev;
    public String value;

    public Link(String value) {
        this.value = value;
        this.next = null;
        this.prev = null;
    }
}

class DoublyLinked {

    public String name;
    public Link first;
    public Link last;
    public DoublyLinked next;

    public DoublyLinked() {
        Link c = new Link("");
        name = "";
        last = first = c;
    }

    public DoublyLinked(String name, String w) {
        New(name, w);
    }

    private void New(String n, String w) {
        Link c = new Link(w);
        name = n;
        last = c;
        first = c;
    }

    public void insertFirst(String w) {
        Link temp = new Link(w);
        if (first.next == null && first.prev != null) { // jeśli lista odwrócona
            first.next = temp;
            temp.prev = first;
        } else {
            first.prev = temp;
            temp.next = first;
        }
        first = temp;
    }

    public void insertLast(String w) {
        Link temp = new Link(w);

        if (last.prev == null && last.next != null) { // gdy lista odwrócona
            last.prev = temp;
            temp.next = last;
        } else { // gdy lista nie jest odwrócona lub 1 elem.
            last.next = temp;
            temp.prev = last;
        }
        last = temp;
    }

    public String delFirst() {

        String temp = new String();
        temp = first.value;
        if (first == last) { // Jeśli lista ma 1 elem.
            first = last = null;
        } else if (first.prev == null && first.next != null) { // Jeśli lista nieodwórocna
            if (first.next.next == first) {
                first.next.next = null;
            } else {
                first.next.prev = null;
            }
            first = first.next;
        } else if (first.prev != null && first.next == null) { // Jeśli lista odwrócona
            if (first.prev.prev == first) {
                first.prev.prev = null;
            } else {
                first.prev.next = null;
            }
            first = first.prev;
        }
        return temp;
    }

    public String delLast() {
        String temp = new String();
        temp = last.value;
        if (first == last) { // Jeśli lista ma 1 elem.
            first = last = null;
        } else if (last.next == null && last.prev != null) { // Jeśli lista nieodwrócona
            if (last.prev.prev == last) {
                last.prev.prev = null;
            } else {
                last.prev.next = null;
            }
            last = last.prev;
        } else if (last.next != null && last.prev == null) { // Jeśli lista odwrócona
            if (last.next.prev == last) {
                last.next.prev = null;
            } else {
                last.next.next = null;
            }
            last = last.next;
        }

        return temp;
    }

    public void reverse() {
        Link swap = first;
        first = last;
        last = swap;
    }

    public String display() {

        Link curr = first;
        Link prev = null;
        boolean REVERSE_FLAG;
        String output = new String();


        // Sprawdza czy pierwsza lista odwrócona;
        if (first.next == null && first.prev != null) {
            REVERSE_FLAG = true;
        } else {
            REVERSE_FLAG = false;
        }

        do {
            output += " " + curr.value;

            // Sprawdza czy kolejna lista odwrócona
            if (REVERSE_FLAG) {
                if (curr.prev == prev) {
                    REVERSE_FLAG = !REVERSE_FLAG;
                }
            } else {
                if (curr.next == prev) {
                    REVERSE_FLAG = !REVERSE_FLAG;
                }
            }

            prev = curr;

            if (REVERSE_FLAG) {
                curr = curr.prev;
            } else {
                curr = curr.next;
            }
        } while (curr != null);

        return output.trim();
    }

    public boolean isEmpty() {
        return (first == null && last == null) ? true : false;
    }

    public Link locate(String w) {
        Link curr = first;
        while (curr != null && !curr.value.equals(w)) {
            curr = curr.next;
        }
        return curr;
    }
}

class LinkHeadList {

    private DoublyLinked head;
    private DoublyLinked last;

    public LinkHeadList() {
        head = new DoublyLinked();
        head.next = null;
        last = null;
    }

    public void New(String name, String w) {
        DoublyLinked t = new DoublyLinked(name, w);
        if (isEmpty()) {
            this.head.next = t;
            this.last = t;
        } else {
            this.last.next = t;
            this.last = t;
        }
    }

    public void insertFirst(String t1, String w) {
        DoublyLinked temp = locate(t1);
        temp.next.insertFirst(w);
    }

    public void insertLast(String t1, String w) {
        DoublyLinked temp = locate(t1);
        temp.next.insertLast(w);
    }

    public void delFirst(String t1, String t2) {
        DoublyLinked list1 = locate(t1);
        String w = list1.delFirst();
        DoublyLinked list2 = new DoublyLinked(t2, w);
        if (list1.next.isEmpty()) {
            if (list1.next == last) {
                last = list1;
                list1.next = null;
            } else {
                list1.next = list1.next.next;
            }
        }
        last.next = list2;
        last = list2;
    }

    public void delLast(String t1, String t2) {
        DoublyLinked list1 = locate(t1);
        String w = list1.next.delLast();
        DoublyLinked list2 = new DoublyLinked(t2, w);
        if (list1.next.isEmpty()) {
            if (list1.next == last) {
                last = list1;
                list1.next = null;
            } else {
                list1.next = list1.next.next;
            }
        }
        last.next = list2;
        last = list2;
    }

    public void union(String t1, String t2) {
        DoublyLinked list1, list2, swap;
        list1 = locate(t1).next;
        list2 = swap = locate(t2);
        boolean[] REVERSE = new boolean[2];


        // Sprawdza czy 1 lista odwrócona
        if (list1.first.next == null && list1.first.prev != null) {
            REVERSE[0] = true;
        } else {
            REVERSE[0] = false;
        }

        // Sprawdza czy 2 lista odwrócona
        if (list2.next.first.next == null && list2.next.first.prev != null) {
            REVERSE[1] = true;
        } else {
            REVERSE[1] = false;
        }

        if (REVERSE[0]) {
            list1.last.prev = list2.next.first;
        } else {
            list1.last.next = list2.next.first;
        }

        if (REVERSE[1]) {
            list2.next.first.next = list1.last;
        } else {
            list2.next.first.prev = list1.last;
        }

        list1.last = list2.next.last;

        swap.next = swap.next.next; // Odłącza niepotrzebną listę ze zbioru list
    }

    public String display(String t) {
        DoublyLinked temp = locate(t);
        String output = temp.next.name + ": " + temp.next.display() + "\n";
        System.out.print(output);
        return output;
    }

    public void reverse(String t) {
        DoublyLinked temp = locate(t);
        temp.next.reverse();
    }

    public boolean isEmpty() {
        return (head.next == null) ? true : false;
    }

    private DoublyLinked locate(String name) {
        DoublyLinked curr = this.head;
        while (curr.next != null) {
            if (curr.next.name.equals(name)) {
                return curr;
            }
            curr = curr.next;
        }
        return curr;
    }
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1