Witam, pracuję nad iteracyjną wersją algorytmu sortowania przez kopcowanie. Oczywiście, w sieci znalazłem masę zaimplementowanych przykładów, gotowców, wyjaśnień i rozwiązań problemów. Ja teoretycznie bardzo dobrze rozumiem ideę algorytmu, tylko mam problem ze swoim własnym kodem. Wiem nawet, gdzie jest błąd. Program ma sortować 10 znaków z zakresu 'A-Z'.

 public class HeapSort {

    private int stepCounter = 0; // licznik kroków
    char[] opArray = new char[10]; // tablica robocza przeznaczona na ciąg znaków
    //char[] inputArray = new char[10]; // tablica wejściowa

    public HeapSort(String input) throws WrongLengthException {
        if (input.length() != 10) {
            // powinien zostać wyrzucony wyjątek, długość ciągu wejściowego jest inna niż 10
            throw new WrongLengthException();
        }
        opArray = input.toCharArray(); // konwersja ciągu znaków na ich tablicę
        for (int i = 0; i < 10; i++) {
            opArray[i] = Character.toUpperCase(opArray[i]); // konwersja wszystkich liter ciągu na małe
        }
    }

    public void buildHeap() {
        System.out.println("==BUDOWANIE STOGU==");
        drawHeap();
        System.out.println("==NAPRAWIANIE STRUKTURY STOGU==");
        for (int i = 0; i < 5; i++) {
            // ostatni węzeł (niebędący liściem) stogu o rozmiarze 10 ma numer 4
            repairHeap(i);
        }
        System.out.println("==WŁAŚCIWE SORTOWANIE==");
        sortHeap();
    }

    private void repairHeap(int element) {
        int max;
        while ((max = maxFromThree(element)) != element) {
            swap(element, max);
            element = max;
        }
    }

    private void sortHeap() {
        for (int i = 0; i < 9; i++) {
            swap(0, 9 - i);
            repairHeap(i);
        }
    }

    private boolean hasLeftChild(int element) {
        return element < 5 ? true : false;
    }

    private boolean hasRightChild(int element) {
        return element < 4 ? true : false;
    }

    private void swap(int indexA, int indexB) {
        char tmp = opArray[indexB];
        opArray[indexB] = opArray[indexA];
        opArray[indexA] = tmp;
    }

    private int maxFromThree(int parentIndex) {
        int max;
        if (hasLeftChild(parentIndex) && CharComparator.compare(opArray[parentIndex], opArray[2 * parentIndex + 1]) < 0) {
            max = 2 * parentIndex + 1;
        } else {
            max = parentIndex;
        }
        if (hasRightChild(parentIndex) && CharComparator.compare(opArray[max], opArray[2 * parentIndex + 2]) < 0) {
            max = 2 * parentIndex + 2;
        }
        return max;
    }

Uff. Idea działania mojego algorytmu jest taka: utwórz kopiec z elementów w kolejności podania, a następnie go napraw w taki sposób, by uzyskał własności kopca. Czyli, dla każdego węzła niebędącego liściem wykonaj metodę repairHeap(), w której to właśnie jest pies pogrzebany. Działa ona tak: przyjmuje indeks pewnego węzła (element), po czym wybiera największą wartość z trójki - rodzic i dwójka jego "dzieci". Jeśli to węzeł rodzica jest największy, pętla się kończy, a dopóki tak nie jest, zostaje zamieniony z większym z dzieci i indeks większego z dzieci zostaje ustawiony jako nowy węzeł rodzicielski. Problem w tym, że to nie działa, bo niektóre zamiany nie wykonują się, choć powinny. Weźmy przykład:

Do posortowania mamy ciąg:
AIBCJEFGHD

Budujemy z niego kopiec (w kolejności jak leci, zaraz będziemy go naprawiać):
A
/
I B
/ \ /
C J E F
/ \ /
G H D

Najpierw wykonujemy repairHeap() dla elementu o indeksie 0 (czyli u nas A) - robi to w kodzie pętla for z metody buildHeap() - pierwsza iteracja. Oczywiście I zamieniane jest z A i max zostaje ustawiony na 1 (bo tam zostało znalezione I i tam teraz znajduje się A).

  I
 /  \
A    B

/ \ /
C J E F
/ \ /
G H D

Teraz na pozycji 5 znaleźliśmy J, które jest większe od naszego maxa, czyli A z pozycji 1. Zamieniamy i ustawiamy max na 5.
I
/
J B
/ \ /
C A E F
/ \ /
G H D

Teraz już do przeszukania nie mamy trójki liczb, lecz dwie, bo pozycja 5 nie ma prawego potomka. Ale to nieistotne. Chodzi o to, że zgubiliśmy konieczność przeszukania trójki, której rodzicem jest I. Widzimy, że z trójki I-J-B już niekoniecznie I jest największym, więc trzeba tutaj też zrobić iterację (dla zmiennej max = 0). Można oczywiście linię "element = max" zamienić na "element = parent(element)", ale to też nie zadziała, bo wtedy zgubimy tę drugą zamianę, która teraz się wykonała (czyli na rysunku A i D). Jak zapamiętywać, że do wykonania są dwie zamiany, robiąc to w jednej pętli?

Bardzo dziękuję za pomoc, będę zobowiązany. Program mam dziś oddać, siedzę przy tym od dwóch dni i w końcu zdecydowałem się napisać.

EDIT: najmocniej przepraszam, że nie w tym dziale. Klikałem dział Java, coś Wam się psuje chyba. Albo jestem aż tak odurzony tym kodzeniem