lista dwukierunkowa sprawdzenie i poprawa błędów

flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

witam mam taką treść zadania:


dana jest lista 2 kier. Na której 1 element wskazuje zmienna wskaźnikowa head. Lista przechowuje wartosci float. Napsiz fukcje która usunie z listy pierwszy znaleziony element którego wartość przechowywana spełnia nierowność: -1,5<wartosc elem<5. Argumentem funkcji jest tylko wskaźnik head funkcja zwraca pozycje usuniętego elementu.

Kod: Zaznacz cały

# include <stdio.h>
# include <stdlib.h>

typedef struct Element_t
{
       float element;
       Element_t *next;
       Element_t *prev;
};

Element_t *head;


void usuń(Element_t *rob)

{
   Element_t *prev_1, *next_1;
   
   prev_1 = rob->prev;
   next_1 = rob->next;
   
   free(rob);
  
   if ((prev_1<5 && (-1,5<next_1) ) 
   {head=NULL; tail=NULL; return;}
    
   if ((prev_1)&&(next_1))
   {
    prev_1->next = next_1;
    next_1->prev = prev_1;     
   }
   
   if (prev_1==NULL) {head = next_1; next_1->prev=NULL;}
   if (next_1==NULL) {tail = prev_1; prev_1->next=NULL;}
}
funkcje wzięłam z innego programu i troche pozmieniałam... ale nie wiem czy ona działa na pewno dobrze.
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

Jak na razie twoja funkcja zwolniłaby pamięć zajmowaną przez głowę listy (skoro masz przekazać wskaźnik do niej jako argument), gdyby w ogóle miała szansę zadziałać, bo zaraz potem usiłuje porównać floata ze wskaźnikiem na Element_t.

Zacznij może od napisania funkcji, która wyszuka w liście dwukierunkowej pierwszy element o danej wartości klucza (i zwróci wskaźnik do niego). Jako argument niech przyjmie szukaną wartość klucza oraz wskaźnik do głowy listy. Potem pomyślimy, co dalej.
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

ehh dzięki za usiłowanie pomocy .. ale chyba nic nie rozumiem z tego .
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

Przyjmuję podziękowania i usiłuję dalej :

Zacznijmy w takim razie od początku.

Masz listę dwukierunkową. Jest to struktura, która u Ciebie składa się z następujących elementów:
klucz (czyli w praktyce - to, co w danym elemencie przechowujemy, np. wartość typu float)
wskaźnik do następnego elementu listy
wskaźnik do poprzedniego elementu listy

Szczególnymi elementami listy są głowa i ogon. Wskaźnik do poprzedniego elementu listy w głowie ma wartość NULL, podobnie jak wskaźnik do następnego elementu listy w ogonie.

Do danego elementu listy nie możemy dostać się bezpośrednio (nie możemy powiedzieć: "zmienię teraz wartość klucza piątego elementu listy" i natychmiast się do tego elementu dostać, tak jak w tablicy: t[5]=3). Do listy mamy dostęp sekwencyjny: wiemy gdzie jest głowa, głowa udostępnia nam wskaźnik, który pokazuje, gdzie jest drugi element listy; jak dostaniemy się do drugiego elementu, to on nam "pokaże", gdzie jest trzeci element itd.

Załóżmy, ze mamy typ Element_t zdefiniowany tak jak u Ciebie. Funkcja, która dostaje jako argument wskaźnik na głowę listy i "przechodzi" po kolejnych elementach listy, wygląda mniej więcej tak:

Kod: Zaznacz cały

void przeszukaj(Element_t *glowa)
{
    while(glowa->next != NULL) glowa=glowa->next;
} 
Czyli "dopóki nie okaże się, że wskaźnik o nazwie glowa pokazuje na ogon (czyli: na element, który we wskaźniku next ma wpisane NULL), przesuwaj go do przodu po liście (czyli: przypisuj mu wartość wskaźnika next, odczytaną z elementu, na który dotychczas wskazywał).

Łatwo zmodyfikować tę funkcję, żeby wykonywała jakąś konkretną akcję na wszystkich elementach listy, np. zwiększyła wartość wszystkich kluczy o 1:

Kod: Zaznacz cały

void przeszukaj(Element_t *glowa)
{
    while(glowa->next != NULL) 
    {
           glowa->element=glowa->element+1.0; 
           glowa=glowa->next;
    }
} 
Spróbuj zastąpić linijkę piątą tak, żeby funkcja w tym miejscu sprawdzała, czy wartość klucza danego elementu mieści się w przedziale \(\displaystyle{ (-1,5;5)}\). Jeśli tak, funkcja ma w tym właśnie miejscu zwrócić wskaźnik do tego elementu (na co trzeba zmienić słówko void rozpoczynające definicję funkcji?)
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

Na pewno rozumiem już więcej

Kod: Zaznacz cały

# include <stdio.h>
# include <stdlib.h>

typedef struct Element_t
{
       float element;
       Element_t *next;
       Element_t *prev;
};

Element_t *head,*ptr;

float przeszukaj(Element_t *glowa)
{
    while(glowa->next != NULL)
    {
           if ((prev<5 )&& (-1,5<next) )   //jeśli spełnia warunki to musi zwrócić wskaźnik na ten element i usunąc go
             {
                ptr=element;
                free(element);
                return ptr ; 
               }
          
       glowa=glowa->next;              // a jeśli nie to szuka dalej takiego elementu 
    
    }
}
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

Ok. Jest już dużo lepiej. Teraz jest jeszcze problem z obsługą listy.

Rozumiem, że funkcja free zwalnia pamięć zajmowaną przez to, na co pokazuje wskaźnik ptr. Twoja funkcja zapisuje zatem zawartość wskaźnika element do wskaźnika ptr, następnie zwalnia pamięć. Od tego momentu wskaźniki ptr i element pokazują na losowe miejsce w pamięci, niezwiązane z listą, w którym być może znalazły się już dane innego programu. Nie ma żadnego sensu zwracanie wskaźnika do usuniętego elementu (myślę, że w zadaniu nie chodziło o zwrócenie adresu, tylko informacji, którym z kolei elementem w liście był ten usunięty; tym zajmiemy się może później).

Jeszcze większym problemem jest rozerwanie listy. Element poprzedni w liście pokazuje na losowe miejsce w pamięci, a powinien pokazać na następny element w liście (po operacji usuwania jest nim ten element, który był w liście po usuniętym elemencie). Podobny problem jest właśnie z tym kolejnym elementem, którego wskaźnik prev po usunięciu powinien wskazywać gdzie indziej.

Jeśli zostawimy funkcję w takim kształcie, jaki ma obecnie, to cała część listy po usuniętym elemencie staje się śmieciem (bo nie zwolniliśmy pamięci, którą zajmuje, ale nie mamy do niej dostępu, bo zgubiliśmy do niej wszystkie wskaźniki).

Spróbuj teraz dodać po linijce 20. kod, który spowoduje, że przed usunięciem elementu lista będzie już poprawnie "połączona" (na takiej zasadzie):



Wskazówka: nie ma problemu, żeby skorzystać z z konstrukcji typu wskaznik->prev->next itp.
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

Kod: Zaznacz cały

# include <stdio.h>
# include <stdlib.h>

typedef struct Element_t
{
       float element;
       Element_t *next;
       Element_t *prev;
};

Element_t *head,*ptr;

float przeszukaj(Element_t *glowa)
{
    while(glowa->next != NULL)
    {
           if ((prev<5 )&& (-1,5<next) )   

             {
                ptr=element;
               ptr->prev=ptr->next   //tak to chyba element poprzedni i nastepny po ptr scale w jeden..?
                

              free(element);
                return ptr ;
               }
         
       glowa=glowa->next;              
   
    }
}
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

No właśnie niestety tak się nie da. W wyniku tego, co napisałaś ptr->prev pokazuje teraz na to, na co pokazywał ptr->next, a nie o to chodziło. Próbujesz zresztą scalić w jeden dwa elementy, które mają pozostać w liście (nie można z nich zrobić jednego - mamy usunąć ten element, który jest pomiędzy).

To może podpowiem:

Kod: Zaznacz cały

ptr->prev->next=prt->next; //pole next elementu pokazywanego przez ptr->prev ma pokazać element następny po ptr
ptr->next->prev=ptr->prev; //pole prev elementu następnego po ptr ma pokazać element przed ptr
(porównaj z rysunkiem z mojego poprzedniego posta)

Teraz zastanów się, czy funkcja zadziała poprawnie, kiedy okaże się, że do usunięcia jest głowa lub ogon listy.
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

ptr->prev->next // a to nie jest tak ptr --(prev)--> przed peterem--(next)-->wracam do ptr ??
czy to nie mogło być po prostu ptr ?


Jeżeli będzie to głowa lub ogon to chyba nie zadziała bo np jest ogon to :
ptr->prev->next=prt->next; tutaj trzeba zmienić bo po tym nie będzie już elementu tlyko będzie NULL?
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

Gdyby ptr->prev->next było po prawej stronie przypisania, to rzeczywiście do wskaźnika po lewej stronie przypisałabyś adres ptr.

Tu jednak chcesz zmienić wartość pola next, należącego do elementu przed ptr. Gdybyś miała osobny wskaźnik, powiedzmy ptr2, pokazujący na element przed ptr, to mogłabyś napisać:

ptr2->next=ptr->next (czyli: do wskaźnika ptr2->next - bo to pole jest jakimśtam wskaźnikiem - przypisz adres, na który pokazuje wskaźnik ptr->next)

Żeby ptr2 pokazywał istotnie na poprzedni element, mogłabyś napisać \(\displaystyle{ ptr2=ptr->prev}\).

W moim zapisie

Kod: Zaznacz cały

ptr2=ptr->prev;
ptr2->next=ptr->next;
jest zastąpione jedną linijką:

Kod: Zaznacz cały

(ptr->prev)->next=ptr->next;
ponieważ nam wskaźnik do ptr->prev potrzebny był tylko po to, żeby "dokleić" do niego "->next" i w ten sposób dobrać się do odpowiedniego pola.

Okazuje się, ze nawet nawiasy można pominąć:

Kod: Zaznacz cały

ptr->prev->next=ptr->next;
Mam nadzieję, że w miarę jasno to wytłumaczyłem. Zasadniczą różnicą jest to, czy dana zmienna (wskaźnik) stoi po lewej czy po prawej stronie operatora przypisania. Jeśli nie jest to dość jasne to:
Ukryta treść:    
Masz jakiś pomysł, jak poprawić funkcję, żeby oprawnie działała także dla głowy/ogona listy? Podpowiem, że wystarczy każdą z dwóch linijek, które Ci podpowiedziałem ostatnim razem, poprzedzić if-em.-- 23 września 2010, 16:40 --Może jeszcze taka sprawa: napisałaś, że z tym będzie problem:
ptr->prev->next=prt->next;
a ja mówię, z tym nie będzie problemu, jeśli ptr jest ogonem; bo pole (ptr->prev)->next po wykonaniu tej instrukcji zacznie pokazywać na to, na co pokazywało pole ptr->next, czyli na NULL (i bardzo dobrze - w ten sposób stanie się nowym ogonem). Problem z tą właśnie instrukcją będzie wtedy, kiedy ptr będzie głową (dlaczego?)
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

if((ptr->next)!=NULL)
ptr->prev->next=prt->next;


if((ptr->prev)!=NULL)
ptr->next->prev=ptr->prev;


??? Niee.. chyba ten program jest za skomplikowany dla mnie.
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

Chodziło o coś takiego, tylko odwrotnie .

if((ptr->prev)!=NULL)
ptr->prev->next=prt->next;

bo przecież zanim odwołasz się do jakiegokolwiek pola elementu ptr->prev, musisz sprawdzić, czy to pole w ogóle istnieje.


if((ptr->next)!=NULL)
ptr->next->prev=ptr->prev;

Po tej poprawce funkcja będzie prawie gotowa .

Teraz jeszcze taka sprawa:

Trzymamy listę za głowę. Poza funkcją jest zadeklarowany wskaźnik head, który ma pokazywać zawsze na głowę Twojej listy. Dlatego fragment:

Kod: Zaznacz cały

if((ptr->prev)!=NULL)
ptr->prev->next=prt->next;
trzeba uzupełnić w następujący sposób:

Kod: Zaznacz cały

if((ptr->prev)!=NULL)
ptr->prev->next=prt->next;
else head=ptr->next; //czyli "jeśli element do usunięcia JEST głową, to zmień zawartość zmiennej head
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

Dziękuje bardzo za pomoc!
Pewnie to śmieszne , że ktoś podstaw nie wie ; )

