Usuwanie wybranego elementu z listy jednokierunkowej

0

Chcę napisać program, który najpierw stworzy listę jednokierunkową składającą się z figur (nazwa, boki, pole), a następnie usunie z niej drugą w kolejności figurę dla której liczba boków jest większa od poprzedzającej figury na liście. (Dla przykładu - > 2 9 4 1 5 8 14 - usuwam 5)

Listę już stworzyłem. Macie może jakieś sugestie co do usunięcia tego jednego elementu?
Mój sposób crashuje konsole


#include <iostream>
#include <cstdlib>

using namespace std;

int const n = 4; // STAŁA

// STRUKTURA
struct figura
{
    string nazwa; // nazwa figury
    int boki; // liczba boków
    int pp; // pole powierzchni
    figura *next; // adres do nastepnej figury
};


// FUNKCJA - DRUKOWANIE LISTY
void drukowanie(figura *adres)
{
    //while(adres!=NULL)
    do{
        cout << (*adres).nazwa << " " << (*adres).boki << " " << (*adres).pp << " " << endl;
        adres = adres->next;
    } while(adres!=NULL);
};



int main()
{
    // ZMIENNE
    figura *glowa, *aktualny, *poprzedni, *tmp;
    int liczba, pole, ile, ilew;
    string fig;

    cout << "LISTA JEDNOKIERUNKOWA" << endl << endl;

    // TWORZENIE LISTY
    aktualny = NULL;
    poprzedni = NULL;
    glowa = poprzedni;


    for(int i=0; i<n; i++){
        poprzedni = aktualny;

        cout << "Tworzymy figure nr " << i+1 << endl;
        cout << "Podaj figure do wstawienia: ";
        cin >> fig;
        cout << "Podaj liczbe bokow: ";
        cin >> liczba;
        cout << "Podaj pole powierzchnii: ";
        cin >> pole;

        aktualny = new figura;
        aktualny->nazwa = fig;
        aktualny->boki = liczba;
        aktualny->pp = pole;
        aktualny->next = NULL;

        if(poprzedni != NULL)
            poprzedni->next = aktualny;
        else
            glowa = aktualny;
    };

    cout << "\nDrukowanie listy: \n";
    drukowanie(glowa);


    // USUWANIE ELEMENTU Z LISTY

    aktualny = glowa;
    poprzedni = NULL;
    ile = 0;

    while (aktualny != NULL)
    {
        if (aktualny->boki > poprzedni->boki )
            ile++;
        if (ile == 2){
            poprzedni->next = aktualny->next;
            tmp = aktualny;
            aktualny = aktualny->next;
            delete tmp;
        } else {
            poprzedni->next = aktualny;
            aktualny = aktualny->next;
        };
    };

    cout << ile;
    cout << "\nDrukowanie listy: \n";
    drukowanie(glowa);

    return 0;
}


0

Masz problem ze znalezieniem tego elementu, czy z usunięciem?

0

Głównie ze znalezieniem elementu. Może wykorzystać funkcję usuwającą element? Tylko tutaj mam problem - po wypisaniu listy (pierwszej) konsola znowu się crashuje.


// USUWANIE PIERWSZEGO ELEMENTU
void usuwanie_pierwszego(figura *&head)
{
    figura *p = head; // zapamietujemy poczatek
    if (p)
    {
        head = p->next;
        delete p;
    }
}

// USUWANIE WYBRANEGO ELEMENTU
void usuwanie(figura *&head, figura *e)
{
    figura *p;
    if (head == e)
        usuwanie_pierwszego(head);
    else
    {
        p = head;
        while(p->next != e)
            p = p->next;
        p->next = e->next;
        delete e;
    }
}

0

Tak możesz - możesz do niej przekazać wskaźnik na odpowiedni element. W szukaniu potrzeby Ci będzie licznik dzięki któremu będziesz wiedział które wystąpienie pożądanego stanu masz, możesz to zrobić rekurencyjnie. Moim zdaniem masz błąd w liście - może nie do końca wiesz jak ona ma wyglądać, Stwórz sobie coś takiego:

0

Nie widzę schematu, który -jak się domyślam- chciałeś dodać.

Dodałem taki fragment odpowiadający za szukanie odpowiedniego elementu i nadal crashuje :(


    while(aktualny != NULL)
    {
        if(aktualny < (aktualny->next))
            i++;
        if(i == 2)
            usuwanie(glowa, wybrany);
            break;
        aktualny = aktualny->next;
    }


0

Faktycznie umknął mi, robiłeś już jakąś listę albo znasz trochę programowanie obiektowe?

0

Ogólnie na co dzień zajmuje się javą, więc obiektowe nie jest mi obce :)

0

O no to dobrze, ja bym przerobił to na coś takiego:

class figura{
public:
//Potrzebne Ci pola metody
}

class wezel{
public:
figura* nastpeny;
//potrzebne metody
}

Zamiast zwykłych wskaźników użyłbym cpp std::unique_ptr<figura> nastepny; zwolni Cię to z pamiętania o usuwaniu wskaźników. Starałbym się robić jak najwięcej rekurencyjnie jeszcze, choć pętle też są dobrą opcją - kto co woli.

