matematykaszkolna.pl
Znajdź błąd w kodzie Mariusz: struct node{ int key; struct node *next; }; void ListInsert(struct node **L,int x) { struct node *px; struct node *y; struct node *z; px=(struct node *)malloc(sizeof(struct node)); px−>data=x; if((*L)==NULL) { (*L)=px; x−>next=NULL; } else { y=(*L); z=NULL; while(y!=NULL && x−>key>y−>key) { z=y; y=y−>next; } px−>next=y; z−>next=px; } }
5 maj 12:42
a: a na samym poczatku w definicji to nie powinno byc juz node *next ? zamiast struct node *next?
5 maj 12:47
Mariusz: oczywiście x−>next=NULL to literówka i powinno tam być px−>next=NULL
5 maj 12:48
Mariusz: a: myśl dalej Jeden błąd to literówka , wyżej go wypisałem Jako podpowiedź co jest nie tak mogę dodać że węzły z kluczem większym od klucza w węźle wskazywanym przez głowę zdaje się wstawiać poprawnie a: możesz nie pisać stale słówka struct jeśli dasz typedef
5 maj 12:53
jc: Co to jest "data" ? czy to też literówka? Dlaczego nie napiszesz po prostu 0, tylko NULL?
5 maj 14:44
Mariusz: data to jest pozostałość po starej nazwie pola danych To też łatwo wychwycić
5 maj 14:52
Sawyer: A gdzie jest inicjalizowana wartość klucza którą porównuje pętla while?
5 maj 15:37
Sawyer: Jeśli Twój kod ma za zadanie wstawiać elementy na przykład na środku listy i nadawać im unikalny klucz wewnętrzny, to przecież należy po wstawieniu elementu pozmieniać wszystkie klucze węzłów znajdujących się za właśnie wstawionym węzłem. Jeśli natomiast ma po prostu wstawiać w dane miejsce np. a−>b−>c−>d, wstaw literę "e" na 2 pozycji: a−>b−>e−>c−>d, to zmienna klucz jest do niczego. Nie ma sensu aktualizować jej, wystarczy zliczać obroty pętli przy przechodzeniu przez listę, a potem zmieniać wskaźniki węzła poprzedniego i następnego. No i dlaczego zmienna L jest wskaźnikiem na wskaźnik to ja nie wiem. Jeśli chcesz zamknąć całą listę w jednym obiekcie to stwórz strukturę listy wewnątrz której jest zagnieżdżona struktury węzłów. Teraz jest tak że zmienna L wskazuje na miejsce w pamięci wskaźnika który wskazuje na obiekt typu node, nie do końca wiem czemu. Możliwe że gadam niestworzone głupoty, jeśli tak proszę mnie poprawić.
5 maj 16:11
Sawyer: W C++ widziałbym to tak: #include <iostream> #include <new.h> #include <conio.h> using namespace std; struct node{ node *next; int data; }; void ListInsert(node *&ListHead, int insertPosition, int data) { /* Stworzenie nowego węzła */ node *newNode; newNode = new node; newNode−>data = data; //utworzenie głowy jeśli nie istnieje if (ListHead == NULL) { newNode−>next = NULL; ListHead = newNode; // tworzy głowę } else { if (insertPosition == 0){ // trzeba zmienić głowę newNode−>next = ListHead; ListHead = newNode; return; } node *iterator; // aktualnie sprawdzana pozycja na liście iterator = ListHead; // ustawia sie na głowie /* jeżeli następna pozycja NULL to koniec listy, lub za pomocą insertposition zliczamy na którym miejscu jesteśmy*/ while (iterator−>next != NULL && insertPosition > 1) { // przy każdym obrocie pętli zostaje nam o jeden mniej mniejsc do przesunięcia iteratora insertPosition−−; iterator = iterator−>next; } /*znaleźlismy miejsce, iterator jest w miejscu gdzie 1 miejsce za nim jest nasze docelowe do wstawienia węzła*/ if (insertPosition <= 1){ /*aby zachować ciągłość listy i nie stracić połączeń tymczasowo stwarzamy wskaźnik na listę za miejscem wstawiania*/ node *bufforNode; bufforNode = iterator−>next; iterator−>next = newNode; // wstawiamy węzeł za iterator newNode−>next = bufforNode; // a całą reszę listy za właśnie wstawiony węzeł } else{ // wyszliśmy poza listę iterator−>next = newNode; newNode−>next = NULL; } } } int main(){ node *Head = NULL; ListInsert(Head, 0, 100); ListInsert(Head, 1, 11); ListInsert(Head, 2, 12); ListInsert(Head, 3, 114); ListInsert(Head, 1, 8); ListInsert(Head, 10, 90); ListInsert(Head, 3, 3); ListInsert(Head, 0, 3); ListInsert(Head, 9, 5); ListInsert(Head, 0, 59); node *displayPointer = Head; while (displayPointer!=NULL){ cout << displayPointer−>data << endl; displayPointer = displayPointer−>next; } getch(); }
5 maj 16:35
Mariusz: Sawyer dlaczego jest wskaźnik na wskaźnik ? W C nie ma referencji , referencja pojawia się dopiero w C++ Funkcja ma wstawiać elementy do listy w ten sposób aby wartości wyróżnionego pola zwanego kluczem były uporządkowane (w tym wypadku rosnąco) Gdybyśmy chcieli nadać węzłom unikalny klucz funkcję trzeba by trochę zmodyfikować Dopisałem funkcje wypisująca dane i wyszukująca węzeł struct node{ int key; struct node *next; }; void ListInsert(struct node **L,int x) { struct node *px; struct node *y; struct node *z; px=(struct node *)malloc(sizeof(struct node)); px−>key=x; if((*L)==NULL) { (*L)=px; px−>next=NULL; } else { y=(*L); z=NULL; while(y!=NULL && x−>key>y−>key) { z=y; y=y−>next; } px−>next=y; z−>next=px; } } void ListPrint(struct node *L) { struct node *p; p=L; while(p!=NULL) { printf("%d−>",p−>key); } printf("NULL\n"); } struct node* ListSearch(struct node *L,int k) { struct node *p; p=L; while(p!=NULL && p−>key!=k) p=p−>next; return p; } Sawyer przeczytałeś chociaż kod tej funkcji linia po linii ? Co według ciebie mogłoby powodować niepoprawne działanie tej funkcji wstawiającej ? Gdyby ktoś z was znalazł błąd to moglibyśmy przejść do usuwania elementów
5 maj 18:58
Mariusz: Przydałaby się edycja oczywiście po wypisaniu wartości klucza czy wyświetlania danych w węźle iterujemy także wskaźnik p=p−>next;
5 maj 19:04
Sawyer: To powinno rozwiązać Twój problem void ListInsert(struct node **L,int x) { struct node *px; struct node *y; struct node *z; px=(struct node *)malloc(sizeof(struct node)); px−>key=x; if((*L)==NULL) { (*L)=px; px−>next=NULL; } else { y=(*L); z=NULL; do{ z=y; y=y−>next; } while(y!=NULL && x−>key>y−>key) ; px−>next=y; z−>next=px; } }
5 maj 19:16
Sawyer: Ah wybacz, nie wziąłem jeszcze jednej rzeczy pod uwagę.
5 maj 19:23
Sawyer: Ponieważ tak na prawdę problem jest taki. Powiedzmy że tworzysz głowę z kluczem 3. Następnie dodajesz węzeł o kluczu 3. To zauważ że jeśli masz while(state) to wskaźnik Z jest NULL. wskaźnik Y jest głową, pętla while nie wykonuje się ani razu, ponieważ 3>3 = false zostawiając wskaźnik Z na NULL, a później chcesz odczytać wartość z−>next ze wskaźnika który nie istnieje. Czyli problem wystąpi zawsze wtedy gdy dodasz wartość mniejszą bądź równą od głowy. Z wartością równą głowie można poradzić sobie ustawiając Z na samym początku na głowę lub stosując pętlę do−while. Ale z wartością mniejszą od głowy to zaraz pomyślę.
5 maj 19:25
Sawyer: Wydaje mi się że tak powinno być dobrze. Sprawdziłem i działa, na wszystkich przypadkach. void ListInsert(struct node **L,int x) { struct node *px; struct node *y; struct node *z; px=(struct node *)malloc(sizeof(struct node)); px−>key=x; if((*L)==NULL) { (*L)=px; px−>next=NULL; } else { y=(*L); z=NULL; while(y!=NULL && x−>key>y−>key) { z=y; y=y−>next; } if (z == NULL){ px−>next = L; L = px; } else{ px−>next = y; z−>next = px; } } } } Dodałem jedną If−kę która po prostu sprawdza z której strony głowy należy wartość wstawić jeśli z "prawej" (instrukcja dla else) to nic sie nie zmienia Jeśli natomiast z lewej to nowy węzeł wskazuje na starą głowę i sam staje się głową, czy dobrze myślę?
5 maj 19:33
Sawyer: Oczywiście tam powinno być px−>next = (*L) oraz (*L) = px. Ale zwykle piszę w C++ i tak z przyzwyczajenia taka literówka.
5 maj 19:39
Mariusz: z−>next=px; Ja opakowałem powyższą instrukcję instrukcją warunkową if i też mi działało Ciekaw byłem czy wyłapiecie błąd no i czy nie ma innych błędów Sawyer może zastanówmy się teraz nad usuwaniem elementów np void ListDelete(struct node **L,struct node * x) { // Tu jest nasz kod } Jakie są warunki brzegowe ? Co może powodować błędy ? W jakim miejscu zwalniać pamięć
5 maj 20:15
Sawyer: dostajemy na argumencie od razu węzeł do usunięcia? Fajniej by było z kluczem emotka Tak więc na samym początku sprawdzamy czy węzłem jest czy nie jest głowa. Jeśli tak to tworzymy buffor przetrzymujący głowa−>next, następnie usuwamy miejsce w pamięci od głowy, a później ustawiamy na nową głowę buffor. Jeśli węzeł to nie głowa na samym początku ustawiamy iteratorFront na głowie, natomiast iteratorBack na NULL potem przesuwamy się w pętli iteratorFront na iteratorFront−>next a iteratorBack staje się iteratorFront, pętla działa aż do znalezienia klucza, czy węzła który przeznaczony jest do usunięcia. Oznacza to że iteratorBack jest teraz na węźle poprzedzającym, przypadek brzegowy gdy chcemy usunąć głowę został rozpatrzony wcześniej ale jeśli by nie został to teraz iteratoBack były NULL i powodowałoby to błędy. IteratorFront wskazuje na nasz element do usunięcia natomiast IteratorFront−>next na resztę listy. Tworzymy buffor przechowujący wartość IteratorFront−>next, usuwamy miejsce w pamięci pod adresem z iteratoFront, ustawiamy iteratorBack−>next na buffor. Przypadek gdy iteratorFront−>next jest NULL nic nie zmiania bo to oznacza po prostu że iteratorBack−>next staje się NULL, co oznacza koniec listy.
5 maj 21:02
Mariusz: Spróbuje na podstawie tego opisu napisać funkcje usuwającą węzeł W razie czego poprawisz ?
5 maj 21:37
Sawyer: w C++ to będzie tak, wydaje mi się że wszystko przewidziałem, czy nie? Sprawdziłem i działa, dla usuwania głowy, usuwania, wewnątrz i usuwania z końca. void deleteNode(node *&L, node *x) { struct node *y; struct node *z; if (x == L) { node *headNext = L−>next; delete L; L = headNext; } else { y = (L); z = NULL; while (y != NULL && y!=x){ z = y; y = y−>next; } node *buffor; buffor = y−>next; delete y; z−>next = buffor; } }
5 maj 21:45
Sawyer: Czy w ogóle jest sens rozpatrywać przypadek w który mamy sobie węzeł "wolny" nie należący do naszej listy nie połączony w żaden sposób z całością, i chcemy go usunać, bo wtedy oczywiście kod nie zadziała. Ale to chyba w ogóle przeczy idei usuwania obiektu z listy, bo przecież go na liście nie ma.
5 maj 21:48
Mariusz: Myślę że takie przypadki też trzeba rozważyć bo jeżeli użytkownik poda wskaźnik który nie pokazuje na węzeł listy albo gdy poda NULL to program się "rozsypie"
5 maj 21:57
Sawyer: no to na samym początku funkcji będzie if (x == NULL)return; A po pętli while if (y == NULL) { delete y; return; }, czyli while (y != NULL && y!=x){ z = y; y = y−>next; } if (y == NULL) { delete y; return; } czyli jeśli przeszlismy całą pętlę i nasz iteratorFront jest NULL znaczy doszliśmy do końca pętli nie znajdując naszego węzła, ergo go tam po prostu nie ma, usuwamy go wiec tak po prostu i wychodzimy z funkcji.
5 maj 22:30
Mariusz: Znalazłem taki oto pseudokod List−Del(L,x) if head[L]! =NIL then if head[L] =x then head[L] :=next[x]; else y:=head[L]; while(next[y]! =x) do y:=next[y]; end while next[y] :=next[x]; end if end if i wygląda na to że ma więcej błędów niż ten pseudokod do wstawiania elementu do listy
5 maj 23:10
Sawyer: W ogóle Mariusz, jeśli to nie tajemnica mogę zapytać czym się zajmujesz emotka ? Przeglądałem Twoje posty i jestem ciekaw co robisz. A co do pseudokodu, to zacznijmy od tego że tu nie ma w ogóle zwalniania pamięci, nie rozważa przypadków gdy element jest spoza listy, ale sam kod jest troszkę inny, zauważ że tutaj dane są przechowywane w tablicy, więc nadpisując miejsce w tablicy powiedzmy "next" nie tracimy informacji o tym co jest ZA nextem bo po prostu będzie to kolejne pole w tablicy, dlatego i buffor dodatkowy przechowujący tymczasowy wskaźnik (jak w moim kodzie), jest niepotrzebny, kod jest spoko, ale po pierwsze, to nie jest dynamiczna alokacja pamięci, to jest tablica z komórkami które mogą być wskaźnikami na obiekty lub coś w ten deseń, ale wciąż komórki same z siebie istnieją. Tutaj nie potrzebujesz też dwóch wskaźników (y, z) ponieważ, wiesz co będzie dalej, wystarczy odnieść się do komórki w tablicy. Lista wygląda tak O2−>O1−>O3−>O4−>NULL W tym przypadku to działa tak że powiedzmy mamy tablice next: zastosuje taki zapis numerobiektu−>pole w tablicy które jest kolejne. Tablica tak: |NULL|O1−>3|NULL|O3−>5|O2−>1|O4−>null| Chcemy usunąć obiekt o indeksie 3. Więc wystarczy że zaczniemy od głowy(pole tablicy 4) O1 ma ona indeksNext = 1, więc wejdziemy do tablicy o indeksie 1 która z kolei skieruje nas na indeks 3 itp itd. Jeśli zauważmy że tablica chce nas skierować na komórkę w której jest nasz obiekt do usunięcia to znaczy że czas na usuwanie. Jesteśmy w komórce numer [1], wiemy że w komórce do której nas skieruje [3] jest interesujący nas obiekt. Nie chcemy żeby z komórki [1] nas kierowało do [3], chcemy niejako obejść ten obiekt. Czyli robimy taką operację next[1](miejsceDoSkierowania) = next[3](miejsceDoSkierowania) I dostajemy wtedy taką tablicę: |NULL|O1−>5|NULL|O3−>5|O2−>1|O4−>null| O2−>O1−>O4−>NULL No przynajmniej ja tak rozumiem ten pseudokod. Czyli lista na tablicy, jakoś nie widzę w tym sensu.
5 maj 23:46
Sawyer: http://codepad.org/WWsdWfwp Napisałem Ci Mariusz bibliotekę takiej listy od zera w C++, może coś Ci się przyda jak sobie przeczytasz, jeśli masz jakieś pytania to śmiało. Potem możemy ją zamienić bardzo prosto na coś ala vector, kontener, za pomocą operatora [], ale już nie chciałem komplikować sprawy.
6 maj 01:06
Mariusz: Gdyby mi programowanie jakoś wychodziło to pewnie bym coś robił http://eduinf.waw.pl/inf/alg/001_search/0083.php Przeglądałem też tę stronę ale tutaj koleś się nie kryje z tym że w jego opisach algorytmów mogą błędy Gdy zwróciłem na to uwagę na innym forum to gimbusy próbowały mnie wyśmiać Starsze środowiska Pascala nie obsługują dynamicznych tablic oraz korzystają z DOSowego modelu pamięci i tam lista przydaje się bardziej Jeżeli mamy usuwanie gdy dany jest wskaźnik to przydaje się też odpowiednio napisana funkcja wyszukująca
6 maj 09:10
Mariusz: http://codepad.org/3o61eBO9 Znalazłem w sieci taki kod funkcji sortującej przez scalanie z trzema wersjami funkcji scalającej Przydałoby się sprawdzić czy ta wersja z tzw fałszywym węzłem jest poprawna (nie spowoduje jakichś błędów) a także opisać te procedury Jak przerobić te funkcje sortujące przez scalanie tak aby nadawały się także do sortowania listy dwukierunkowej Przypuśćmy że chcielibyśmy napisać program do całkowania funkcji wymiernych Korzystamy z takiego schematu 1. deg L(x)>=deg M(x) Dzielimy wielomiany z resztą
 L(x) R(x) 

dx=∫W(x)dx+∫

dx
 M(x) M(x) 
2. gcd(M(x),M'(x))≠const Stosujemy sposób Ostrogradskiego wydzielenia części wymiernej całki
 R(x) R1(x) R2(x) 

dx=

+∫

dx
 M(x) M1(x) M2(x) 
M1(x)=GCD(M(x),M'(x)) M(x)=M1(x)M2(x) deg R1(x)<deg M1(x) deg R2(x)<deg M2(x) Wielomiany z liczników znajdujemy korzystając z współczynników nieoznaczonych 3. gcd(M(x),M'(x))=const Stosujemy rozkład na sumę ułamków prostych Rozkład ten będzie jednak łatwiejszy bo mianownik nie ma pierwiastków wielokrotnych Trójmiany kwadratowe występujące w rozkładzie mianownika zapisujemy w postaci kanonicznej Wielomian można przechowywać na liście aby nie przechowywać zer jeśli się pojawią Przydatne operacje na wielomianach 1. Dodawanie wielomianów 2. Odejmowanie wielomianów 3. Mnożenie wielomianów 4. Dzielenie wielomianów z resztą 5. NWD wielomianów (wykorzystujemy funkcję z punktu 4.) 6. Schemat Hornera 7. Pierwiastki wielomianu bądź rozkład wielomianu na czynniki (Tutaj musimy zadowolić się metodami numerycznym, najlepsza byłaby taka która znajdowałaby najpierw czynniki kwadratowe)
6 maj 12:20
Mariusz: Do twojej listy dopisałem funkcję zwracającą pozycje wyszukiwanego węzła o danym kluczu
6 maj 14:54
Sawyer: Jeśli piszemy program stricte do wielomianów to nie trzeba pisać funkcji wstawiania w dowolnym miejscu, każdorazowo dodanie nowego elementu będzie przez funkcję sortującą (tam w moim kodzie to jest insertSortedByValue), która będzie przyrównywać stopnie X w wyrazach wprowadzanych, czyż nie?
6 maj 17:17
Mariusz: Twoja liste na razie testowalem piszac sito Eratostenesa Widzialem ze niektorzy proponuja uzyc listy do przechowywania wielomianu W przypadku wielomianu na liscie przydaloby sie napisac wymienione funkcje Ja na tablicy mialem funkcje dodawanie wielomianow, mnozenie wielomianow, dzielenie wielomianow z reszta (z Numerical recipes in C) schemat Hornera pierwiastki tylko do czwartego stopnia wlacznie gdzie nie trzeba bylo metod numerycznych http://codepad.org/zvWnqSsy Tutaj mamy liste z sortowaniem przez scalanie oraz z odwracaniem kolejnosci Jesli chodzi o sortowanie listy przez scalanie to czy scalanie w tzw falszywym wezlem (dummy node) jest napisane bez bledow Przydaloby sie tez porownanie tych funkcji scalajacych jedna jest napisana rekurencyjnie, druga wykorzystuje falszywy wezel (dummy node) trzecia jest napisana iteracyjnie bez zadnych udziwnien Jak przerobić te funkcje sortujące przez scalanie tak aby nadawały się także do sortowania listy dwukierunkowej Wstawienie n wezlow w sposob uporzadkowany to ∑1nk czyli O(n2) Wstawienie n wezlow a pozniej posortowanie scalaniem O(nlog n) pod warunkiem ze wezly wstawiamy na poczatek listy Tak wiec jesli chcemy miec liste stale posortowana to lepiej uzyc wstawiania jezeli potrzebujemy posortowac liste raz na jakis czas lepsze bedzie sortowanie przez scalanie Ostatnio na podstawie pseudokodow Cormena napisalem kopiec na tablicy Moze gdy w przyszlosci przejdziemy do drzew to napiszemy go dynamicznie na drzewie
6 maj 20:59
Mariusz: Jeśli chodzi o sortowanie tablic to kody można znaleźć Sortowanie bąbelkowe // Wirth p 66 Sortowanie przez wstawianie // CLRS p 18,26 Sortowanie przez wybór // Wirth p 64 Sortowanie przez kopcowanie // CLRS p 154,157,160 // Wirth p 75 Sortowanie przez scalanie // CLRS p 31,34 Sortowanie przez podział // Wirth p 79 Jeśli chodzi o sortowanie plików to kod łączenia naturalnego znalazłem w jakimś hiszpańskojęzycznym pdf o programowaniu w Pascalu Co do wyszukiwań to mamy funkcje function LinearSearch(A:TArray;n:integer;x:TElem):integer; var i:integer; begin i:=1; while((i<=n)and(A[i]<>x))do i:=i+1; if(i>n) then LinearSearch:=0; (*kazda wartosc poza przedzialem dla indeksów tablicy będzie dobra*) else LinearSearch:=i; end; function BinarySearch(A:TArray;n:integer;x:TElem):integer; var p,q,r:integer; found:boolean; begin p:=1; r:=n; found:=false; while((p<=r)and(not found))do begin q:=(p+r) div 2; if(A[q]=x) then found:=true else if(A[q]>x)then r:=q−1 else p:=q+1; end; if (not found) then BinarySearch:=0 else BinarySearch:=q end; Tak jak wspomniałem napisałem też tablicową wersję kopca bazując na pseudokodach Cormena i teraz czas na listy na których można napisać stos a także kolejkę
9 maj 14:26