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