470 lines
15 KiB
C
470 lines
15 KiB
C
/* ******************************* c206.c *********************************** */
|
|
/* Předmět: Algoritmy (IAL) - FIT VUT v Brně */
|
|
/* Úkol: c206 - Dvousměrně vázaný lineární seznam */
|
|
/* Návrh a referenční implementace: Bohuslav Křena, říjen 2001 */
|
|
/* Vytvořil: Martin Tuček, říjen 2004 */
|
|
/* Upravil: Kamil Jeřábek, září 2020 */
|
|
/* Daniel Dolejška, září 2021 */
|
|
/* Daniel Dolejška, září 2022 */
|
|
/* ************************************************************************** */
|
|
/*
|
|
** Implementujte abstraktní datový typ dvousměrně vázaný lineární seznam.
|
|
** Užitečným obsahem prvku seznamu je hodnota typu int. Seznam bude jako datová
|
|
** abstrakce reprezentován proměnnou typu DLList (DL znamená Doubly-Linked
|
|
** a slouží pro odlišení jmen konstant, typů a funkcí od jmen u jednosměrně
|
|
** vázaného lineárního seznamu). Definici konstant a typů naleznete
|
|
** v hlavičkovém souboru c206.h.
|
|
**
|
|
** Vaším úkolem je implementovat následující operace, které spolu s výše
|
|
** uvedenou datovou částí abstrakce tvoří abstraktní datový typ obousměrně
|
|
** vázaný lineární seznam:
|
|
**
|
|
** DLL_Init ........... inicializace seznamu před prvním použitím,
|
|
** DLL_Dispose ........ zrušení všech prvků seznamu,
|
|
** DLL_InsertFirst .... vložení prvku na začátek seznamu,
|
|
** DLL_InsertLast ..... vložení prvku na konec seznamu,
|
|
** DLL_First .......... nastavení aktivity na první prvek,
|
|
** DLL_Last ........... nastavení aktivity na poslední prvek,
|
|
** DLL_GetFirst ....... vrací hodnotu prvního prvku,
|
|
** DLL_GetLast ........ vrací hodnotu posledního prvku,
|
|
** DLL_DeleteFirst .... zruší první prvek seznamu,
|
|
** DLL_DeleteLast ..... zruší poslední prvek seznamu,
|
|
** DLL_DeleteAfter .... ruší prvek za aktivním prvkem,
|
|
** DLL_DeleteBefore ... ruší prvek před aktivním prvkem,
|
|
** DLL_InsertAfter .... vloží nový prvek za aktivní prvek seznamu,
|
|
** DLL_InsertBefore ... vloží nový prvek před aktivní prvek seznamu,
|
|
** DLL_GetValue ....... vrací hodnotu aktivního prvku,
|
|
** DLL_SetValue ....... přepíše obsah aktivního prvku novou hodnotou,
|
|
** DLL_Previous ....... posune aktivitu na předchozí prvek seznamu,
|
|
** DLL_Next ........... posune aktivitu na další prvek seznamu,
|
|
** DLL_IsActive ....... zjišťuje aktivitu seznamu.
|
|
**
|
|
** Při implementaci jednotlivých funkcí nevolejte žádnou z funkcí
|
|
** implementovaných v rámci tohoto příkladu, není-li u funkce explicitně
|
|
** uvedeno něco jiného.
|
|
**
|
|
** Nemusíte ošetřovat situaci, kdy místo legálního ukazatele na seznam
|
|
** předá někdo jako parametr hodnotu NULL.
|
|
**
|
|
** Svou implementaci vhodně komentujte!
|
|
**
|
|
** Terminologická poznámka: Jazyk C nepoužívá pojem procedura.
|
|
** Proto zde používáme pojem funkce i pro operace, které by byly
|
|
** v algoritmickém jazyce Pascalovského typu implemenovány jako procedury
|
|
** (v jazyce C procedurám odpovídají funkce vracející typ void).
|
|
**
|
|
**/
|
|
|
|
#include "c206.h"
|
|
|
|
bool error_flag;
|
|
bool solved;
|
|
|
|
/**
|
|
* Vytiskne upozornění na to, že došlo k chybě.
|
|
* Tato funkce bude volána z některých dále implementovaných operací.
|
|
*/
|
|
void DLL_Error(void) {
|
|
printf("*ERROR* The program has performed an illegal operation.\n");
|
|
error_flag = true;
|
|
}
|
|
|
|
/**
|
|
* Provede inicializaci seznamu list před jeho prvním použitím (tzn. žádná
|
|
* z následujících funkcí nebude volána nad neinicializovaným seznamem).
|
|
* Tato inicializace se nikdy nebude provádět nad již inicializovaným seznamem,
|
|
* a proto tuto možnost neošetřujte.
|
|
* Vždy předpokládejte, že neinicializované proměnné mají nedefinovanou hodnotu.
|
|
*
|
|
* @param list Ukazatel na strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_Init( DLList *list ) {
|
|
list->firstElement = NULL;
|
|
list->activeElement = NULL;
|
|
list->lastElement = NULL;
|
|
list->currentLength = 0;
|
|
}
|
|
|
|
/**
|
|
* Zruší všechny prvky seznamu list a uvede seznam do stavu, v jakém se nacházel
|
|
* po inicializaci.
|
|
* Rušené prvky seznamu budou korektně uvolněny voláním operace free.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_Dispose( DLList *list ) {
|
|
DLLElementPtr current = list->firstElement;
|
|
while (current != NULL)
|
|
{
|
|
DLLElementPtr temp = current;
|
|
current = current->nextElement;
|
|
free(temp);
|
|
}
|
|
list->firstElement = NULL;
|
|
list->activeElement = NULL;
|
|
list->lastElement = NULL;
|
|
list->currentLength = 0;
|
|
}
|
|
|
|
/**
|
|
* Vloží nový prvek na začátek seznamu list.
|
|
* V případě, že není dostatek paměti pro nový prvek při operaci malloc,
|
|
* volá funkci DLL_Error().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param data Hodnota k vložení na začátek seznamu
|
|
*/
|
|
void DLL_InsertFirst( DLList *list, long data ) {
|
|
DLLElementPtr newElement = (DLLElementPtr)malloc(sizeof(struct DLLElement));
|
|
if (newElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
newElement->data = data;
|
|
newElement->previousElement = NULL;
|
|
newElement->nextElement = list->firstElement;
|
|
if (list->firstElement != NULL)
|
|
{
|
|
list->firstElement->previousElement = newElement;
|
|
}
|
|
else
|
|
{
|
|
list->lastElement = newElement;
|
|
}
|
|
list->firstElement = newElement;
|
|
list->currentLength++;
|
|
}
|
|
|
|
/**
|
|
* Vloží nový prvek na konec seznamu list (symetrická operace k DLL_InsertFirst).
|
|
* V případě, že není dostatek paměti pro nový prvek při operaci malloc,
|
|
* volá funkci DLL_Error().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param data Hodnota k vložení na konec seznamu
|
|
*/
|
|
void DLL_InsertLast( DLList *list, long data ) {
|
|
DLLElementPtr newElement = (DLLElementPtr)malloc(sizeof(struct DLLElement));
|
|
if (newElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
newElement->data = data;
|
|
newElement->nextElement = NULL;
|
|
newElement->previousElement = list->lastElement;
|
|
if (list->lastElement != NULL)
|
|
{
|
|
list->lastElement->nextElement = newElement;
|
|
}
|
|
else
|
|
{
|
|
list->firstElement = newElement;
|
|
}
|
|
list->lastElement = newElement;
|
|
list->currentLength++;
|
|
}
|
|
|
|
/**
|
|
* Nastaví první prvek seznamu list jako aktivní.
|
|
* Funkci implementujte jako jediný příkaz, aniž byste testovali,
|
|
* zda je seznam list prázdný.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_First( DLList *list ) {
|
|
list->activeElement = list->firstElement;
|
|
}
|
|
|
|
/**
|
|
* Nastaví poslední prvek seznamu list jako aktivní.
|
|
* Funkci implementujte jako jediný příkaz, aniž byste testovali,
|
|
* zda je seznam list prázdný.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_Last( DLList *list ) {
|
|
list->activeElement = list->lastElement;
|
|
}
|
|
|
|
/**
|
|
* Prostřednictvím parametru dataPtr vrátí hodnotu prvního prvku seznamu list.
|
|
* Pokud je seznam list prázdný, volá funkci DLL_Error().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param dataPtr Ukazatel na cílovou proměnnou
|
|
*/
|
|
void DLL_GetFirst( DLList *list, long *dataPtr ) {
|
|
if (list->firstElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
*dataPtr = list->firstElement->data;
|
|
}
|
|
|
|
/**
|
|
* Prostřednictvím parametru dataPtr vrátí hodnotu posledního prvku seznamu list.
|
|
* Pokud je seznam list prázdný, volá funkci DLL_Error().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param dataPtr Ukazatel na cílovou proměnnou
|
|
*/
|
|
void DLL_GetLast( DLList *list, long *dataPtr ) {
|
|
if (list->lastElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
*dataPtr = list->lastElement->data;
|
|
}
|
|
|
|
/**
|
|
* Zruší první prvek seznamu list.
|
|
* Pokud byl první prvek aktivní, aktivita se ztrácí.
|
|
* Pokud byl seznam list prázdný, nic se neděje.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_DeleteFirst( DLList *list ) {
|
|
if (list->firstElement != NULL)
|
|
{
|
|
DLLElementPtr temp = list->firstElement;
|
|
list->firstElement = temp->nextElement;
|
|
if (list->firstElement != NULL)
|
|
{
|
|
list->firstElement->previousElement = NULL;
|
|
}
|
|
else
|
|
{
|
|
list->lastElement = NULL;
|
|
}
|
|
if (list->activeElement == temp)
|
|
{
|
|
list->activeElement = NULL;
|
|
}
|
|
free(temp);
|
|
list->currentLength--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zruší poslední prvek seznamu list.
|
|
* Pokud byl poslední prvek aktivní, aktivita seznamu se ztrácí.
|
|
* Pokud byl seznam list prázdný, nic se neděje.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_DeleteLast( DLList *list ) {
|
|
if (list->lastElement != NULL)
|
|
{
|
|
DLLElementPtr temp = list->lastElement;
|
|
list->lastElement = temp->previousElement;
|
|
if (list->lastElement != NULL)
|
|
{
|
|
list->lastElement->nextElement = NULL;
|
|
}
|
|
else
|
|
{
|
|
list->firstElement = NULL;
|
|
}
|
|
if (list->activeElement == temp)
|
|
{
|
|
list->activeElement = NULL;
|
|
}
|
|
free(temp);
|
|
list->currentLength--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zruší prvek seznamu list za aktivním prvkem.
|
|
* Pokud je seznam list neaktivní nebo pokud je aktivní prvek
|
|
* posledním prvkem seznamu, nic se neděje.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_DeleteAfter( DLList *list ) {
|
|
if (list->activeElement != NULL && list->activeElement->nextElement != NULL)
|
|
{
|
|
DLLElementPtr temp = list->activeElement->nextElement;
|
|
list->activeElement->nextElement = temp->nextElement;
|
|
if (temp->nextElement != NULL)
|
|
{
|
|
temp->nextElement->previousElement = list->activeElement;
|
|
}
|
|
else
|
|
{
|
|
list->lastElement = list->activeElement;
|
|
}
|
|
free(temp);
|
|
list->currentLength--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zruší prvek před aktivním prvkem seznamu list .
|
|
* Pokud je seznam list neaktivní nebo pokud je aktivní prvek
|
|
* prvním prvkem seznamu, nic se neděje.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_DeleteBefore( DLList *list ) {
|
|
if (list->activeElement != NULL && list->activeElement->previousElement != NULL)
|
|
{
|
|
DLLElementPtr temp = list->activeElement->previousElement;
|
|
list->activeElement->previousElement = temp->previousElement;
|
|
if (temp->previousElement != NULL)
|
|
{
|
|
temp->previousElement->nextElement = list->activeElement;
|
|
}
|
|
else
|
|
{
|
|
list->firstElement = list->activeElement;
|
|
}
|
|
free(temp);
|
|
list->currentLength--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vloží prvek za aktivní prvek seznamu list.
|
|
* Pokud nebyl seznam list aktivní, nic se neděje.
|
|
* V případě, že není dostatek paměti pro nový prvek při operaci malloc,
|
|
* volá funkci DLL_Error().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param data Hodnota k vložení do seznamu za právě aktivní prvek
|
|
*/
|
|
void DLL_InsertAfter( DLList *list, long data ) {
|
|
if (list->activeElement != NULL)
|
|
{
|
|
DLLElementPtr newElement = (DLLElementPtr)malloc(sizeof(struct DLLElement));
|
|
if (newElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
newElement->data = data;
|
|
newElement->nextElement = list->activeElement->nextElement;
|
|
newElement->previousElement = list->activeElement;
|
|
list->activeElement->nextElement = newElement;
|
|
if (newElement->nextElement != NULL)
|
|
{
|
|
newElement->nextElement->previousElement = newElement;
|
|
}
|
|
else
|
|
{
|
|
list->lastElement = newElement;
|
|
}
|
|
list->currentLength++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vloží prvek před aktivní prvek seznamu list.
|
|
* Pokud nebyl seznam list aktivní, nic se neděje.
|
|
* V případě, že není dostatek paměti pro nový prvek při operaci malloc,
|
|
* volá funkci DLL_Error().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param data Hodnota k vložení do seznamu před právě aktivní prvek
|
|
*/
|
|
void DLL_InsertBefore( DLList *list, long data ) {
|
|
if (list->activeElement != NULL)
|
|
{
|
|
DLLElementPtr newElement = (DLLElementPtr)malloc(sizeof(struct DLLElement));
|
|
if (newElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
newElement->data = data;
|
|
newElement->nextElement = list->activeElement;
|
|
newElement->previousElement = list->activeElement->previousElement;
|
|
list->activeElement->previousElement = newElement;
|
|
if (newElement->previousElement != NULL)
|
|
{
|
|
newElement->previousElement->nextElement = newElement;
|
|
}
|
|
else
|
|
{
|
|
list->firstElement = newElement;
|
|
}
|
|
list->currentLength++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prostřednictvím parametru dataPtr vrátí hodnotu aktivního prvku seznamu list.
|
|
* Pokud seznam list není aktivní, volá funkci DLL_Error ().
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param dataPtr Ukazatel na cílovou proměnnou
|
|
*/
|
|
void DLL_GetValue( DLList *list, long *dataPtr ) {
|
|
if (list->activeElement == NULL)
|
|
{
|
|
DLL_Error();
|
|
return;
|
|
}
|
|
*dataPtr = list->activeElement->data;
|
|
}
|
|
|
|
/**
|
|
* Přepíše obsah aktivního prvku seznamu list.
|
|
* Pokud seznam list není aktivní, nedělá nic.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
* @param data Nová hodnota právě aktivního prvku
|
|
*/
|
|
void DLL_SetValue( DLList *list, long data ) {
|
|
if (list->activeElement != NULL)
|
|
{
|
|
list->activeElement->data = data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Posune aktivitu na následující prvek seznamu list.
|
|
* Není-li seznam aktivní, nedělá nic.
|
|
* Všimněte si, že při aktivitě na posledním prvku se seznam stane neaktivním.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_Next( DLList *list ) {
|
|
if (list->activeElement != NULL)
|
|
{
|
|
list->activeElement = list->activeElement->nextElement;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Posune aktivitu na předchozí prvek seznamu list.
|
|
* Není-li seznam aktivní, nedělá nic.
|
|
* Všimněte si, že při aktivitě na prvním prvku se seznam stane neaktivním.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*/
|
|
void DLL_Previous( DLList *list ) {
|
|
if (list->activeElement != NULL)
|
|
{
|
|
list->activeElement = list->activeElement->previousElement;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Je-li seznam list aktivní, vrací nenulovou hodnotu, jinak vrací 0.
|
|
* Funkci je vhodné implementovat jedním příkazem return.
|
|
*
|
|
* @param list Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
|
|
*
|
|
* @returns Nenulovou hodnotu v případě aktivity prvku seznamu, jinak nulu
|
|
*/
|
|
bool DLL_IsActive( DLList *list ) {
|
|
return list->activeElement != NULL;
|
|
}
|
|
|
|
/* Konec c206.c */
|