0

Mogłem napisac wcześniej, jednak zapomniałem - treść zadania wymaga zrezygnowania z rekurencji.

Ogólnie poniższy kod już nie crashuje. Ostatnim problemem jest usuwanie właściwego elementu. Podejrzewam, że źle zdefiniowałem wybrany element i wskaźniki wskazują na inny.


#include <iostream>
#include <cstdlib>

using namespace std;

int const n = 4; // STAŁA

// STRUKTURA
struct figura
{
    string nazwa; // nazwa figury
    int boki; // liczba boków
    int pp; // pole powierzchni
    figura *next; // adres do nastepnej figury
};

// USUWANIE PIERWSZEGO ELEMENTU
void usuwanie_pierwszego(figura *&head)
{
    figura *p = head; // zapamietujemy poczatek
    if (p)
    {
        head = p->next;
        delete p;
    }
}

// USUWANIE WYBRANEGO ELEMENTU
void usuwanie(figura *&head, figura *e)
{
    figura *p;
    if (head == e)
        usuwanie_pierwszego(head);
    else
    {
        p = head;
        while(p->next != e)
            p = p->next;
        p->next = e->next;
        delete e;
    }
}

// FUNKCJA - DRUKOWANIE LISTY
void drukowanie(figura *adres)
{
    //while(adres!=NULL)
    do{
        cout << (*adres).nazwa << " " << (*adres).boki << " " << (*adres).pp << " " << endl;
        adres = adres->next;
    } while(adres!=NULL);
};



int main()
{
    // ZMIENNE
    figura *glowa, *aktualny, *poprzedni, *wybrany;
    int liczba, pole;
    int i = 0;
    string fig;

    cout << "LISTA JEDNOKIERUNKOWA" << endl << endl;

    // TWORZENIE LISTY
    aktualny = NULL;
    poprzedni = NULL;
    glowa = poprzedni;


    for(int i=0; i<n; i++){
        poprzedni = aktualny;

        cout << "Tworzymy figure nr " << i+1 << endl;
        cout << "Podaj figure do wstawienia: ";
        cin >> fig;
        cout << "Podaj liczbe bokow: ";
        cin >> liczba;
        cout << "Podaj pole powierzchnii: ";
        cin >> pole;

        aktualny = new figura;
        aktualny->nazwa = fig;
        aktualny->boki = liczba;
        aktualny->pp = pole;
        aktualny->next = NULL;

        if(poprzedni != NULL)
            poprzedni->next = aktualny;
        else
            glowa = aktualny;
    };

    cout << "\nDrukowanie listy: \n";
    drukowanie(glowa);


    // USUWANIE ELEMENTU Z LISTY

    poprzedni = glowa;
    aktualny = poprzedni->next;

    while(aktualny != NULL)
    {
        if(aktualny > poprzedni)
            i++;
        if(i == 3)
            usuwanie(glowa, aktualny);

        poprzedni = aktualny;
        aktualny = aktualny->next;
    }


    cout << "\nDrukowanie listy: \n";
    drukowanie(glowa);

    return 0;
}


2

Masz mały bałagan w tym kodzie i trzeba coś z tym zrobić.

Szkoda, że nie używasz dwóch wskaźników do reprezentowania listy (head i tail) – łatwiej było by operować na liście, no i pozwoliłoby to na szybsze działanie kodu. Dodanie elementu na koniec listy może mieć stałą złożoność.

Funkcja usuwanie_pierwszego nie jest Ci w ogóle potrzebna – niepotrzebnie komplikujesz sobie kod. Jeśli wykluczyć pierwszy warunek z funkcji usuwanie, to pozostanie poniższa konstrukcja, która nadal zdolna będzie do usuwania dowolnego węzła:

void usuwanie(figura *&head, figura *e)
{
  figura *p = head;
    
  while(p->next != e)
    p = p->next;
      
  p->next = e->next;
  delete e;
}

Trzeba tylko dodać warunek na początku, porównujący head do e.

Problem jest taki, że do funkcji możesz przekazać zmienną z nullem, co nie jest w żaden sposób walidowane. Zresztą, jeśli podasz adres nieistniejącego w liście węzła, to pętla będzie iterować bez końca, aż wyjedzie poza listę i polecą błędy.

Dlatego też trzeba ten kod nieco zabezpieczyć – musisz dodatkowo sprawdzać czy p jest różne od null, a także czy p->next jest od niego różne. Możesz to sprawdzić od razu (na początku). Jeśli head lub e zawiera null to wyjdź z funkcji.

Mała sugestia – piszesz ten kod w dwóch językach. Część identyfikatorów masz po polsku, a część po angielsku (plus składnia języka jest po angielsku). Wymień wszystkie nazwy w programie na angielskie – w razie problemów odpal tłumacz Google i sobie pomóż. I nie używaj jednoliterowych identyfikatorów, bo są nieczytelne i niczego nie mówią o swoim przeznaczeniu. Zmień e na given, a z p zrób curr.

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