C++

Struktury danych: stos

Stack-Overflow-Logo

Cześć, oddaję w wasze ręce trzeci z kolei wpis na temat abstrakcyjnych struktur danych. Dzisiaj na warsztat wezmę stos i jego implementację w C. 

W celu lepszego zrozumienia kilku technicznych aspektów jak np. podwójny wskaźnik **, polecam zapoznać się z poprzednimi postami z serii: 

 

kurs matura z informatyki
Już niebawem rusza kurs przygotowujący do matury z informatyki.
Nie daj się zaskoczyć na maturze - zapisz się do listy mailingowej już teraz!

 Stos – koncepcja 

Stos podobnie do list jest strukturą liniową, tzn. taką, w której przy poruszaniu się w danym kierunku mamy tylko jedną możliwość. Różnią się za to sposobem dostępu do danych znajdujących się w strukturze. Listy umożliwiały operacje na dowolnych elementach, wchodzących w ich skład. Mogliśmy dopisywać elementy na początku listy, na końcu lub w środku. W ten sam sposób mogliśmy usuwać dane z listy. 

W stosie zastosowano inne rozwiązanie. Mianowicie operować można jedynie pierwszym elementem na naszym stosie, tzw. wierzchołkiem (ang. top). Aby dostać się do danego elementu stosu należy przejść przez wierzchołek właśnie. 

stos w c

 

Dodając nowy element na stos staje się on nowym wierzchołkiem. Natomiast usuwając element ze stosu, usuwamy właśnie dotychczasowy top naszego stosu, a jego rolę przejmuje element będący w danym momencie pod nim.  

Zobrazujmy to sobie za pomocą popularnych pankejków. Dostajemy taki stos i decydujemy, że będziemy go spożywać jak ludzie. W tym celu zawsze będziemy operować pierwszym z góry naleśnikiem - wierzchołkiem naszego stosu. Zjadając wierzchołek wywołujemy pop() na stosie – usuwamy element. Natomiast jeżeli zdecydujemy się na dodatkowego pankejka to położymy go na szczycie naleśnikowego stosu tym samym stanie się on nowym wierzchołkiem. Taka dokładka odpowiada funkcji push() - dodaniu elementu do stosu.  

Ze względu na rozwiązanie funkcji dodawaniu i usuwania elementów, stos nazywa się często kolejką LIFO (ang. Last in first out), gdyż jak sama nazwa mówi: element dodany jako ostatni usuwany jest w pierwszej kolejności. 

Ważne właściwości stosu

Z pojęciem stosu wiążą się dwa ważne terminy. Chodzi o popularny termin stack overflow oraz nieco mniej znany stack underflow. Pierwszy odnosi się do sytuacji, gdy próbujemy do stosu o określonej pojemności dodać jeszcze jeden element. Dochodzi wówczas do błędu przepełnienia stosu. Od tego pojęcia nazwę wzięło najsławniejsze z programistycznych miejsc w sieci – strona stackoverflow.com. 

Drugi termin opisuje sytuację, w której chcemy usunąć element z już pustego stosu. 

Zastosowanie stosu 

Stos jest jedną z najpowszechniej używanych struktur. Wykorzystywany jest na przykład przy: 

    • Obliczaniu wartości wyrażeń w notacjach prefiksowej (polskiej) i postfiksowej (polskiej odwrotnej), 

 

    • Zarządzaniu pamięcią programu – korzystamy ze stosu za każdym razem, gdy używamy tablic i wskaźników, 

 

  • Konwersji z kodu naturalnego binarnego na system dziesiętny. 

 

Stos – implementacja w C 

Stos w języku C można w łatwy sposób zaimplementować za pomocą listy powiązanej tak jak to robiliśmy w przypadku struktur list. Stos korzysta z następujących funkcji: 

    • push() - dodanie elementu na szczyt stosu, 

 

    • pop() - zdjęcie elementu ze stosu, 

 

    • empty() - sprawdzenie, czy stos jest pusty, 

 

  • size() - zliczenie elementów na stosie.

