Dodanie na początek wymaga zatem wykonania następujących operacji (bez uwzględnienia sytuacji kiedy bazowa lista jest pusta):
i
na nowym elemencie z punktu 1next
nowego elementu z punktu 1 jest ustawiane jako adres pierwszej elementu początkowej listyKod w main
z kolejnym dodawaniem elementów
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
int main()
{
struct element * lista = NULL; //tworzymy "pustą listę"
//dodajemy pierwszy element na listę (tu pojęcie koniec czy początek nie ma większego sensu)
struct element * wsk = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk->i=-4;
wsk->next=NULL; //bo nic więcej nie ma na liście
lista=wsk; // ustawiamy wskaznik początku listy
// dodajemy nowy element na początek
struct element * wsk2 = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk2->i=7;
wsk2->next=lista; // następnik nowego elementu to bieżacy wskaznik listy
lista=wsk2; // ustawiamy wskaznik nowego elementy jako początek listy
// dodajemy jeszcze jeden nowy element na początek
struct element * wsk3 = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk3->i=5;
wsk3->next=lista; // następnik nowego elementu to bieżacy wskaznik listy
lista=wsk3; // ustawiamy wskaznik nowego elementy jako początek listy
//tu już mamy listę jak na rysunku - pierwszy eleemnt yo 5, drugi to 7, trzeci to i 4
// teraz dodajemy 11 na początek
struct element * wsk4 = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk4->i=11;
wsk4->next=lista; // następnik nowego elementu to bieżacy wskaznik listy
lista=wsk4; // ustawiamy wskaznik nowego elementy jako początek listy
return 0;
}
Prostsze rozwiązanie gdy lista jest już stworzona:
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
int main()
{
struct element * lista = malloc(sizeof (struct element));
lista->i = 5;
lista->next = malloc(sizeof (struct element));
lista->next->i = 7;
lista->next->next = malloc(sizeof (struct element));
lista->next->next->i = -4;
lista->next->next->next = NULL;
struct element * temp = lista;
while (temp != NULL)
{
printf("%d\n", temp->i);
temp = temp->next;
}
printf("dodanie\n");
struct element * wsk = malloc(sizeof (struct element));
wsk->i = 11;
wsk->next = lista;
lista = wsk;
temp = lista;
while (temp != NULL)
{
printf("%d\n", temp->i);
temp = temp->next;
}
return 0;
}
Kod w funkcji
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
struct element* dodaj(struct element*Lista, int a)
{
struct element * wsk = malloc(sizeof(struct element));
wsk->i=a;
wsk->next=Lista;
return wsk;
}
struct element * utworz()
{
return NULL;
}
int main()
{
struct element* l1 = utworz();
l1 = dodaj(l1,2);
l1 = dodaj(l1,4);
l1 = dodaj(l1,7);
l1 = dodaj(l1,-2);
struct element * wsk = l1;
while(wsk!=NULL)
{
printf("%d\n",wsk->i);
wsk=wsk->next;
}
return 0;
}
Kod w funkcji - argument podwójny wskaźnik
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
void dodaj(struct element**Lista, int a)
{
struct element * wsk = malloc(sizeof(struct element));
wsk->i=a;
wsk->next=*Lista;
*Lista= wsk;
}
struct element * utworz()
{
return NULL;
}
int main()
{
struct element* l1 = utworz();
dodaj(&l1,2);
dodaj(&l1,4);
dodaj(&l1,7);
dodaj(&l1,-2);
struct element * wsk = l1;
while(wsk!=NULL)
{
printf("%d\n",wsk->i);
wsk=wsk->next;
}
return 0;
}
Dodanie na początek (przy założeniu że bieżąca lista nie jest pusta) polega na wykonaniu operacji:
i
next
ustawiamy jako to co znajduje się w „głowie” w polu next
next
w „głowie” ustawiając je jako wskaźnik na nowy element z punktu 1Ważne: nie możemy zmienić kolejności punktu 3 i 4 bo zmieni to sens.
Kod w main
:
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
int main()
{
//tworzymy głowę
struct element * lista = malloc(sizeof(struct element));
//pola i glowy nie ustawiamy
lista->next= NULL; //następnik glowy jest na NULL, mamy zatem w tym miejscu pustą listę z głową
//dodajemy pierwszy element na listę - w liscie z głową musimy go postawić po głowie
struct element * wsk = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk->i=-4;
wsk->next=lista->next; //tutaj teoretycznie można ustawić na NULL, ale zgodnie z konwencją trzeba ustawić pole next głowy
lista->next=wsk; // ustawiamy nastepnik glowy jako wskaznik na nowy element
// dodajemy nowy element na początek
struct element * wsk2 = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk2->i=7;
wsk2->next=lista->next; // następnik nowego elementu to nastepnik "głowy"
lista->next=wsk2; // ustawiamy następnik głowy jako wskaźnik na nowy element
// dodajemy jeszcze jeden nowy element na początek
struct element * wsk3 = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk3->i=5;
wsk3->next=lista->next; // następnik nowego elementu to nastepnik "głowy"
lista->next=wsk3; // ustawiamy następnik głowy jako wskaźnik na nowy element
//tu już mamy listę jak na rysunku - "głowa", pierwszy eleemnt to 5, drugi to 7, trzeci to i 4
// teraz dodajemy 11 na początek
struct element * wsk4 = malloc(sizeof(struct element)); // rezerwujemy miejsce w pamięci na jeden z elementów
wsk4->i=11;
wsk4->next=lista->next; // następnik nowego elementu to nastepnik "głowy"
lista->next=wsk4; // ustawiamy następnik głowy jako wskaźnik na nowy element
return 0;
}
Prostsze rozwiązanie gdy lista jest już stworzona:
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
int main()
{
struct element * lista = malloc(sizeof(struct element));
lista->next = malloc(sizeof(struct element));
lista->next->i = 5;
lista->next->next = malloc(sizeof(struct element));
lista->next->next->i = 7;
lista->next->next->next = malloc(sizeof(struct element));
lista->next->next->next->i = -4;
lista->next->next->next->next = NULL;
struct element * temp = lista->next;
while(temp != NULL)
{
printf("%d\n", temp->i);
temp = temp->next;
}
printf("Dodanie\n");
struct element * wsk = malloc(sizeof(struct element));
wsk->i = 11;
wsk->next = lista->next;
lista->next = wsk;
temp = lista->next;
while(temp != NULL)
{
printf("%d\n", temp->i);
temp = temp->next;
}
return 0;
}
Kod przez funkcje:
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
struct element * utworz()
{
struct element * wskaznik = malloc(sizeof(struct element));
wskaznik->next=NULL;
return wskaznik;
}
void dodaj(struct element*Lista, int a)
{
struct element * wsk = malloc(sizeof(struct element));
wsk->i=a;
wsk->next=Lista->next;
Lista->next=wsk;
}
int main()
{
struct element* l1 = utworz();
dodaj(l1,2);
dodaj(l1,4);
dodaj(l1,-8);
dodaj(l1,-22);
struct element * wsk = l1;
while(wsk->next!=NULL)
{
wsk=wsk->next;
printf("%d\n",wsk->i);
}
return 0;
}
Co zyskujemy? To zależy od kontekstu w którym używamy i jakie operacje mamy. W wielu sytuacjach zmiana wskaźnika „początku” przy liście bez głowy jest operacją, która zwiększa tzw. złożoność obliczeniową.
Na dziś można sobie myśleć, że jest to „szybsze” (to też zależy od języka programowania).
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
struct element * dodaj_na_koniec(struct element * Lista, int a)
{
struct element * wsk;
wsk = Lista;
if (Lista == NULL)
{
Lista = malloc(sizeof(struct element));
Lista->i = a;
Lista->next = NULL;
}
else
{
while (wsk->next != NULL)
{
wsk = wsk->next;
}
wsk->next = malloc(sizeof(struct element));
wsk->next->i = a;
wsk->next->next = NULL;
}
return Lista;
}
void wyswietl(struct element * Lista)
{
struct element * wsk;
wsk = Lista;
if (wsk == NULL)
{
printf("Lista jest pusta\n");
return;
}
while (wsk != NULL)
{
printf("%d\n", wsk->i);
wsk = wsk->next;
}
printf("---\n");
}
int main()
{
// przypadek pusty
struct element * Lista = NULL;
wyswietl(Lista);
Lista = dodaj_na_koniec(Lista, 5);
wyswietl(Lista);
// przypadek listy dwuelementowej
struct element * lista2 = malloc(sizeof(struct element));
lista2->i = 12;
lista2->next = malloc(sizeof(struct element));
lista2->next->i = -8;
lista2->next->next = NULL;
wyswietl(lista2);
lista2 = dodaj_na_koniec(lista2, 3);
wyswietl(lista2);
return 0;
}
Podwójny wskaźnik
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
void dodaj_na_koniec(struct element ** Lista, int a)
{
struct element * wsk;
wsk = *Lista;
if (wsk == NULL)
{
wsk = malloc(sizeof(struct element));
wsk->i = a;
wsk->next = NULL;
*Lista = wsk;
return;
}
while (wsk->next != NULL)
{
wsk = wsk->next;
}
wsk->next = malloc(sizeof(struct element));
wsk->next->i = a;
wsk->next->next = NULL;
}
void wyswietl(struct element * Lista)
{
struct element * wsk;
wsk = Lista;
if (wsk == NULL)
{
printf("Lista jest pusta\n");
return;
}
while (wsk != NULL)
{
printf("%d\n", wsk->i);
wsk = wsk->next;
}
printf("---\n");
}
int main()
{
// przypadek pusty
struct element * Lista = NULL;
wyswietl(Lista);
dodaj_na_koniec(&Lista, 5);
wyswietl(Lista);
// przypadek listy dwuelementowej
struct element * lista2 = malloc(sizeof(struct element));
lista2->i = 12;
lista2->next = malloc(sizeof(struct element));
lista2->next->i = -8;
lista2->next->next = NULL;
wyswietl(lista2);
dodaj_na_koniec(&lista2, 3);
wyswietl(lista2);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element * next;
};
void dodaj_na_koniec(struct element * Lista, int a)
{
struct element * wsk;
wsk = Lista;
while (wsk->next != NULL)
{
wsk = wsk->next;
}
wsk->next = malloc(sizeof(struct element));
wsk->next->i = a;
wsk->next->next = NULL;
}
void wyswietl(struct element * Lista)
{
struct element * wsk;
wsk = Lista->next;
if (wsk == NULL)
{
printf("Lista jest pusta\n");
return;
}
while (wsk != NULL)
{
printf("%d\n", wsk->i);
wsk = wsk->next;
}
printf("---\n");
}
int main()
{
// przypadek pusty
struct element * Lista1 = malloc(sizeof(struct element));
Lista1->next = NULL;
wyswietl(Lista1);
dodaj_na_koniec(Lista1, 5);
wyswietl(Lista1);
// przypadek dwuelementowy
struct element * Lista2 = malloc(sizeof(struct element));
Lista2->next = malloc(sizeof(struct element));
Lista2->next->i = 12;
Lista2->next->next = malloc(sizeof(struct element));
Lista2->next->next->i = -8;
Lista2->next->next->next = NULL;
wyswietl(Lista2);
dodaj_na_koniec(Lista2, 3);
wyswietl(Lista2);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element *next;
};
void wyswietlListeBezGlowy(struct element * lista)
{
if (lista == NULL)
{
printf("Lista jest pusta\n---\n");
return;
}
struct element * wsk = lista;
while(wsk != NULL)
{
printf("%d\n", wsk->i);
wsk = wsk->next;
}
printf("---\n");
}
struct element * usun(struct element * lista, int a)
{
if (lista == NULL)
return NULL;
if(lista->i == a)
{
struct element * wsk = lista->next;
free(lista);
return wsk;
}
struct element * wsk= lista;
while( (wsk->next!=NULL) && (wsk->next->i !=a) )
{
wsk=wsk->next;
}
if(wsk->next!=NULL)
{
struct element * wsk2 =wsk;
wsk2=wsk2->next;
wsk->next=wsk->next->next;
free(wsk2);
}
return lista;
}
int main()
{
struct element * lista = malloc(sizeof(struct element));
lista->i = 6;
lista->next = malloc(sizeof(struct element));
lista->next->i = 2;
lista->next->next = NULL;
wyswietlListeBezGlowy(lista);
lista=usun(lista, 2);
wyswietlListeBezGlowy(lista);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct element
{
int i;
struct element *next;
};
void wyswietlListeZGlowa(struct element * lista)
{
if (lista->next == NULL)
{
printf("Lista jest pusta\n---\n");
return;
}
struct element * wsk = lista->next;
while(wsk != NULL)
{
printf("%d\n", wsk->i);
wsk = wsk->next;
}
printf("---\n");
}
void usun(struct element * lista, int a)
{
if (lista->next == NULL)
return;
struct element * wsk= lista;
while( (wsk->next!=NULL) && (wsk->next->i !=a) )
{
wsk=wsk->next;
}
if(wsk->next!=NULL)
{
struct element * wsk2 =wsk;
wsk2=wsk2->next;
wsk->next=wsk->next->next;
free(wsk2);
}
}
int main()
{
struct element * lista = malloc(sizeof(struct element));
lista->next = malloc(sizeof(struct element));
lista->next->i = 6;
lista->next->next = malloc(sizeof(struct element));
lista->next->next->i = 2;
lista->next->next->next = NULL;
wyswietlListeZGlowa(lista);
usun(lista, 6);
wyswietlListeZGlowa(lista);
return 0;
}
Struktura danych (ang. data structure) – sposób przechowywania danych w pamięci komputera. Na strukturach danych operują algorytmy.
Podczas implementacji programu programista często staje przed wyborem między różnymi strukturami danych, aby uzyskać pożądany efekt. Odpowiedni wybór może zmniejszyć złożoność obliczeniową, ale z drugiej strony trudność implementacji danej struktury może stanowić istotną przeszkodę.
Przykładowe struktury danych to:
Abstrakcyjny typ danych (ang. abstract data type, ADT) – tworzenie i opisywanie w formalny sposób typów danych tak, że opisywane są jedynie własności danych i operacji wykonywanych na nich (a nie przez reprezentację danych i implementację operacji).
Specyfikacja ADT powinna:
być jednoznaczna i dokładna;
zawierać wszystkie przypadki warte rozważenia;
nie zawierać niepotrzebnych informacji.
podając specyfikację ADT (dowolnego typu), powinniśmy uwzględnić:
Własności typu:
Dostępne działania:
Nazwa typu: Zbiór
Właściwości typu:
Dostępne działania:
Opcjonalnie:
Nazwa typu: Prosta lista
Własności typu: Potrafi przechować ciąg pozycji.
Dostępne działania:
Nazwa typu: Kolejka
Własności typu: Potrafi przechować uporządkowany ciąg pozycji.
Dostępne działania:
Własności typu:
Dostępne działania:
Krotka (ang. tuple) – struktura danych będąca odzwierciedleniem matematycznej n-ki, tj. uporządkowanego ciągu wartości. Krotki przechowują stałe wartości o różnych typach danych – nie można zmodyfikować żadnego elementu, odczyt natomiast wymaga podania indeksu liczbowego żądanego elementu.
Multizbiór (także wielozbiór, ang. multiset) – uogólnienie pojęcia zbioru, w którym w odróżnieniu od klasycznych zbiorów jeden element może występować wiele razy. Nie jest jednak dana żadna ich kolejność i tym multizbiór różni się od krotki.
Zbiory \(\{1,2,3\}, \{3,2,1\}\) i \(\{1,2,2,3\}\) są identyczne.
Multizbiory \(\{1,2,3\},\{1, 2, 3\}\) i \(\{3,2,1\}\) są identyczne, \(\{1,2,2,3\}\) jest jednak inny.
Kolejka priorytetowa - https://pl.wikipedia.org/wiki/Kolejka_priorytetowa
Kopiec - https://pl.wikipedia.org/wiki/Kopiec_(informatyka)
Rekord - https://pl.wikipedia.org/wiki/Rekord_(informatyka)
Tablica mieszająca - https://pl.wikipedia.org/wiki/Tablica_mieszaj%C4%85ca
Tablica asocjacyjna - https://pl.wikipedia.org/wiki/Tablica_asocjacyjna
https://www.geeksforgeeks.org/data-structures/linked-list/
Lista jednokierunkowa
Warianty - z głową (z wartownikiem) i bez.
Lista dwukierunkowa
Warianty: * z głową * z ogonem * mieszane
Lista cykliczna