Na pewno nie umiem list dobrze ale teraz chociaż mniej więcej wiem o co chodzi ! ; )
Crizz
Użytkownik
Użytkownik
Posty: 4094
Rejestracja: 10 lut 2008, o 15:31
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 12 razy
Pomógł: 805 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: Crizz »

Ja wcale nie myślę, ze to śmieszne. Tak długo człowiek nie umie, dopóki nie usiądzie i się nie nauczy . Może na wszelki wypadek napiszę cały kod, żeby nie było wątpliwości:

Kod: Zaznacz cały

# include <stdio.h>
# include <stdlib.h>

typedef struct Element_t
{
       float element;
       Element_t *next;
       Element_t *prev;
};

Element_t *head,*ptr;

int przeszukaj(Element_t *glowa)
{
    int pozycja=0;
    while(glowa->next != NULL)
    {
           if(glowa->element<5.0 && glowa->element>-1.5)
           {
               ptr=glowa;
               if((ptr->prev)!=NULL) ptr->prev->next=prt->next;
               else head=ptr->next;
               if((ptr->next)!=NULL) ptr->next->prev=ptr->prev;
               free(ptr);
               return pozycja;
           }
           glowa=glowa->next;
           pozycja++;
    }
}
Tak na dobrą sprawę zmienna ptr jest tu średnio potrzebna, można by cały czas korzystać ze zmiennej glowa. Dodałem tu zmienną pozycja, która zlicza, ile razy "przeszliśmy" do kolejnego elementu listy, zanim dotarliśmy do elementu, który trzeba usunąć (czyli w praktyce: na której pozycji w liście był usunięty element).
flowers_evil
Użytkownik
Użytkownik
Posty: 87
Rejestracja: 8 mar 2009, o 09:27
Płeć: Kobieta
Podziękował: 41 razy

lista dwukierunkowa sprawdzenie i poprawa błędów

Post autor: flowers_evil »

Jeszcze raz dzięki !
ODPOWIEDZ