Stos w C nie różni się specjalnie od implementacji innych struktur liniowych. Jednakże chcąc uzyskać efekt stack overflow zdefiniujemy maksymalną liczbę elementów stosu. Upodobnimy wtedy nasz stos do tego w pamięci, która nie jest przecież nieskończona.

Dodatkowo uważam, że stos jest o wiele łatwiejszy do zaimplementowania, gdyż musimy rozpatrzeć tylko jeden przypadek przy dodawaniu i usuwaniu elementów. Użytkownik może dodać element tylko na wierzchołek stosu i basta.

//£ukasz Kosiñski - implementacja stosu
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>

int max_size;

typedef struct StackElement { int data; struct stackElement * next; } StackElement_type;

int push(StackElement_type **top, int number); int pop(StackElement_type **top); void empty(StackElement_type *top); void show(StackElement_type *top); int size(StackElement_type *top);

int main() {

printf("Wprowadz maksymalny rozmiar stosu: ");
scanf("%i", &amp;max_size); Sleep(1000);

if(max_size&lt;=0) return 1;
else {
    StackElement_type *top;
    top=NULL;

    int option=-1;
    int number=-1;
    while(option!=0) {

        system("cls");
        printf("Aktualny stan stosu: ");
        show(top);

        printf("\n\nDrogi uzytkowniku! Co chcesz zrobic?\n");
        printf("1. Polozyc element na stosie.\n");
        printf("2. Zdjac element ze stosu.\n");
        printf("3. Sprawdzic czy stos jest pusty.\n");
        printf("4. Policzyc elementy na stosie.\n");
        printf("0. Zakonczyc program.\n");

        scanf("%i", &amp;option);

        switch (option) {
        case 0:
            return 0;
            break;

        case 1:
            printf("Wpisz liczbe jaka chcesz dodac: ");
            scanf("%i", &amp;number);
            push(&amp;top, number);
            break;
        case 2:
            pop(&amp;top);
            break;
        case 3:
            empty(top); Sleep(2000);
            break;
        case 4:
            printf("Liczba elementow na stosie: %i", size(top)); Sleep(2000);
            break;
        default:
            printf("Podaj wlasciwa opcje."); Sleep(2000);
            break;
        }
    }
}

return 0;

}

int push(StackElement_type **top, int number) {

if(size(*top)==max_size) {
    printf("\nSTACK OVERFLOW!! Nie mozna dodac elementu do stosu."); Sleep(2000);
    return 1;
}

if(*top==NULL) {
    *top=(StackElement_type *) malloc (sizeof(StackElement_type));
    (*top)-&gt;data=number;
    (*top)-&gt;next=NULL;
}else {
    StackElement_type *newElement;
    newElement=(StackElement_type *)malloc(sizeof(StackElement_type));
    newElement-&gt;data=number;
    newElement-&gt;next=*top;
    *top=newElement;
}

}

int pop(StackElement_type **top) {

if (*top==NULL) {
  printf("\nSTACK UNDERFLOW!! Nie ma czego zdjac ze stosu."); Sleep(2000);
}else {
 StackElement_type * tmp=NULL;
 tmp=(*top)-&gt;next;
 free(*top);
 *top=tmp;
}

}

void empty(StackElement_type *top) { (top==NULL) ? printf(“Stack is empty :O”) : printf(“Stack is not empty :]”); }

void show(StackElement_type *top) {

printf("\n");
if(top==NULL) printf("List is empty");
else {
    StackElement_type *current=top;
    do {
        printf("%i", current-&gt;data);
        printf("\n");
        current = current-&gt;next;
     }while (current != NULL);

}

}

int size(StackElement_type *top) {

int counter=0;
StackElement_type *current=top;

while(current!=NULL)
{
    counter++;
    current=current-&gt;next;
}

return counter;

}

 

 

Jeżeli chcesz poznać inne struktury danych, zachęcam cię do przeczytania tej książki: