![]() |
Índex |
Una pila és una estructura de dades (Objecte en la terminologia de OOP) que es composa d'elements del mateix tipus i podem dir que funciona de la següent manera:
Tipus de dades
En primer lloc definim un nou tipus de dades que anomenarem PilaEnters. |
typedef struct PilaEnters
{ int PunterACim; int Element[MAX]; }; |
La funció InicialitzaPila.
Segons veiem en la figura si la pila no està buida, el PunterACim està apuntant a l'últim element de la pila. Prendrem el conveni de que la pila està buida se el PunterACim té el valor -1, es adir que no apunta a cap element del vector. |
void InicialitzaPila(PilaEnters * Pila)
{ Pila->PunterACim=-1; } |
La funció EsBuida
Retorna CERT si la pila està buida (PunterACim és -1) i FALS si la pila conté elements (PunterACim es diferent de -1) |
Boolean EsBuida(PilaEnters * Pila)
{ return (Pila->PunterACim) == -1; } |
La funció InsereixElement
Aquesta funció comprova si el vector està ple (PunterACim<MAX) i en cas que hi hagi elements buits incrementa en ú el PunterACim i posa en el nou Cim la nova dada. Aquesta funció podria tornar CERT en cas que hagués tingut èxit en la inserció i FALS en cas contrari.
|
void InsereixElement(int Num, PilaEnters * Pila)
{ if (Pila->PunterACim+1<MAX) { Pila->PunterACim++; Pila->Element[Pila->PunterACim]=Num; } else printf("La pila està plena\n"); } |
La funció ExtreuElement
Primer comprova si la pila està buida i en cas que no l'estigui decrementa el PunterACim i ens torna la dada del cim anterior. Si està buida ens torna NULL. |
int ExtreuElement(PilaEnters *Pila)
{ if (! EsBuida(Pila)) { Pila->PunterACim--; return Pila->Element[Pila->PunterACim + 1]; } else return NULL; } |
Queda per escriure la funció main que fa ús del tipus de dades PilaEnters i de les seves funcions. | #include <stdio.h>
#include <conio.h> #define MAX 10 #define CERT 1
typedef int Boolean; void InicialitzaPila(PilaEnters * Pila);
main()
|
Aquest mètode per a la resolució del problema
Pila
és molt més natural que l'anterior del vector.
Ara són dos el tipus de dades que es defineixen: Un és l'element de la pila i l'altre és la pila mateixa. Element de pila és una estructura que es composa de dos camps, el camp de dades i el camp de punter a element de la pila per poder enllaçar aquest element amb el següent element de la pila. Fem notar que el camp de dades pot ser tan simple com un nombre enter o tan complex com una estructura de dades. El tipus de dades Pila és un punter a un element de la pila que és l'element que està en el cim de la pila. |
![]() |
Tipus de dades
Es defineixen els dos tipus mencionats abans, ElementPilaEnters i PilaEnters. |
typedef struct ElementPilaEnters
{ int Dada; ElementPilaEnters * PunterASeguent; }; typedef struct PilaEnters
|
La funció InicialitzaPila
En aquesta pila iniciar la pila és fer que el cim de la pila sigui NULL. Es a dir que farem que el punter a cim apunti a NULL. |
void InicialitzaPila(PilaEnters * Pila)
{ Pila->PunterACim=NULL; } |
La funció EsBuida
La pila estarà buida si el punter a cim apunta a NULL. Per tant la funció retorna CERT o FALS segons el cas. |
int EsBuida(PilaEnters * Pila)
{ return !(Pila->PunterACim); } |
La funció InsereixElement
Aquesta funció fa reserva d'una zona de memòria (un nou element) per tal de hi posar les dades amb les instruccions:
|
Boolean InsereixElement(int
Num, PilaEnters * Pila)
{ ElementPilaEnters * Element; Element = new ElementPilaEnters; if (Element) { Element->Dada = Num; Element->PunterASeguent=Pila->PunterACim; Pila->PunterACim = Element; return CERT; } else return FALS; } |
La funció TornaElement
Aquesta funció ens torna les dades del cim de la pila si la pila no està buida. |
int TornaElement(PilaEnters *Pila)
{ if (! EsBuida(Pila)) return Pila->PunterACim->Dada; else return NULL; } |
La funció ExtreuElement
Aquesta funció esborra l'element que està al cim de la pila si no està buida.
|
void ExtreuElement(PilaEnters *Pila)
{ ElementPilaEnters * Provisional; if (! EsBuida(Pila)) { Provisional = Pila->PunterACim; Pila->PunterACim = Provisional->PunterASeguent; delete Provisional; } } |
Queda fer la funció
principal en la que es declara una pila anomenada Numeros i en la que inserim
els elements 5,7,9 i 11.
Després es fa ús d'aquestes dades i s'eliminen de la pila. |
#include <stdio.h>
#include <conio.h> void InicialitzaPila(PilaEnters * Pila);
main()
PilaEnters Numeros;
InsereixElement(5,&Numeros);
printf("Element %d\n",TornaElement(&Numeros));
printf("Numeros est'a buida: %d\n", EsBuida(&Numeros)); return 0;
|
Una cua és una estructura de dades (Objecte en la terminologia de OOP) que es composa d'elements del mateix tipus i podem dir que funciona de la següent manera:
Tipus de dades
En primer lloc definim un nou tipus de dades que anomenarem CuaEnters. Farem que el punter a principi apunti al primer element de la cua i que el punter a final apunti a l'element següent a l'últim. |
typedef struct CuaEnters
{ int PunterAPrincipi; int PunterAFinal; int Element[MAX]; }; |
La funció InicialitzaCua.
La cua estarà inicialitzada quan el punter a principi i el punter a final apuntin tots dos a la posició 0 del vector d'elements. |
void InicialitzaCua(CuaEnters * Cua)
{ Cua->PunterAPrincipi=0; Cua->PunterAFinal=0; } |
La funció EsBuida
Segons el conveni utilitzat per als punters la cua estarà buida quan els dos punters apuntin al mateix lloc. |
Boolean EsBuida(CuaEnters * Cua)
{ return Cua->PunterAPrincipi == Cua->PunterAFinal; } |
La funció InsereixElement
Aquesta funció comprova si el vector està ple (Cua->PunterAFinal+1<MAX) i en cas que hi hagi elements buits i fa la inserció del valor al final de la cua i incrementa el PunterAFinal. Aquesta funció podria tornar CERT en cas que hagués tingut èxit en la inserció i FALS en cas contrari. |
void InsereixElement(int Num, CuaEnters * Cua)
{ if (Cua->PunterAFinal+1<MAX) { Cua->Element[Cua->PunterAFinal]=Num; Cua->PunterAFinal++; } else printf("La pila està plena\n"); } |
La funció PrimerElement
Primer comprova si la cua està buida i en cas que no l'estigui torna les dades corresponents al primer element de la cua, si no torna un NULL. |
int PrimerElement(CuaEnters *Cua)
{ if (! EsBuida(Cua)) return Cua->Element[Cua->PunterAPrincipi]; else { printf("La cua està buida\n"); return NULL; } } |
La funció TreuPrimer
Per a treure un element aquesta funció incrementa en 1 el punter a principi cas que la cua no estigui buida. |
void TreuPrimer(CuaEnters *Cua)
{ if (! EsBuida(Cua)) Cua->PunterAPrincipi++; else printf("No hi ha més elements\n"); } |
Queda per escriure la funció main que fa ús del tipus de dades VuaEnters i de les seves funcions. | #include <stdio.h>
#include <conio.h> #define MAX 100 #define CIERTO 0
typedef int Boolean; void InicialitzaCua(CuaEnters * Cua);
main()
CuaEnters Numeros;
return 0;
|
En aquest cas també veurem que la reserva de memòria per a les dades es fa en el moment d'execució i per tant la cua pot créixer tant com la mida de memòria disponible.
Tal i com veiem a la figura es necessiten dos nous tipus de dades que són el tipus element de cua i la cua mateixa. En la figura veiem que en el cas d'una pila de nombres enters les dades dels elements són nombres enters. Però aquí podem posar qualsevol tipus de dades.
Tipus de dades
En primer lloc definim dos nous tipus de dades:
|
typedef struct ElementCuaEnters
{ int Dada; ElementCuaEnters * PunterASeguent; }; typedef struct CuaEnters
|
La funció InicialitzaCua.
La cua estarà inicialitzada quan el punter a principi i el punter a final apuntin tots dos a NULL. |
void InicialitzaCua(CuaEnters * Cua)
{ Cua->PunterAPrincipi=NULL; Cua->PunterAFinal=NULL; } |
La funció EsBuida
La cua estarà buida quan el punter a principi apunti a NULL. |
Boolean EsBuida(CuaEnters * Cua)
{ return !(Cua->PunterAPrincipi); } |
La funció InsereixElement
Aquesta funció fa una reserva de memòria per a la nova dada creant un nou element. Si té èxit posa les dades en el nou element i fa que el punter a següent sigui NULL ja que no hi haurà element següent. Després actua segons l'estat de la cua:
|
void InsereixElement(int Num, CuaEnters * Cua)
{ ElementCuaEnters * Element;
if (EsBuida(Cua))
Cua->PunterAPrincipi=Element;
|
La funció PrimerElement
Torna les dades corresponents al primer element de la cua. Si no hi ha elements torna un NULL. |
int PrimerElement(CuaEnters *Cua)
{ return Cua->PunterAPrincipi->Dada; } |
La funció TreuPrimer
Aquesta funció comprova si la cua està buida. En cas que no estigui fa les següents operacions:
|
void TreuPrimer(CuaEnters *Cua)
{ if (!(EsBuida(Cua))) { ElementCuaEnters * PunterProvisional; PunterProvisional = Cua->PunterAPrincipi; Cua->PunterAPrincipi = Cua->PunterAPrincipi->PunterASeguent; delete PunterProvisional; if (! Cua->PunterAPrincipi) Cua->PunterAFinal=NULL; } } |
Queda per escriure la funció main que fa ús dels nous tipus de dades de les seves funcions. | #include <stdio.h>
#include <conio.h> #define CIERTO 0
typedef int Boolean; void InicialitzaCua(CuaEnters * Cua);
main()
CuaEnters Numeros;
InsereixElement(15,&Numeros);
printf("Primer element: %d\n",PrimerElement(&Numeros));
return 0;
|
Recorregut, Busca, InsereixDespres, InsereixAbans, EsborraElement, EsborraSeguent, EsborraLlista
Totes aquestes funcions ens permetran també fer llistes ordenades.
A la figura veiem una llista que es diu Classe i que té quatre registres. El programa que crea aquesta llista és el següent:
Les estructures per a aquesta
llista són:
|
typedef struct RegistreAgenda
{ char nom[45]; char direccio[36]; char telefon[15]; char email[40]; RegistreAgenda * PunterASeguent; }; typedef struct LlistaAgenda
|
La funció InicialitzaLlista
Aquesta funció inicialitza la llista. Només ha de fer-se ús després de declarar una llista nova. Heu de pensar que, si una llista ja té registres aquesta funció no allibera la memòria que ocupen aquest elements i serà memòria perduda. |
void InicialitzaLlista(LlistaAgenda *
Llista)
{ Llista->PunterAPrimer=NULL; } |
La funció EsBuida
Aquest funció torna Cert si el punter al primer element apunta a NULL i fals en cas contrari. |
int EsBuida(LlistaAgenda * Llista)
{ return !(Llista->PunterAPrimer); } |
La funció InsereixElement
Aquesta funció insereix un element (registre) com a primer element de la llista. Aquesta funció rep un punter a un element i el punter a la llista. Torna Cert cas que tingui èxit i pugui inserir l'element i fals en cas contrari. |
Boolean InsereixElement(RegistreAgenda * Reg, LlistaAgenda
* Llista)
{ RegistreAgenda * Element; Element = new RegistreAgenda; if (Element) { memcpy(Element,Reg,sizeof(RegistreAgenda)); Element->PunterASeguent=Llista->PunterAPrimer; Llista->PunterAPrimer = Element; return CERT; } else return FALS; } |
La funció TornaElement
Aquesta funció torna el primer element de la llista. No fa cap validació i cas que la llista estigui buida es produirà un error del programa. |
RegistreAgenda TornaElement(LlistaAgenda *Llista)
{ RegistreAgenda Provisional; strcpy(Provisional.nom,""); strcpy(Provisional.direccio,""); strcpy(Provisional.telefon,""); strcpy(Provisional.email,""); if (! EsBuida(Llista))
|
La funció ExtreuElement
Aquesta funció saca el primer element de la llista. Si valida el cas de tenir la llista buida i no es produirà error. |
void ExtreuElement(LlistaAgenda *Llista)
{ RegistreAgenda * Provisional; if (! EsBuida(Llista)) { Provisional = Llista->PunterAPrimer; Llista->PunterAPrimer = Provisional->PunterASeguent; delete Provisional; } } |
La funció principal i les declaracions prèvies só les del quadre de la dreta. | #include <stdio.h>
#include <conio.h> #include <string.h> typedef int Boolean;
void InicialitzaLlista(LlistaAgenda * Llista);
main()
clrscr(); InicialitzaLlista(&Classe); strcpy(regActual.nom,"Josep");
return 0;
|
La funció Recorregut
Aquesta funció rep una llista i imprimeix per pantalla tots els noms de la llista. Per a fer el recorregut fa ús d'un punter auxiliar anomenat PunterAReg. En principi fem apuntar aquest punter al primer registre. Després fem que apunti al següent i a així successivament. |
void Recorregut(LlistaAgenda
* Llista)
{ //Recorregut de la llista RegistreAgenda * PunterAReg; PunterAReg = Llista->PunterAPrimer; while (PunterAReg) { printf("%s\n",PunterAReg->nom); PunterAReg=PunterAReg->PunterASeguent; } } |
La funció Busca
Aquesta funció rep un string amb una cadena a buscar i una llista on buscar aquest string. Fa un recorregut pels registres buscant la cadena al camp Nom. Cas que trobi la informació torna un punter a aquest registre. Si no la troba llavors torna NULL. Podem cridar aquesta funció amb les instruccions: RegistreAgenda * RegistreBuscat;
|
RegistreAgenda * Busca(char
* InfoABuscar, LlistaAgenda * Llista)
{ //Recorregut de la llist RegistreAgenda * PunterAReg; Boolean Trobat=FALS; PunterAReg = Llista->PunterAPrimer; while (PunterAReg && (!Trobat)) { if (! strcmp(InfoABuscar,PunterAReg->nom)) Trobat = CERT; else PunterAReg=PunterAReg->PunterASeguent; } return PunterAReg; } |
La funció InsereixDespres
Aquesta funció rep el punter a un registre d'una llista i un punter a un registre a inserir després. La funció insereix el registre. Si té èxit torna CERT i sinó torna FALS. Poden cridar aquesta funció amb les instruccions: RegistreAgenda regActual;
Si té èxit la recerca de Ramón a la llista llavors inserim Lola després de Ramón. |
Boolean InsereixDespres(RegistreAgenda
* PunterAReg,
RegistreAgenda * RegAInserir) { RegistreAgenda * Element; Element = new RegistreAgenda; if (Element) { memcpy(Element,RegAInserir,sizeof(RegistreAgenda)); Element->PunterASeguent=PunterAReg->PunterASeguent; PunterAReg->PunterASeguent = Element; return CERT; } else return FALS; } |
La funció InsereixAbans
El procedimient es una mica més complicat car no podem moure el PunterAReg en sentit esquerra. Peró es pot fer por mitjà d'un artifici que consisteix en inserir el nou registre després i intercanviar les dades dels dos registres. |
Boolean InsereixAbans(RegistreAgenda
* PunterAReg,
RegistreAgenda * RegAInserir) { RegistreAgenda * Element; Element = new RegistreAgenda; if (Element) { memcpy(Element,PunterAReg,sizeof(RegistreAgenda)); memcpy(PunterAReg,RegAInserir,sizeof(RegistreAgenda)); PunterAReg->PunterASeguent = Element; return CERT; } else return FALS; } |
La funció EsborraElement.
Es poden presentar tres situacions diferents segons el registre a esborrar:
RegistreBuscat=Busca("Pep",&Classe);
|
void EsborraElement(RegistreAgenda
* PunterAReg,
LlistaAgenda * Llista) { RegistreAgenda * Provisional, * RegAnterior; if(!Llista->PunterAPrimer->PunterASeguent)
//Nomes existeix un registre
|
La funció EsborraSeguent
Aquesta funció crida a la funció anterior amb el punter adien. RegistreBuscat=Busca("Ramon",&Classe); if (RegistreBuscat) EsborraSeguent(RegistreBuscat, &Classe); |
void EsborraSeguent(RegistreAgenda
* PunterAReg,
LlistaAgenda * Llista) { if (PunterAReg->PunterASeguent) EsborraElement(PunterAReg->PunterASeguent, Llista); } |
La funció EsborraLlista.
Consisteeix en esborrar successívamente el primer registre. Per a fer-hoo es cópia el segon registre al primer mentre hi ha segon registre. També és necesari matenir un punter provisional que apunti al segon registre per a, després de copiades les seves dades al primer, alliberar la memoria que ocupa. Quedará alliberar la memòria del primer registre i inicialitzar
la llista.
|
void EsborraLlista(LlistaAgenda
* Llista)
{ RegistreAgenda * PunterProvisional; while(Llista->PunterAPrimer->PunterASeguent) { PunterProvisional=Llista->PunterAPrimer->PunterASeguent; memcpy(Llista->PunterAPrimer,PunterProvisional, sizeof(RegistreAgenda)); delete PunterProvisional; } delete Llista->PunterAPrimer; Llista->PunterAPrimer=NULL; } |
Per a recórrer una llista circular hem de mantenir un punter PunterAReg que apunti a un element de la llista. Com que no té ni principi ni fi haurem de convenir que acabarem el recorregut quan el PunterAReg torni a estar en el lloc on va començar a moure's. Així si el punter està apuntant a l'element Lola i el movem (Josep, Pep, Ramon, ...) haurem d'aurar el moviment quan arribem una altra vegada a Lola.
En aquest tipus de llista necessitaràs les estructures següents:
typedef struct RegistreAgenda
{ char nom[45]; char direccio[36]; char telefon[15]; char email[40]; RegistreAgenda * PunterASeguent; }; typedef struct LlistaCircular
|
Hauràs de fer les funcions següents: InicialitzaLlista,
InsereixALaDreta, Busca, EsborraElement i ImprimeixNoms.
En aquest tipus de llista necessitaràs les estructures següents:
typedef struct ElementLlista
{
RegistreAgenda * PunterAAnterior;
char nom[45];
char direccio[36];
char telefon[15];
char email[40];
RegistreAgenda * PunterASeguent;
};typedef struct LlistaBidireccional
{
RegistreAgenda * PunterAPrimer;
RegistreAgenda * PunterAUltim;
};Hauràs de fer les funcions següents: InicialitzaLlista, InsereixALaDreta, InsereixALaEsquerra, Busca, EsborraElement i ImprimeixNomsDreta i ImprimeixNomsEsquerra.
typedef struct Carta
{ int Numero; int Palo; struct Carta * pCartaSeguent; }; typedef struct LlistaCartes
|
Estat inicial: Disposem de sis llistes on posar cartes: Maç,
CartesSortides, Oros, Copes, Espases i Bastons.
El joc consisteix en treure les cartes de la llista Maç
de dues en dues i apilalar-les a la llista CartesSortides. D'aquesta llista
anirem passant cartes a les llistes d'oros, copes espases o bastos de manera
ordenada, és a dir que les llistes dels pals estan ordenades de
menor a major i totes les cartes d'una llista de pal són del mateix
pal.
Per a fer el programa de manera que pugui jugar un usuari farem un
menú amb les següents opcions:
0 Acabar
1 Iniciar el joc
|
Funcions auxiliars que et seran útils:
typedef struct ElementLlista
{ char nom[45]; char direccio[36]; char telefon[15]; char email[40]; }; |
Has de emmagatzemar aquests registres en una llista unidireccional ordenada pel camp nom i fer un llistat per pantalla posant pàgines de 10 en 10 registres.
typedef struct
Punt
{ int x; int y; char Caracter; int Color; int Fons; struct Punt * Seguent; }; |
a) Un programa demana per pantalla els punts d'un dibuix i a mesura que es reben per pantalla els inclou en una llista unidireccional. Després escriu el dibuix en pantalla i ho repeteix quatre vegades en diferents posicions.
b) Fes un programa igual que l'anterior, preo ara la font d'entrada
de dades serà un fitxer de disc.
Primer has de fer un fitxer ASCII que contingui les dades.