Documente Academic
Documente Profesional
Documente Cultură
Alba Iulia
2008
CUPRINS
I.
f.
Denumire
structur
Structura
secvenial
Reprezentare grafic
Semnificaie
Execuia
secvenial
a
operaiilor
descrise
n
simbolurile de calcul:
S1, Sn
Execut secvena S1 dac
condiia Cond este adevrat
SAU execut secvena S2 dac
Cond este fals
Structura
alternativ
Structura repetitiv
precondiionat:
Ct timp condiia Cond rmne
adevrat
repet execuia
secvenei S
Structura repetitiv
postcondiionat:
Repet execuia secvenei S ct
timp condiia Cond rmne
adevrat
7. Pseudocod
Pseudocodul este un limbaj standardizat intermediar ntre limbajul natural i
limbajul de programare. Descrierea algoritmilor cu ajutorul limbajului pseudocod
nltur din neajunsurile utilizrii schemelor logice, fiind mult mai flexibil i
natural dect acestea. n plus, descrierea n pseudocod permite convertirea cu
uurin a algoritmilor astfel exprimai ntr-un limbaj de programare.
Limbajul pseudocod folosete dou tipuri de propoziii:
1. Propoziiile standard: proprii limbajului pseudocod
2. Propoziiile ne-standard: acele propoziii ce descriu n limbaj uzual
operaii ce urmeaz a fi detaliate, rafinate ulterior. Aceste propoziii sunt marcate
prin simbolul #.
Exist o concordan ntre simbolurile grafice ale schemelor logice i
propoziiile limbajului pseudocod. De asemenea, structurile fundamentale pot fi
descrise prin propoziii proprii limbajului pseudocod.
Tabelul urmtor prezint concordana dintre descrierea grafic i propoziiile
pseudocod pentru operaii specifice algoritmilor:
Schema logic
Pseudocod
Algoritmul NUME-ALGORITM este:
Sfrit NUME-ALGORITM
sau SfAlgoritm
Citete a1,a2,,an
Tiprete a1,a2,,an
Limbajul pseudocod, pentru a uura traducerea ulterioar a algoritmului n
limbaj de programare, permite folosirea unei propoziii standard echivalent uneia
dintre instruciunile populare ale limbajelor de programare. Aceasta propoziie
corespunde unei structuri repetitive cu numr cunoscut de repetri i se descrie
astfel:
Schema logic
Pseudocod
Pentru var de la I la F cu pasul pas
Execut S
Sfrit Pentru
- structura ciclic cu numr cunoscut
de repetri
Schema logic
Pseudocod
Limbaj natural
[Execut] S1
...
[Execut] Sn
Dac Cond atunci
Execut S1
Altfel
Execut S2
SfDac
Dac Cond atunci
Execut S
Sf Dac
Execut secvenial
blocurile de calcul
S1, S2,Sn
Dac condiia este
adevrat atunci se execut
blocul S1, altfel se execut
blocul S2.
10
Structuri
repetitive
Altfel
Tiprete Rdcini complexe
SfDac
SfDac
Sfrit EcuaieGradII
11
Pentru i de la 1 la n cu pasul 1
Execut P:=P * i
Sfrit Pentru
Tiprete P
Sfrit Factorial
Structur
repetitiv
precondiionat
10. Subalgoritmi
O problem de dificultate sporit necesit uneori o mprire n subprobleme de
dificultate mai mic n baza principiului divide et impera. Algoritmii de rezolvare a
subproblemelor rezultate sunt mai uor de descris i vor constitui subalgoritmi ai
algoritmului global de rezolvare a problemei considerate. Deseori se poate ntlni
situaia ca o anumit secven de operaii s fie executat de mai multe ori pentru
date de intrare diferite. n aceste cazuri este util construirea unui subalgoritm
corespunztor secvenei de operaii i apelarea acestuia n algoritmul global ori de
cte ori este nevoie.
Subalgorimii sunt practic algoritmi care rezolv subprobleme ale unei probleme
date. Acest lucru indic faptul c orice subalgoritm verific proprietile de
generalitate, finitudine, claritate, corectitudine ale algoritmilor informatici.
Caracterul de generalitate va fi asigurat prin parametrizarea subalgoritmilor.
12
Acetia vor primi la intrare nu datele de intrare ale problemei ci date intermediare
ale problemei globale sau rezultate pariale din procesele de calcul. Ieirile
subalgoritmilor sunt date pe care algoritmul general le va prelucra ulterior.
Comunicarea dintre subalgoritm i algoritmul printe se realizeaz prin
intermediul parametrilor. n pseudocod sintaxa general a unui subalgoritm este
urmtoarea:
Subalgoritm NumeSubalgoritm ( lista parametrii formali)
. // operaii asupra parametrilor din lista
Sfrit NumeSubalgoritm
Apelarea unui subalgoritm din algoritmul apelant se face prin propoziia:
Cheam NumeSubalgoritm (list parametrii actuali)
Lista parametrilor formali va conine nume generice ale datelor de intrare i
datelor de ieire ale subalgoritmului. Lista parametrilor actuali conine numele
datelor concrete care se doresc a fi transmise subalgoritmului i numele sub care se
rein rezultatelor pariale ale subalgoritmului.
La apelul unui subalgoritm parametrii formali (generici) din list se vor nlocui
cu parametrii concrei (actuali).
Exemplu: S se descrie un algoritm de determinare a maximului dintre 4 valori
date, evideniind subalgoritmul de determinare a maximului dintre oricare dou
valori.
Subalgoritm Maxim2(DI: x ,y; DE: Max)
Dac x>y atunci
Max=x
Altfel
Max=y
SfDac
Sfrit Maxim2
Apelul repetat al
subalgoritmului, pentru
diferii parametri
actuali
13
14
II.
Identificatori
Cuvinte cheie (cuvinte rezervate)
Constante
Operatori
C. Unitile sintactice:
1. Expresii
2. Instruciuni
D. Comentarii: au rolul de a justifica textul surs redactat i de a oferi o
mai bun nelegere a acestuia. Comentariile sunt marcate n programul
surs prin // sau /**/
Exemple: //variabila i are semnificaia unui contor
/* acest program determin
soluiile ecuaiei de gradul II*/
15
struct
while
Domeniul de valori
Semnificaie
ntreg reprezentat intern pe 16 bii,
n cod complementar fa de 2
ntreg reprezentat intern pe 16 bii,
n cod complementar fa de 2
ntreg reprezentat intern pe 32 bii,
n cod complementar fa de 2
ntreg fr semn reprezentat pe 16
bii
ntreg fr semn reprezentat pe 32
bii
caracter reprezentat intern prin
codul ASCII corespunztor pe 8
bii
float
double
unsigned
char
signed
char
long
double
[-32768,32767]
[-32768,32767]
[-2147483648,2147483647]
[0,65535]
[0,4294967295]
[0.255]
[- 3,4 10 38 , 3,4 10 38 ]
- valorile minime cuprinse n
intervalul [- 3,4 10 38 ,
3,4 10 38 ] se confund cu 0
[- 1.7 10 308 , 1.7 10 308 ]
- valorile minime cuprinse n
intervalul
[- 1.7 10 308 ,
1.7 10 308 ] se confund cu
0
[0,255]
[-128,127]
- valoarea minim absolut
reprezentabil: 3,4 10 4932
- valoarea maxim absolut
reprezentabil: 1.1 10 +4932
B.3. Constante
Constantele sunt mrimi ale cror valoare nu se modific pe parcursul execuiei
unui program. Constantele se clasific n patru categorii:
17
Constante caracter
Constante ir de caracter
Constante numerice
Constante simbolice
Constante caracter
Constanta caracter este un simbol care are ataat valoarea codului ASCII al
caracterului corespondent.. Un caracter este reprezentat n memoria calculatorului
pe un octet (8 bii).
Exist dou categorii de caractere: caractere afiabile (imprimabile) i caractere
neafiabile:
<constanta caracter>::= <caracter>
<caracter>::=<caracter afiabil (ASCII)> |<caracter neafiabil>
O constant caracter grafic (afiabil) se specific incluznd caracterul
ntre simbolurile apostrof: .
Exemple:
A codul ASCII 65
a codul ASCII 97
+ codul ASCII 77
2 codul ASCII 50
O constant caracter non-grafic (neafiabil) au notaii speciale, se specific
utiliznd caracterul backslash (\) i apostroful ().
Exemple:
ntoarcerea cu un spaiu (Backspace) \b codul ASCII 8
Retur car
\r codul ASCII 13
Linie nou \n codul ASCII 10
Constante ir de caractere
Sunt formate dintr-o succesiune de caractere care se delimiteaz prin ghilimele:
exemplu. Fiecare caracter al irului se reprezint n memoria calculatorului pe un
octet, prin codul ASCII corespunztor. Sfritul secvenei de coduri ale caracterelor
dintr-un ir este marcat n memorie de un octet special cu valoarea NULL,
corespunztor caracterului \0.
Observaie: reprezentarea n memorie a lui k este diferit de reprezentarea lui
k. Astfel, irului k i este alocat un spaiu de 2 octei, ultimul fiind cel ce
marcheaz sfritul NULL.
Constante numerice
Constantele numerice sunt n fapt numerele. n limbajul C se face o distincie
ntre numere, n funcie de tipul (ntreg sau real), baza de numeraie i precizia
codificrii.
Numerele ntregi zecimale, exemple: -123, 5, +5.
Numerele octale ntregi, ncep cu cifra 0 i sunt de forma: 0cc, unde c
este cifr octal. Exemple: 0452 , unde 452 este numrul n baza 8
Numerele hexazecimale ntregi: ncep cu 0x sau 0X i sunt de forma:
0xcc unde c sunt cifre hexazecimale. Exemplu: 0xabbd.
18
21
<instruciune_n>
}
Rolul acestei instruciuni este de a grupa mai multe instruciuni simple,
compuse sau alte instruciuni bloc.
C. Instruciunile de ramificare
- corespund structurilor alternative: decizie simpl i decizie multipl.
Instruciunea if
Sintaxa instruciunii:
if ( <expresie> )
<instruciune_1>
23
else
<instruciune_2>
sau:
if ( <expresie> )
<instruciune_1>
Unde, instruciunile 1 sau 2 pot fi de asemenea instruciuni compuse sau bloc
de instruciuni.
Efectul instruciunii:
<expresia> este evaluat i dac valoarea obinut n urma evalurii este
diferit de 0 se execut instruciunea 1, altfel se execut instruciunea 2.
Cele dou instruciuni 1 i 2 se exclud reciproc.
Instruciunea IF simpl nu conine partea else <instruciune 2>
Instruciunea IF complet conine ambele ramuri.
Exemple:
if
(a!=0)
b=1/a;
If
(a>=b)
maxim=a;
if
else
maxim=b;
(delta>=0)
{
x1=(-b-sqrt(delta))/2*a;
x2=(-b+sqrt(delta))/2*a;
}
};
Efectul instruciunii:
- Se evalueaz expresia dintre paranteze la o valoare.
- Se caut secvenial valoarea la care a fost evaluat expresia n lista
de valori val1, val2, valn.
- Dac nu se gsete nici o valoare val1, val2,valn egal cu valoarea la
care a fost evaluat expresia se va executa, dac este precizat,
instruciunea default sau efectul instruciunii switch este nul
- Dac s-a gsit o valoare egal cu valoarea expresiei se va executa
instruciunea corespunztoare i se va prsi structura alternativ.
Exemplu: Afiarea numelui zilei din sptmn creia i corespunde un numr
1,2,...,7:
switch (numar)
{
case 1: printf(\n Luni);break;
case 2: printf(\n Marti); break;
case 3: printf(\n Miercuri); break;
case 4: printf(\n Joi); break;
case 5: printf(\n Vineri); break;
case 6: printf(\n Sambata); break;
case 7: printf(\n Duminica); break;
default:printf(\n nu este numar 1,2,3,4,5,6,7);
}
D. Instruciunile de ciclare
n pseudocod am evideniat cele trei tipuri de structuri repetitive. Acestora le
corespund trei instruciuni de ciclare n limbajului C:
Structura repetitiv precondiionat -> instruciunea while
Structura repetitiv postcondiionat -> instruciunea do while
Structura repetitiv cu numr prefixat de repetri -> instruciunea for
Instruciunea while
Sintaxa:
while (<expresie>) //antetul instruciunii
<instruciune>
//corpul instruciunii
unde: <instruciune> poate fi o instruciune simpl, compus, i poate conine
alte instruciuni while, caz n care spunem c avem o instruciune while imbricat
Efectul instruciunii:
n primul pas, se evalueaz expresia dintre paranteze. Dac rezultatul evalurii
este o valoare non-nul (adevrat) se execut instruciunea din corpul while i
procesul se reia prin reevaluarea expresiei. Dac rezultatul unei evaluri este nul, se
25
Instruciunea dowhile
Sintaxa:
do
<instruciune>
while (<expresie>);
//corpul
26
scanf(%c,&caracter);
}
while (caracter<>0);
Instruciunea for
Sintaxa:
for ( <expresie1> ; <expresie2> ; <expresie3> )
<instruciune>
Unde:
-
Efect:
- n primul pas, se evalueaz expresia1, respectiv se execut secvena de
iniializare
- Se evalueaz expresia2 (aceast expresie are acelai rol ca <expresie> din
structura while sau dowhile )
- Dac rezultatul evalurii anterioare este adevrat se execut corpul
instruciunii for i apoi se execut secvena de actualizare specificat prin
expresie3
- Dac rezultatul este fals, se ncheie execuia for.
Exemplu 1: Afiarea primelor 10 numere naturale.
for ( i=1; i<=10; i++)
printf(\n %d, i);
28
}
31
Exemplu de program C:
#include <stdio.h> //rezolvarea ec. de gradul II
#include <math.h>
void main(void)
{
double a,b,c,x1,x2,delta;
printf("\nDati coeficientii ecuatiei de gradul II: ");
scanf("%lf,%lf,%lf",&a,&b,&c);
if (a==0)
{
if (b==0)
printf("ecuatie imposibila!");
else
{
x1=-c/b;
printf("Solutia ecuatiei de grad I: %lf",x1);
}
}
else
{
delta=b*b-4*a*c;
if (delta>=0)
{
x1=(-b-sqrt(delta))/2*a;
x2=(-b+sqrt(delta))/2*a;
printf("Solutiile ec. gr. II: %lf, %lf",x1,x2);
}
else
printf("Ecuatia de gr. II nu are solutii reale!");
}
}
Probleme:
1. S se citeasc un sir de n numere ntregi de la tastatur i s se calculeze suma
acestora.
2. S se citeasc n numere ntregi i s se numere cte elemente sunt pozitive.
3. S se scrie un program C pentru afiarea relaiei dintre dou numere: < , > sau =.
4. Scriei un program C pentru rezolvarea ecuaiei de gradul II
Obs. Pentru scrierea unui program care rezolv ecuaia de gradul 2 este necesar
cunoaterea funciei de extragere a radicalului: sqr(<parametru>) al crei prototip
se afl n biblioteca math.h
5. S se scrie un program C care citete dou numere reale i un caracter: + , - , /
sau * i afieaz rezultatul operaiei aritmetice corespunztoare.
6. S se scrie un program C care afieaz toi multiplii de k mai mici dect o
valoare dat n.
7. S se scrie un program C care calculeaz i afieaz puterile lui 2 pn la n,
adic 2, 22,23, , 2n .
32
III.
<instruciuni>
}
- Corpul funciei cuprinde ntre acolade {} conine partea de declarare a
variabilele locale funciei respective i partea de prelucrare a datelor:
secven de instruciuni prin care se prelucreaz variabilele locale sau
altele cu caracter global.
- n antetul unei funcii este precizat tipul de dat pe care l returneaz
<tip>. Acesta poate fi void, caz n care funcia nu returneaz nimic.
- Parantezele rotunde (.) ce urmeaz numelui funciei delimiteaz lista
argumentelor funciei.
- Fiecrui argument i este precizat tipul i numele sub care este referit n
cadrul funciei curente.
Prototipul funciei: este format de antetul funciei din care poate s lipseasc
numele parametrilor formali:
<tip>NumeFuncie(<tip1>,,<tipn>) ; //declararea unei funcii
La definirea unei funcii, argumentele precizate n antetul acesteia se numesc
parametrii formali. La apelul funciei, parametrii formali sunt nlocuii de
parametrii actuali:
Apelul unei funcii se face prin construcia:
33
Cifre
2. Funcii cu tip
Dac unei funcii i este precizat tipul (diferit de void) acest lucru ne spune
c funcia returneaz codului apelant o valoare de acest tip. n caz contrar, funcia
nu returneaz valoare. Tipul funciei reprezint n fapt tipul de dat al valorii pe
care o returneaz. Instruciunea return, folosit n cadrul funciei cu tip definite,
are sintaxa:
return <expresie>;
i are rolul de a reveni n programul apelant de a returna acestuia o valoare de
tipul precizat.
Funcia poate fi apelat printr-o instruciune de asignare:
ValoareReturnat= NumeFuncie(); //membrul stng al instruciunii este o
variabil de tipul returnat de funcie
34
void main(void)
//varianta compacta
{
printf(valoarea este: %d,
CitesteValoare());
}
3. Funcii parametrizate
Funciile cu parametri corespund acelor subalgoritmi care prelucreaz date de
intrare reprezentnd rezultate intermediare ale algoritmului general. Intrrile
funciei sunt descrise prin lista parametrilor formali ce conine nume generice ale
datelor prelucrate n corpul funciilor. Parametrii efectivi sunt transmii la apelul
funciilor, acetia nlocuind corespunztor parametrii generici specificai. n
limbajul C transmiterea parametrilor actuali funciilor apelate se face prin valoare:
nelegnd prin aceasta c valorile curente ale parametrilor actuali sunt atribuite
parametrilor generici ai funciilor.
Exemplu:
int minim(int a, int b)
{ return ( (a>b)?a:b) }
Fie funcia:
tip NumeFuncie(tip1 pf1, tip2 pf2, , tipn pfn)
{};
La apelul:
NumeFuncie(pa1, pa2, , pan);
se vor transmite prin valoare parametrii actuali i fiecare parametru formal din
antetul funciei este nlocuit cu valoarea parametrului actual. Dac parametrul
35
actual este o expresie, aceasta este evaluat la o valoare, ulterior este copiat n
parametrul formal corespunztor.
Observaie: modificrile aduse parametrilor formali n cadrul funciei
apelate nu afecteaz valorile parametrilor actuali. Exempul urmtor
evideniaz acest aspect:
#include <stdio.h>
void putere(int n) //ridic la ptrat valoarea n i afieaz
rezultatul
{
n=n*n;
printf( valoarea lui n in functie este %d,n); //n este 25
}
void main(void)
{
int n=5;
printf( valoarea lui n inainte de apel este %d,n);
// efectul: valoarea lui n inainte de apel este 5
putere(n);
// efectul: valoarea lui n in functie este 25
printf( valoarea lui n dupa de apel este %d,n);
//efectul: valoarea lui n dupa de apel este 5 (!nu s-a
modificat parametrul actual)
}
n multe situaii este necesar ca efectul operaiilor din corpul unei funcii apelate
asupra parametrilor de intrare s fie vizibil i n corpul apelant. n exemplul
anterior efectul dorit este acela ca variabila n dup apel s pstreze rezultatul
ridicrii la ptrat. Transmiterea prin valoare nu permite acest lucru. n schimb,
transmiterea prin adres realizat prin intermediul variabilelor de tip pointer
asigur modificarea valorilor parametrilor actuali.
Pentru a nelege mecanismul transmiterii parametrilor prin adres n limbajul
C, vom defini n continuare noiunea de pointer.
Pointeri
Pointer = variabil care are ca valori adrese de memorie.
Sintaxa de declarare a unei variabile pointer:
tip * p; // variabil pointer spre tip
Prin aceast declaraie s-a introdus o variabil ale crei valoare este adresa unei
zone de memorie ce poate reine o dat de tipul tip.
Fie x o variabil simpl de tipul tip:
tip x;
i p un pointer (variabil de tip pointer) care are ca valoare adresa variabilei x.
Pentru a face o atribuire unei variabile de tip pointer p se folosete construcia:
p=&x; // semnificaia: lui p i se atribuie adresa variabilei x.
36
Valoarea lui x
Adresa lui x
37
(!s-a
a,
int
//Apelul:
int x,y;
x=1;
x=2;
interschimbare(x,y)
//Efectul
Inainte de apel:
x=1
y=2
Dup apel:
x=1
y=2
//Apelul:
int x,y;
x=1;
x=2;
interschimbare(&x,&y)
//Efectul
Inainte de apel:
x=1
y=2
Dup apel:
x=2
y=1
Probleme:
1. Dezvoltai un program C care determin minimul dintr-un ir de numere
citite de la tastatur (fr a utiliza tablouri), punnd n eviden funcia care
determin minimul dintre dou valori.
2. Scriei o funcie care rezolv ecuaia de gradul II. Funcia va avea 5
argumente: coeficienii ecuaiei (a,b,c) i posibilele soluii (x1,x2). Funcia
returneaz un numr ntreg cu semnificaia:
- -1, ecuaia nu este de gradul II
- 0 , ecuaia are soluii complexe
- 1, ecuaia are soluii reale
Primii trei parametrii se transmit prin valoare, ultimii doi - prin adres.
38
RECURSIVITATE
Recursivitatea se obine prin instruciunea de apel a unei funcii n corpul
definirii funciei respective:
<tip>NumeFuncie(<tip1><arg1>,,<tipn><argn>)
{
<instruciuni de declarare de tip a variabilelor locale>
<instruciuni>
}
La fiecare apel al funciei recursive, pe stiva programului sunt depuse noul set
de variabile locale (parametrii). Chiar dac variabile locale au acelai nume cu cele
existente nainte de apelarea funciei, valorile lor sunt distincte, i nu exist
conflicte de nume. Practic, ultimele variabile create sunt luate n considerare n
operaiile coninute de funcie.
Problem 1: S se calculeze P(n)=n! printr-o funcie recursiv.
Analiza problemei: Pornind de la observaia c produsul P(n)=1*2*(n-1)*n se
mai poate formula ca:
P(n)=P(n-1) * n, vom defini o funcie factorial care trateaz problema
determinrii lui P(k) pe baza formulei anterioare, presupunnd c P(k-1) este
calculabil dup acelai procedeu. P(k-1)=P(k-2)*(k-1).
Funcia autoapelant trebuie s conin o condiie de terminare a recursivitii.
n problema calculului n!, condiia de terminare a recursivitii se deduce din
observaia c 1! are valoarea 1, ceea ce nu mai necesit un calcul suplimentar.
Apelul iniial al funciei factorial se va face pentru parametrul actual nreprezentnd data de intrare a programului. Funcia returneaz la ieirea din apel
rezultatul dorit n!.
int factorial(int k)
{
if (k>1)
return (k*factorial(k-1));
else
return 1;
}
//apelul functiei
int p;
p=factorial(n);
Considerm n=3
1. La apelul iniial factorial(n) se transmite valoarea 3 parametrului formal k i
se pred controlul funciei apelate
2. Din condiia adevrat 3>=1 rezult amnarea revenirii din funcie i un
nou apel factorial(2); la ieirea din primul apel se va return 3*factorial(2).
3. Apelul factorial(2) va produce transmiterea valorii 2 la parametrul formal
k i predarea controlului funciei apelate pentru valoarea curent a
parametrului
4. Condiia adevrat 2>=1 produce o nou apelare factorial(1) cu amnarea
revenirii din apelul curent; la ieirea din acest al doilea apel se va return
2*factorial(1) .
5. Apelul factorial(1) va produce transmiterea valorii 1 la parametrul formal
k i predarea controlului funciei apelate pentru valoarea curent a
parametrului
6. Din condiia fals 1>1 rezult c la ieirea din acest apel se va return 1 i
funcia nu se mai apeleaz.
7. Revenirea din ultimul apel este urmat de revenirile n cascad din apelurile
precedente n ordinea invers, ceea ce conduce la rezultatul 3*2*1 = 6.
factorial(n)
n* factorial(n-1)
n* (n-1)*factorial(n-2)
n* (n-1)* (n-2)*factorial(n-3)
...
n* (n-1)* (n-2)*...*factorial(1)
n* (n-1)* (n-2)*...* (1)
Fibonacci(k)=Fibonacci(k-1)+ Fibonacci(k-2)
//autoapel
IV.
42
tab[1]
tab[2]
......................
tab[n]
tab
Exemplu:
int tab[100];
//tab este adresa elementului t[0]
Observaie: Deoarece memoria alocat unui tablou este o zon contigu, respectiv,
elementele tabloului sunt grupate, cunoscnd adresa primului element, printr-o
operaie elementar se poate accesa oricare alt element al tabloului.
Operaii cu pointeri:
Asupra pointerilor se pot efectua urmtoarele operaii:
- incrementare/decrementare
- adunarea/scdere cu un ntreg
- diferena a doi pointeri
Fie declaraia urmtoare:
<tip> *p; //p este un pointer la tipul <tip>
Efectul operaiilor de incrementare/decrementare este urmtorul:
p++ i ++p echivalent cu: p=p+dim
p-- i --p echivalent cu: p=p-dim
unde: dim reprezint dimensiunea exprimat n octei a unei variabile de tipul
<tip>
Exemplu.
int *p;
p++;
//p se mrete cu 2, deoarece o variabil de tipul int se memoreaz pe 2 octei
Adunarea i scderea unui ntreg
Fie: <tip> *p;
int k;
Expresiile: p+k, p-k sunt expresii valide n limbajul C, avnd semnificaiile
urmtoare:
43
tab
tab+1
tab[1]
tab[2]
......................
tab+2
tab[n]
tab+n
Exemplu:
int tab[100]; //tablou de 100 ntregi
int i;
(tab) adresa primului element tab[0]
(tab+1) adresa celui de-al doilea element tab[1]
(tab+i) reprezint adresa celui de-al (i-1) -lea element al tabloului, respectiv este
adresa elementului tab[i]
*(tab+i) reprezint valoarea elementului tab[i]
Diferena a doi pointeri:
Dou variabile de tip pointer prin care se refer elementele aceluiai tablou pot
fi sczute. Fie tabloul tab i doi pointeri p i q care adreseaz elementele tab[i] i
tab[j]. Diferena p-q este un numr ntreg k, reprezintnd numrul de elemente
care desparte cele dou adrese. Aceast dimensiune se poate determina prin
calculul elementar: k=(j-i).
ntre adresele p i q se afl un numr de (j-i) elemente, respectiv, (j-i)* dim
octei, unde dim - dimensiunea necesar reprezentrii unui element al tabloului.
dim
dim
tab[i]
tab+i
......................
dim *k
tab[i+k]
tab+(i+k)
iruri de caractere
Operaiile cu iruri de caractere se efectueaz prin apelul unor funcii de
bibliotec specifice. Prototipurile funciilor care manipuleaz iruri de caractere se
afl n fiierul antet string.h.
44
este operaia prin care un ir de caractere surs este copiat ntr-o alt
zon de memorie ataat unui alt ir de caractere destinaie
funcia specific este: strcpy
char * strcpy(char *destinatie, const char *sursa)
45
pass[0]=NULL;
printf("dati cuv ");scanf("%s",&cuv);
i=(strlen(cuv)-1);
if (i%2) i--;
n=0;
while (i>=0)
{
pass[n]=cuv[i];
i-=2;
n++;
}
pass[n]=NULL;
printf("parola este %s",pass);
46
int tablou[30];
int dim_tablou10;
citire_tablou(tablou, &dim_tablou);
//apelul funciei de citire tablou
afisare_tablou(tablou, dim_tablou);
//apelul funciei de afiare tablou
2. Cutarea secvenial
- operaia de cutare presupune parcurgerea element cu element a vectorului, n
ordinea dat de indeci
//Cutarea : determinarea poziiei pe care se afl valoarea cutat
int
49
z[k]=y[j]
k=k+1 //trecere la urmtoarea poziie n vectorul Z
j=j+1//trecere la urmtoarea poziie n vectorul Y
SfDac
SfCttimp
Dac i<n atunci //au mai rmas elemente n vectorul X
Pentru w de la i la n //se copiaz elementele rmase n X
z[k]=x[w]
k=k+1
SfPentru
SfDac
Dac j<m atunci //au mai rmas elemente n vectorul Y
Pentru w de la j la m //se copiaz elementele rmase n Y
z[k]=y[w]
k=k+1
SfPentru
SfDac
p=k-1 // sau p=n+m
Tiprete z[1],z[2],...,z[p]
SfAlgoritm //Interclasare
TABLOURI n-DIMENSIONALE
Adesea, este necesar prelucrarea datelor structurate n tablouri
multidimensionale. Un caz particular este cel al tablourilor bidimensionale,
cunoscute sub denumirea de matrice. Structura de matrice este reprezentat n
matematic prin:
x11
x
X = 21
...
x
m1
x12
...
x 22
...
...
xm 2
...
...
x1n
x2n
...
x mn
Afiarea valorilor unei matrici (n formatul uzual) este descris prin secvena
urmtoare :
printf(\n Matricea este: );
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
printf(%d, mat[i][j]);
printf(\n);
}
53
}
}
void main()
{
int n,m,p;
int a[DimMax][DimMax];
int b[DimMax][DimMax];
int c[DimMax][DimMax];
citire_matrice(&n,&m,a);
citire_matrice(&m,&p,b);
produs(n,m,p,a,b,c);
afisare_matrice(n,p,c);
suma(n,m,a,b,c);
afisare_matrice(n,m,c);
}
ALGORITMI DE SORTARE
Sortarea reprezint procedeul prin care o mulime de elemente este aranjat
dup o relaie de ordine dat.
Sortarea prin selecie
Fie x1,x2,...,xn un vector de n elemente.
Principiul este acela de a considera subvectorul x i,,xn i de a determina
minimul din aceast secven, urmnd ca minimul rezultat s se interschimbe cu
elementul xi. Procedeul se va repeta pentru oricare i=1,,n-1.
Algoritm SortareSelecie este:
Citete n, x1,x2,...,xn
Pentru i de la 1 la n-1
//determin poziia i valoarea minimului subvectorului xi,,xn
pozmin=i
min=xi
Pentru j de la i+1 la n
Dac xj <min atunci
pozmin=j
min=xj
SfDac
sfPentru
//interschimbare xi cu min
xpozmin=xi
xi=min
SfPentru
54
SfAlgoritm
Exemplu:
/*program care citeste un vector de numere intregi,
ordoneaza elementele vectorului prin metoda sortarii prin selectie
si tipareste vectorul ordonat */
#include <stdio.h>
void citireSir(int x[],int *n)
{
int i;
printf("\ndati n=");
scanf("%d",n);
for(i=0;i<*n;i++)
{printf("\nX[%d]=",i+1);
scanf("%d",&x[i]);
}
}
void tiparireSir(int x[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%d ",x[i]);
}
void SSort(int x[],int n)
{
int i,j,aux,poz;
for(i=0;i<n;i++)
{
//caut in sirul i .... n elementul minim
aux=x[i];poz=i;
for(j=i+1;j<n;j++)
if (x[j]<aux)
{aux=x[j];
poz=j;}
//interschimb cu x[i]
x[poz]=x[i];
x[i]=aux;
}
}
void main()
{
int a[100],n;
citireSir(a,&n);
SSort(a,n);
tiparireSir(a,n);
}
55
56
57
}
}while (cod!=0);
}
4.
5.
58
V.
{
int zi;
int luna;
int an;
} DataNasterii, DataAngajarii;
60
62
63
persoana:");
65
66
a->m = m_nou;
67
reduce(c);
68
VI.
LUCRUL CU FIIERE
Creare fiier
Deschidere fiier
Citire din fiier
Adaugare scriere n fiier
Actualizare fiier
Poziionare n fiier
tergere fiier
Observaii:
Dac nu este specificat complet calea fiierului, se consider implicit calea
curent.
Calea complet a unui fiier este specificat printr-un ir de caractere de forma:
Litera:\Dir1 \ Dir2 . \Dir
Unde:
- Litera poate fi A,B,C reprezint litera de identificare a discului (ex:
C harddisk, A floppy disk)
- Dir1, DIr2, sunt nume de subdirectoare
Dac specificm calea fiierului n apelul funciei open, toate caracterele \
trebuie dublate
Exemplu:
int df;
df = open (C:\\Borlandc\\bin\\text.cpp, O_RDWR);
70
4. Scrierea n fiier
Funcia write , prototipul acesteia se afl n fiierul io.h:
int write(int df, void *mem, unsigned lung);
unde:
- df descriptorul ataat fiierului (returnat de funcia open)
- mem pointer spre zona de memorie din care se preia informaia ce se va
scrie n fiier
- lung lungimea nregistrrii exprimat n numr de octei
Funcia write returneaz un ntreg reprezentnd numrul de octei scrii din
fiier, n general numrul specificat de parametrul lung. Dac cele dou valori sunt
diferite, este semnalat prin acesta un caz de eroare.
Exemplu: scrierea ntr-un fiier a unei secvene de caractere
int df, i;
char text[30] = Acest text se scrie in fisier!;
df=open(proba.txt,O_APPEND);
write(df, text, 30);
5. Poziionarea n fiier
Citirea i scrierea n fiiere se face n mod secvenial, ceea ce nseamn c
nregistrrile se scriu una dup alta pe suportul fiierului i citirea se face n
aceeiai ordine n care au fost scrise. Dac dorim ca accesul la nregistrri s se
71
fac ntr-o ordine diferit dect cea implicit se poate utiliza funcia lseek pentru
poziionarea n fiier:
long lseek(int df, long depl, int orig);
unde:
- df descriptorul de fiier
- depl deplasamentul exprimat n numr de octei
- orig este un numr ntreg ale crui valori semnific:
0 deplasamentul se consider de la inceputul fiierului
1 deplasamentul se consider din poziia curent
2 deplasamentul se consider de la sfritul fiierului
Funcia returneaz poziia fa de nceputul fiierului.
6. nchiderea fiierului
Funcia close avnd prototipul:
int close (int df); //df- descriptorul de fiier
Utilizarea funciei close implic includerea fiierului bibliotec io.h
Returneaz:
0 la nchiderea normal a fiierului
-1 n caz de eroare
Nivelul superior de prelucrare a fiierelor
Funcii de manipulare a fiierelor sunt definite n bibliotecile standard ale
limbajului. Prototipurile funciilor se gsesc n fiierul antet stdio.h. Fiierul care
se prelucreaz este identificat n mod unic printr-un pointer de fiier (uzual
denumim acest pointer de fiier pf).
n limbajul C exist tipul de date FILE (fiier) prin care se definete structura
datelor dintr-un fiier. n declaraiile de tip vom avea nevoie de o secven:
FILE *pf;
1.
Deschiderea unui fiier
Funcia de deschidere a unui fiier se numete: fopen i prototipul funciei este
urmtorul:
FILE *fopen(const char *nume, const char *mod);
n declaraiile de tip vom avea nevoie de o secven:
FILE *pf, fopen();
Argumentele funciei:
nume ir de caractere prin care se identific numele fiierului de pe disc
pe care dorim s-l accesm.
mod ir de caractere prin care se specific modul de acces la fiier.
Modurile valide de deschidere a fiierului sunt:
r - deschidere pentru citire
w - deschidere pentru scriere (informaia din fiier se pierde la
deschiderea n acest mod)
a - deschide pentru scriere (fr pierderea informaiei din fiier) sau
creeaz fiier
r+ - deschide pentru actualizare
72
pf=fopen(fis1.dat, r);
// deschid fiierul fis1.dat pentru a putea citi informaii
if (pf==NULL)
{
printf(\n Nu s-a putut deschide fisierul);
exit(1); //iesire din program
}
2.
nchiderea fiierului
Scrierea n fiier
4.
Poziionarea n fiier
74
76
VII.
Una dintre cele mai costisitoare resurse ale unui program este este memoria
utilizat. Programarea este o activitate n care, n afara corectitudinii programelor
elaborate, se urmrete i eficiena acestora, msurat prin timpul de execuie i
memoria utilizat. n scopul economiei resurselor, limbajele de programare ofer
programatorului instrumentele necesare unei bune gestionri a memoriei.
Alocarea memoriei pentru variabilele locale declarate ntr-o funcie se face n
etapa de execuie a programului, pe stiv, i nu este permanent. La ieirea din
funcie, stiva se cur, fapt pentru care datele alocate sunt distruse. Acest
mecanism de alocare este eficient i se denumete alocare dinamic. n schimb,
datele declarate global au un comportament diferit. Pentru variabilele globale,
memoria este alocat n faza premergtoare execuiei i zona de memorie
respectiv rmne alocat acestora pn la terminarea programului. Spre exemplu,
declararea global a unui tablou unidimensional de lungime maxim 2000
(<TipElement> tablou[2000]; ), face ca o zon de memorie considerabil s fie
blocat pentru aceast structur, indiferent de numrul elementelor folosite efectiv,
numr care n multe cazuri poate fi cu mult mai mic dect dimensiunea maxim
declarat. n astfel de situaii este preferabil o manier de alocare dinamic n
vederea unei gestionri economice a memoriei. Alocarea dinamic a memoriei
poate fi simplificat definit prin totalitatea mecanismelor prin care datelor le sunt
asignate zone specifice de memorie n faza de execuie a programelor.
Dezavantajele alocrii dinamice a memoriei constau n:
1. sarcina suplimentar a programatorului de a asigura i eliberarea
memoriei cnd nu mai are nevoie de datele respective
2. efectul fragmentrii memoriei care poate produce imposibilitatea
alocrii ulterioare a unei zone de memorie de dimensiune dorit.
Limbajul C/C++ ofer programatorului funcii standard prin care se realizeaz
alocarea, respectiv, dealocarea (eliberarea) memoriei. Funciile malloc i free au
prototipurile n fiierul antet: alloc.h i gestioneaz o zon de memorie special,
denumit memoria heap .
Funcia malloc:
void *malloc(size_t dimensiune)
Argumentul funciei este dimensiunea exprimat n octei, semnificnd
dimensiunea zonei alocate. Funcia returneaz n caz de succes adresa de memorie
a zonei alocate. Este posibil ca cererea de alocare s nu poat fi satisfcut dac nu
mai exist suficient memorie n zona de heap sau nu exist o zon compact de
dimensiune cel puin egal cu dimensiune. n caz de insucces, funcia malloc
returneaz NULL.
Funcia calloc:
void *calloc(size_t nrElemente, size_t dimensiune)
Efectul funciei: se aloc un bloc de memorie de mrime nrElemente *
dimensiune (aceast zon nu trebuie s depeasc 64 Ko octei din heap) i
coninutul blocului alocat este ters. n caz de succes, funcia returneaz adresa
77
78
79
info
adr
O structur care descrie compunerea unui element de acest gen este urmtoarea:
struct nod
{
//declaraii de date cmpuri ale informaiei
struct nod *adr; //adresa urmtorului nod
}
Convenim c informaia din noduri conine un cmp special (cheie) ale crui
valori sunt distincte pentru elementele aceleiai liste (cheie nu este un cmp
obligatoriu). Pentru a simplifica exemplele urmtoare, vom introduce un nou tip de
dat, tipul nodurilor, denumit TNod.
typedef struct nod
{
int cheie; //cmp cu valori unice pentru nodurile listei
//alte cmpuri
struct nod *urm; //adresa urmtorului nod
}Tnod;
Gestionarea unei liste de astfel de noduri necesit cunaterea adresei primului i
eventual al ultimului nod din list. Reinndu-se doar adresa primului nod, celelalte
noduri pot fi parcurse, accesate, prin urmrirea legturilor urm coninute n
nodurile curente.
Adresa fiecrui nod este coninut de nodul precedent, cu excepia primului nod
al listei, pentru care nu exist un nod precedent. Ultimul nod nu va referi niciun alt
nod urmtor, fapt care se marcheaz prin pointerul urm care devine Null. Figura
urmtoare sugereaz organizarea unei liste simplu nlnuite:
.
prim
ultim
NULL
82
1
prim
nou
void adaugare_prim()
{
tnod *p;
p=incarca_nod();
if (prim= =0) //lista vida
{prim=p;
ultim=p;
ultim->urm=0;
return;
}
p->urm=prim;
prim=p;
}
83
1
2
ultim
nou
void adaugare_ultim()
{ tnod *nou; nou=incarca_nod();
if (prim==0) //lista vida
{prim=nou;
ultim=nou;
ultim->urm=0;
return;
}
ultim->urm=nou;
ultim=nou; ultim->urm=0;
}
84
.
prev
curent
nou
Funcia urmtoare descrie operaia de inserare naintea unui nod cutat dup
valoarea cheii:
void adaugare_inainte()
{
int valoare;
printf("\ndati cheia de cautat:");scanf("%d",&valoare);
tnod *p,*prec, *nou;
p=prim; //iniializare nodul curent cu prim
while (p!=0)
{
if (p->numar!=valoare)
{//NU s-a gasit nc nodul
prec=p; //salveaz adresa precedentului n prev
p=p->urm; //trece la urmtorul element
}
else
{ //s-a gsit nodul de cheie dat
if (p==prim)
{//caz particular, nodul cutat este primul
adaugare_prim();return;
}
else
{
nou=incarca_nod();
nou->urm=p; //reface legturile
prec->urm=nou;
return;
}
}
}
}//sfrit funcie
85
.
p
p->urm
nou
Considernd nodul de cheie dat gsit: p, etapele inserrii noului nod sunt:
1. stabilete adresa urm a nodului nou ca fiind adresa nodul urmtor al lui p:
nou->urm=p->urm (1)
2. stabile;te adresa urm a lui p ca fiind adresa lui nou: p->urm=nou. Prin
aceast asignare s-a pierdut automat nlnuirea veche ntre p i p->urm
void adaugare_dupa()
{
int valoare; printf("\ndati cheia de
cautat:");scanf("%d",&valoare);
tnod *p,*nou;
p=prim;
while (p!=0)
{
if (p->numar!=valoare)
{//NU am gasit
p=p->urm;
}
else
{ //caz particular
if (p==ultim)
{
adaugare_ultim();return;
}
else
{
//alocare memorie i ncrcare nod cu inf.
nou=incarca_nod();
nou->urm=p->urm; //stabilirea legturilor
p->urm=nou;
return;
}
}
}
}
86
87
nodului
prev
void stergere_oarecare()
{
int valoare; printf("\ndati cheia de
cautat:");scanf("%d",&valoare);
tnod *p,*prev,*pvechi;
p=prim;
while(p!=0)
{
if (p->cheie==valoare)
{//s-a gsit nodul i acesta va fi ters
if (p==prim)
{
stergere_prim();
return;
} //caz particular
if (p==ultim)
{
stergere_ultim();
return;
} //caz particular
//cazul general
pvechi=p; //salvare adres nod curent
prev->urm=p->urm; //refacere legturi
free(pvechi); //eliberare memorie
return;
88
}
{//nu s-a gsit nc
prev=p; //salvarea adresei precedentului
p=p->urm; //trecere la urmtorul nod
}
}
}
4. tergerea listei
tergerea complet a listei se poate realiza prin apelul repetat al funciilor deja
construit de tergere a primului, respectiv, a ultimului nod pn cnd lista devine
vid (pointer-ul prim devine Null). Din punct de vedere al eficienei, variante
tergerii listei prin apelul funciei tergere_prim este preferat deoarece nu necesit
traversarea listei pentru fiecare operaie de tergere a unui nod.
O variant simpl dar greit de tergere a listei const n distrugerea capetelor
listei prim i ultim, fapt pentru care gestionarea ulterioar a listei ( accesarea i
prelucrarea nodurilor sale ) devine imposibil. Cu toate acestea, memoria rmne
alocat nodurilor intermediare. Prin aceasta s-a distrus doar mecanismul de
accesare a nodurilor, nu s-au distrus efectiv nodurile listei.
Funcia urmtoare este o variant de tergere a listei, prin tergerea repetat a
nodurilor din captul listei.
void stergere_lista()
{
tnod*p,*primvechi;
p=prim;
while(p!=0) //ct timp mai sunt noduri n list
{
if (prim==0)
{
printf("\nlista e complet stearsa");
return;
}
else
{
//sterg primul nod i actualizez prim
primvechi=prim;
prim=prim->urm;
free(primvechi);
}
}
}
89
IX.
info
Avantajul utilizrii listelor dublu nlnuite rezult din posibilitatea parcurgerii
(traversrii) listei n ambele sensuri: de la primul la ultimul, respectiv, de la ultimul
la primul nod. Acest lucru permite o manipulare mai flexibil a nodurilor listei
Structura unui nod al listei dublu nlnuite este urmtoarea:
struct nod
{
//declaraii de date cmpuri ale informaiei
struct nod *urm; //adresa urmtorului nod
struct nod *prec; //adresa nodului precedent
}
n exemplele urmtoare vom utiliza un tip de date utilizator prin care specificm
tipul nodurilor listei:
typedef struct nod
{int cheie;
..//alte cmpuri
struct nod *pre;
struct nod* urm;
}tnod;
Ca i la lista simplu nlnuit, principalele operaii sunt:
- crearea;
- accesul la un nod; parcurgerea listei
- adugarea unui nod;
- tergerea unui nod,
- tergerea listei.
Gestionarea unei liste dublu nlnuite se face n maniera similar listelor simplu
nlnuite prin adresele nodurilor prim i ultim. n plus, nodul prim se marcheaz
prin stabilirea adresei precedentului su la Null: prim->prec=0.
tnod *prim,*ultim;
Figura urmtoare sugereaz maniera de organizare a unei liste dublu nlnuite
(alocate dinamic):
ultim
prim
90
void tiparireInversa()
{
tnod *p;
if (prim==0)
{printf("\nLista e vida!");
return;
91
}
p=prim; //iniializare adres nod curent
while(p!=0)
{
printf("\n %d",p->cheie);
p=p->urm; //trece la urmtorul nod
}
}
}
p=ultim; //iniializare adres nod curent
while(p!=0)
{
printf("\n %d",p->cheie);
p=p->prec; //trece la precedentul nod
}
}
nou
Observaie: n cazul n care lista este naintea adugrii unui nod nou, efectul
operaiei const n obinerea unei liste cu un singur element, fapt pentru care
capetelor listei prim i ultim sunt confundate i este necesar tratarea acestui caz
particular.
void adaugare_prim()
{
tnod *nou; p=incarca_nod();
if (prim==0)
{
prim=nou;ultim=nou;
prim->prec=0;ultim->urm=0;
}
else
{
nou->urm=prim; //pasul 1
92
ultim
2
3
void adaugare_ultim()
{
tnod *nou;nou=incarca_nod();
if (prim==0)
{
prim=nou;ultim=nou;
prim->prec=0;ultim->urm=0;
}
else
{
nou->prec=ultim; //(1)
ultim->urm=nou; //(2)
ultim=nou; //(3)
ultim->urm=0; //(4)
}
}
93
nou
p->prec
nou
94
{
adaugare_prim();
return;
}
}
}
}//sfarsit functie
95
96
2
p->urm
p->prec
97
tergerea listei
tergerea complet listei dublu nlnuit se poate face cu acelai efort de calcul
prin apelarea repetat a funciei stergere_prim sau stergere_ultim. tergerea
capetelor listei nu asigur eliberarea memoriei ocupate de nodurile intermediare.
Exemplu:tergerea listei prin apelul repetat al funciei de tergere a primului nod.
void stergere_lista()
{
while(prim!=0)
stergere_prim();
}
98
X.
Lista circular este o list (simplu sau dublu) nlnuit cu proprietatea c toate
nodurile sunt echivalente, respectiv, nu exist noduri speciale care nu conin adresa
nodurilor succesoare sau predecesoare. Aceste noduri speciale - denumite capetele
listei au fost utilizate n gestionarea listelor simplu i dublu nlnuite. O list
circular va fi gestionat prin alte mecanisme dect cele bazate pe meninerea
adreselor speciale prim i ultim.
LISTA SIMPLU NLNUIT CIRCULAR
ntr-o list circular simplu nlnuit toate nodurile conin adresa urmtorului
nod. Structura nodului este similar celei prezentate la capitolul dedicat listelor
simplu nlnuite:
typedef struct nod
{
int cheie; //cmp cu valori unice pentru nodurile listei
//alte cmpuri
struct nod *urm; //adresa urmtorului nod
}Tnod;
Organizarea unei liste circulare cu noduri de acest tip este sugerat de figura
alturat.
Orice list simplu nlnuit gestionat prin pointer-ii prim i ultim se poate
transforma n list circular printr-o operaie elementar de asignare:
ultim->urm=prim
Prin operaia anterioar s-a stabilit faptul c ultimul nod al listei iniiale va
conine adresa primului nod al listei, ceea ce conduce la o structur de list
circular a crei gestionare poate fi efectuat prin adresa pointer-ului prim, ns
fr ca acesta s semnifice adresa unui capt al listei, ci doar adresa unui nod
oarecare.
Spre deosebire de listele simplu nlnuite la care este suficient cunoaterea
adresei primului nod i, eventual, pentru simplificarea prelucrrilor, i a adresei
ultimului nod, ntr-o list circular, cunoaterea adresei oricrui nod din
compunerea listei este suficient pentru a putea gestiona aceast structur. Astfel,
gestionarea unei liste circulare se face prin unui pointer care refer oricare nod al
listei:
Tnod *pLC; //pointer la lista circular
99
.............
100
101
102
Observaii:
- nodul referit de pLC este ultimul nod verificat n etapa de cutare
- instruciunea decizional if (p->cheie==valoare) este redundant, dat
fiind faptul c o condiie precedent verificat situaia opus i provoac
revenirea din funcie. Din motive de lizibilitate i nu de optimizare a
codului am convenit s furnizm o variant explicit a funciei pentru o
urmrire uoar a etapelor descrise.
2. Inserarea unui nod nou dup un nod precizat de cheie presupune:
- cutarea nodului de cheie dat
- inserarea propriu-zis
Dac prima etap s-a ncheiat cu succes, se cunoate adresa nodului de cheie
dat p, dar i adresa urmtorului nod (datorit legturii urm) p->urm. Nodul nou
va fi inserat ntre cele dou noduri de adrese cunoscute. Nu mai este necesar
determinare altei adrese dect cea a nodului cutat dup valoarea cheii.
Funcia urmtoare este o posibil implementare a operaiei discutate:
void inserareDupa(int valoare)
{
tnod *nou;
tnod *p; //pointer care refera nodul curent
if (pLC==0) return; //lista este vida, nu are sens continuare
operatiei
p=pLC;
do
//cautarea nodului p
{
if (p->cheie==valoare) //s-a gasit nodul
break; //iesire din instructuinea repetitiva
//p este adresa nodului gasit
p=p->urm; //trece la urmatorul nod
}while(p!=pLC);
if (p->cheie!=valoare) //cautarea s-a incheiat cu Insucces
return;
//dac s-a ajuns n acest punct, cautarea s-a incheiat cu
//Succes
103
Observaie: nodul referit de pLC este primul nod verificat n etapa de cutare.
tergerea unui nod precizat de valoarea cheii
Operaia de tergere a nodului precizat printr-o cheie presupune:
- cutarea nodului i reinerea adresei precedentului su ()
- tergerea nodului: refacerea legturilor i eliberarea memoriei
Cazurile particulare ale operaiei se trateaz diferit:
a. lista este vid nainte tergerii
b. lista devine vid dup tergere
c. nodul de ters este chiar pLC
Convenim c n cazul particular c. (nodul ce se va terge este chiar nodul referit
de pointer-ul pLC i lista nu devine vid), pLC va referi nodul precedent celui
ters.
O funcie C care descrie operaia de tergere este urmtoarea:
void steregereNod(int valoare)
{
tnod *p,*prev;
//p - adresa nodului curent
//prev - adresa precedentului nodului curent
if (pLC==0) return; //lista este vida, cazul particular (a.)
p=pLC;
do
//cautarea nodului p
{
prev=p; //retine precedentul nodului curent
p=p->urm; //trece la urmatorul nod
if (p->cheie==valoare) //s-a gasit nodul
break; //iesire din instructuinea repetitiva, p este adresa
nodului gasit
}while(p!=pLC);
if (p->cheie!=valoare) return; //nu s-a gasit nodul
104
105
Observaie: primul nod eliberat este cel referit de pLC, fapt pentru care cnd
condiia p==pLC devine adevrat se indic revenirea n punctul de plecare a
pointer-ului p ceea ce semnific faptul c toate nodurile au fost terse (inclusiv
nodul referit de pointer-ul special pLC) i lista este vid.
LISTA DUBLU NLNUIT CIRCULAR
Lista circular dublu nlnuit este gestionat printr-un pointer la un nod
oarecare. Structura nodului este cea prezentat la listele dublu nlnuite i conine:
zona de informaii, adresa precedentului i adresa nodului urmtor.
Operaiile specifice: creare, inserare nod, tergere nod, tergere list,
parcurgere, cutare, sunt similare operaiilor descrise cu liste circulare simplu
nlnuite. Diferenele semnificative apar la procedurile de inserare naintea unui
nod precizat i tergerea unui nod oarecare, care se simplific prin existena unei
legturi spre nodurile precedente.
Transformarea unei liste dublu nlnuite n list circular se realizeaz prin
legarea capetelor prim i ultim, n ambele sensuri:
ultim->urm=prim;
prim->prec=ultim;
STIVE. COZI.
Stiva reprezint un caz special de lista liniara n care intrrile si ieirile se fac
la un singur capt al ei. Organizarea structurilor de date de tip stiv se poate face
n dou maniere:
- secvenial - elementele stivei sunt memorate la adrese
consecutive
- nlnuit elementele stivei nu ocup adrese consecutive,
fiecare element conine o legtur spre urmtorul element.
Prin organizarea secvenial nu se poate face economie de memorie, fapt pentru
care n general se practic organizarea nlnuit cu alocare dinamic a stivelor.
Structura de stiv se remarc prin operaiile specifice: push i pop,
corespunztoare adugrii unui element, respectiv, tergerii unui element n/din
vrful stivei. Principiul de funcionare al stivei este cel cunoscut sub denumirea de
LIFO (Last In First Out ultimul intrat, primul ieit).
106
vrful
info
info
info
info
adr
info
adr
info
adr
info
baza
info
baza
Practic, stiva este o list simplu nlnuit pentru care operaiile specifice se
limiteaz la urmtoarele:
- creare stiv vid
- adugare element (push)
- tergere element (pop)
- terge lista (clear)
- accesare fr eliminare - a elementului din vrful stivei
n plus fa de operaiile enumerate anterior sunt posibile implementate operaii
de verificare:
- verific dac stiva este plin
- verific dac stiva este goal
Gestionarea stivei se face n mod similar listei nlnuite prin capetele prim i
ultim. La nivel abstract, o stiv are o baz a sa i un vrf, ceea ce convine unei
asocieri a nodurilor referite de prim i ultimi cu cele dou elemente specifice:
- baza stivei corespunde nodului prim i vrful stivei corespunde
nodului ultim
n aceast abordare, operaiile push i pop se traduc prin operaiile de:
- adugare a unui nou nod dup ultim (adugare n vrful stivei)
- tergere ultim (tergere din vrful stivei)
Privitor la eficiena operaiilor descrise ntr-un capitol anterior, ne reamintim c
operaia de adugare a unui nou element dup cel referit de pointer-ul ultim
necesita o parcurgere prealabil a listei. n schimb, adugarea unui nou nod
naintea celui referit de prim este mai puin costisitoare. Din aceste considerente, se
practic o inversare a rolurilor celor dou capete ale stivei, pentru a obine operaii
mai eficiente:
- baza stivei corespunde nodului ultim i vrful stivei
corespunde nodului prim
Astfel, operaiile push i pop se vor traduce prin:
- adugare a unui nou nod nainte de prim (adugare n vrful
stivei)
- tergere prim (tergere din vrful stivei)
107
Coada este un alt caz special de list nlnuit bazat pe principiul FIFO (First
In First Out primul intrat, primul ieit). Acest principiu arat c primul element
introdus n list este i primul care va fi ters. O structur de acest gen are dou
capete, denumite sugestiv: cap i coad.
Operaiile primare cu cozi sunt:
- creare stiv vid
- adugare element n coad
- tergere element din cap
- terge lista (clear)
Spre deosebire de stiv, adugarea i tergerea unui element se execut n
capetele diferite ale cozii.
Ca i n cazul stivelor, organizarea unei cozi poate fi fcut n mod secvenial
(static) prin intermediul tablourilor unidimensionale sau dinamic prin liste
simplu nlnuite. Cea de-a doua variant este de preferat din raiuni economice.
prim
ultim
0
Coada este astfel o list nlnuit ale crei capete referite prin prim i ultim
semnific capul i coada structurii, ceea ce permite organizarea n dou maniere:
- prim refer capul listei i ultim refer coada listei
- ultim refer capul listei i prim refer coada listei
Conform celor dou abordri enumerate anterior, operaiile de adugare i
scoatere elemente n/din lista FIFO se traduc prin:
- adugare dup nodul ultim i tergere nod prim
- adugare nainte de prim i tergere nod ultim
Constatm c spre deosebire de stive, ambele abordri sunt eficiente, astfel nct
alegerea oricrei variante este posibil. Printr-o convenie, adugarea unui nod se
face dup ultimul nod (coada) al listei, iar scoaterea din list a unui nod este
implementat prin tergerea nodului prim (cap).
108
XI.
ARBORI
Nod intern
Nod intern
Terminal
Nod intern
Nod intern
Terminal
Terminal
Terminal
Terminal
Nivelul 2
Nivelul 3
Nivelul 4
Dac numrul de fii ai fiecrui nod din compunerea unui arbore este 0,1 sau 2,
atunci arborele respectiv este numit arbore binar. Structura unui nod al arborelui
binare conine:
- zona de informaii
- legtur spre fiul stng
- legtur spre fiul drept
ntr-un arbore binar, exist posibilitatea ca una sau ambele legturi ale unui
printe spre fiii si s fie nule. Nodurile terminale au ambele legturile nule.
Anumite noduri interne pot s aib doar un fiu, astfel nct legtura spre cellalt fiu
este nul.
Importana studierii arborilor binari este dat de multitudinea de aplicaii
practice n care se face uz de aceast structur de date. n plus, un arbore poate fi
transformat i reprezentat prin arbore binar. Transformarea presupune etapele
urmtoare:
1. se stabilete legtur ntre fraii de acelai printe
2. se suprim legturile dinspre printe, cu excepia legturii cu
primului fiu
Exemplu:
Fie arborele oarecare din figura urmtoare:
1
7
Figura 1 Arbore oarecare
111
Parcurgerea arborilor
Parcurgerea (traversarea) arborilor presupune obinerea unei liste a nodurilor
arborelui. n funcie de ordinea n care sunt considerate nodurile arborelui, avem
mai multe tipuri de traversare a arborilor.
-
112
Absena unui fiu (stng sau drept) se marcheaz prin valoarea Null (0) a
pointer-ului corespunztor.
Gestionarea unui arbore binare este posibil prin adresa nodului rdcin:
Tnod *rad;
Operaiile specifice arborilor binari sunt:
1. crearea arborelui
2. inserarea unui nod
3. tergerea unui nod
4. tergerea arborelui
5. accesarea unui nod (cutarea)
6. parcurgerea unui arbore
Operaia de creare a arborelui binar necesit iniializarea nodului rdcin:
rad=0 i adugarea (inserarea) ulterioar a noilor noduri dup o anumit regul.
Operaiile de inserare i accesare a nodurilor dintr-un arbore binar presupun
existena unui criteriu care se desprinde din specificaiile problemei concrete de
rezolvat. Aceste criterii fiind variate, rezolvarea problemelor cu arbori binare se
poate simplifica prin considerarea unei funcii verific care are doi parametri:
pnod1 i pnod2 adresele a dou noduri i returneaz valoare -1,0 sau 1 cu
semnificaia:
A) -1 dac pnod2 este adresa unui nod care poate fi accesat sau inserat n
stnga nodului adresat de pnod1
B) +1 dac pnod2 este adresa unui nod care poate fi accesat sau inserat n
dreapta nodului adresat de pnod1
C) 0 dac pnod2 refer un nod care NU poate fi accesat sau inserat n
subarborii stng sau drept ai nodului referit de pnod2.
Prototipul acestei funcii ajuttoare este:
int verifica(Tnod *pnod1 , Tnod * pnod2);
Inserarea unui nou nod presupune:
- determinarea locului n care va fi inserat noul nod
- alocarea de memorie i ncrcarea cu informaii a nodului nou
- stabilirea legturilor n arbore
n privina locaiei n care poate fi inserat nodul, aceasta este dat de criteriul
specificat de problema concret i se disting urmtoarele cazuri:
- n poziia rdcinii
- ca nod terminal
- ca nod intern
tergerea unui nod oarecare presupune:
- determinarea locaiei sale
- refacerea legturilor n arbore
- eliberarea memoriei alocate nodului ters
tergerea ntregului arbore necesit tergeri repetate ale nodurilor sale pn
cnd rdcina devine vid.
Parcurgerea unui arbore binare poate fi fcut n trei moduri:
113
114
115
116
117
118
c. nodul cutat nu este nod terminal, dar are doar un subarbore stng
- leag printele de subarborele stng al nodului de ters:
o dac nodul este legat n stnga printelui, atunci
parinte->st=p->st cazul c.1
o dac nodul este legat n dreapta parintelui, atunci
parinte->dr = p->st cazul c.2
- elibereaz nodul
d. nodul cutat p nu este nod terminal, dar are doar un subarbore drept
- leag printele de subarborele drept al nodului p:
o dac nodul este legat n stnga printelui, atunci
parinte->st=p->dr cazul d.1
o dac nodul este legat n dreapta parintelui, atunci
parinte->dr = p->dr cazul d.2
- elibereaz nodul
e. nodul cutat p nu este terminal i are ambii subarbori (legturile stnga i
dreapta sunt nenule).
O alt manier de stabilire a cazurilor de tergere n arborele binar este dat de
ordinul nodului ce va fi ters:
Dac nodul are ordinul 0 sau 1 (are maxim un subarbore) este util s grupm
cazurile descrise mai sus a,b,c,d ntr-o singur tratare: printele nodului p va
conine adresa subarborelui lui p (stng sau drept), chiar dac acest subarbore este
vid (nodul p este terminal).
Dac ordinul nodului p este 2, se va trata cazul e.
Cazul e este cel mai complex caz de tergere a unui nod. Numim predecesor al
nodului p, cel mai din dreapta nod din subarborelui stng al lui p. Numim succesor
al nodului p, cel mai din stnga nod din subarborelui drept al lui p. Att
predecesorul ct i succesorul unui nod oarecare, sunt noduri terminale n arborele
dat.
Exemplu:
Nodul
de sters
Predecesor
Nodul
de sters
5
Succesor
120
Nodul
de sters
2
Predecesor
Nodul
de sters
2
Predecesor
n acest caz, pentru nodul de ters se va actualiza legtura stng. Dup operaia
de tergere, arborele devine:
121
122
123
124
125
XII.
6
4
3
7
O muchie de la vrful x la vrful y este notata cu perechea ordonata (x, y), dac
graful este orientat i n mod uzual este folosit termenul de arc, si cu mulimea
{x, y}, dac graful este neorientat. n reprezentarea grafic, arcele (x,y) sunt
marcate prin sgei de la extremitatea iniial x la cea final y, iar muchiile prin
segmente.
ntr-un graf orientat, existena unui arc de la vrful x la vrful y nu presupune i
existena arcului de la y la x. n grafurile neorientate, dac exist muchie ntre x i
y, atunci aceasta este i muchie ntre vrfurile y i x. Vrfurilor unui graf li se pot
ataa informaii numite uneori valori, iar muchiilor li se pot ataa informaii numite
costuri.
Urmtoarele noiuni sunt specifice grafurilor:
Dou vrfuri unite printr-o muchie se numesc adiacente.
Un drum este o succesiune de muchii de forma:
(x1, x2), (x2, x3), ..., (xn-1, xn) n graf neorientat
sau de forma
{x1, x2}, {x2, x3}, ..., {xn-1, xn} n graf neorientat
Un lan se definete ca o succesiune de vrfuri x 1, x2, x3, xn n care oricare
dou vrfuri sunt adiacente.
ntr-un drum simplu muchiile care l compun sunt distincte.
ntr-un drum elementar vrfurile care l compun sunt distincte.
Lungimea drumului este egala cu numrul muchiilor care l constituie.
Un lan elementar al grafului G care conine toate vrfurile grafului se numete
lan hamiltonian. Determinarea unui lan hamiltonian al grafului este o problem
foarte popular cunoscut ca Problema Comis Voiajorului rezolvat prin metoda
Greedy.
Un ciclu este un drum care este simplu i care are drept capete un acelai vrf.
Un graf fr cicluri se numete graf aciclic.
Un subgraf al lui G este un graf G=(X', '), unde X' X, iar ' este formata din
muchiile din care unesc vrfuri din X'.
126
Parcurgerea grafurilor
Parcurgerea unui graf presupune vizitarea intr-o anumita ordine nodurilor
grafului, o singura dat fiecare. n functie de ordinea de parcurgere a vrfurilor
exista 2 metode de parcurgere:
1.
2.
Inserare(C,z)
SfDac
SfPentru
SfCttimp
SfSubalgoritm
Observaie: marcarea unui vrf ca fiind vizitat sau nevizitat se poate realiza prin
folosirea unui tablou tab[1n] ale crui elemente sunt asociate vrfurilor
grafului i au valori binare cu semnificaia: tab[i]=1 vrful i este vizitat, respectiv,
tab[i]=0 vrful i este nevizitat
2. Parcurgerea n adncime presupune vizitarea vrfului iniial x S i marcarea sa
ca fiind vizitat, apoi se alege un vrf x, adiacent lui x S i se aplic aceiai procedur
recursiv, avnd ca punct de plecare vrful x. Procedura de parcurgere n
adncime a vrfurilor unui graf se preteaz la o implementare recursiv. La
terminarea procedurii curente (la revenirea din apelul recursiv), dac exista un alt
vrf adiacent vrfului curent x, care nu a fost vizitat, apelam din nou procedura etc.
Dac toate vrfurile adiacente lui x au fost marcate ca vizitate se termin vizitarea
vrfului x.
Subalgoritm DepthFirst (x) este:
*Marcheaz x ca vizitat
Pentru fiecare vrf y adiacent lui x
Dac y este nevizitat atunci
Cheam DepthFirst (y)
SfDac
SfPentru
SfSubalgoritm
Observaie: Varianta nerecursiv a subalgoritmului DepthFirst se realizeaz prin
utilizarea unei structuri de date de tip stiv.
Subalgoritmii BreadthFirst i DepthFirst sunt apelai din algoritmul Parcurgere:
Algoritm Parcurgere(G) este
Pentru fiecare x din X
*Marcheaz x ca nevizitat
SfPentru
Pentru fiecare x din X
Dac x este nevizitat atunci
Cheam BreadthFirst (x) sau Cheam
DepthFirst (x)
SfDac
SfPentru
SfAlgoritm
128
129
130
# P1 de dimensiunea n1,
# P2 de dimensiune n2,
...
# Pk de dimensiune nk
Cheam DivideEtImpera(P1,n1,S1)
Cheam DivideEtImpera(P2,n2,S2)
...
Cheam DivideEtImpera(Pk,nk,Sk)
#combin soluiile pariale S1,S2,...,Sk i obine S_formal
SfDac
SfSubalgoritm
Propoziiile nestandard din descrierea subalgoritmului nu pot fi detaliate dect n
funcie de problema rezolvat.
Pentru a rezolva problema global P, apelul subalgoritmului se va face pentru
parametrii actuali P, n i S:
Cheam DivideEtImpera(P,n;S)
Problema 1: Se cere determinarea maximului dintr-un vector de n valori numerice.
Analiza problemei:
Determinarea celui mai mare element dintr-un vector poate fi privit ca o
problem de determinare a maximului dintre dou valori intermediare,
reprezentnd maximele celor dou subiruri obinute prin mprirea irului iniial
n dou pri egale:
x1 ,x2 ,x3 , , xk+1 , xk , xk+1 ,,xn-2 ,xn-1 ,xn
Maxim
x1 ,x2 ,x3 , , xk+1 , xk
Maxim1
Maxim2
Maxim= maxim(Maxim1,Maxim2)
Fiecare subir din mprirea anterioar se va putea mpri din nou n dou pri
de dimensiuni apropiate. Acest proces de mprire se va ncheia cnd un subir de
elemente nu mai poate fi mprit, respectiv cnd dimensiunea acestuia s-a redus la
1. n acest moment, subproblema devine elementar, deoarece maximul
elementelor unui vector format dintr-un singur element este nsi elementul
respectiv.
132
133
return max2;
}
void main()
{
int a[NMAX],dim;
citire(a,&dim);
int max;
max=maxim(a,0,dim-1);
printf("Maximul este: %d",max);
}
Problema 2: Problema turnurilor din Hanoi. Se dau 3 tije simbolizate prin A,B,C.
Pe tija A se gsesc n discuri de diametre diferite, aezate de jos n sus n ordine
cresctoare a diametrelor. Se cere sa se mute toate discurile de pe tija A pe tija B,
utiliznd ca tija intermediara tija C, respectnd urmtoarele reguli:
- la fiecare pas se muta un singur disc ;
- nu este permis sa se aeze un disc cu diametrul mai mare peste un disc cu
diametrul mai mic.
Analiza problemei:
Cazul elementar al problemei const n existena unui singur disc pe tija A,
ceea ce reduce rezolvarea la o mutare a discului de pe tija A pe tija B.
Pentru n=2 (dou discuri), rezolvarea problemei const n 3 mutri,
folosind tija intermediar C:
- mut discul mai mic de pe tija A pe tija C
- mut discul mai mare de pe tija A pe tija B
- mut discul mic de pe tija C pe tija B
Generaliznd, pentru un numr oarecare n de discuri situate pe tija A,
rezolvarea problemei se reduce la parcurgerea urmtoarelor etape:
- mut primele n-1 discuri de pe A pe C, utiliznd tija B ca intermediar
- mut discul rmas de pe tija A pe tija B
134
136
//avans la stanga
while ((i<dr) && (x[i]<M)) i++;
//avans spre dreapta
while ((j>st) && (x[j]>M)) j--;
if (i<j)
{//interschimb x[i] cu x[j]
138
139
140
141
s4
s3
s2
t3
t2
t1
s1
145
s4
s3
t3
t2
s2
s1
t1
i =1,..., n
s i i ti ,
Date de ieire B
Fie B={}
*Ordoneaz cresctor A dup valorile ti
Pentru i de la 1 la n
Dac *(nu exist aj B, astfel nct ai se suprapune lui aj) atunci
B=B {ai}
SfDac
SfPentru
SfAlgoritm
Observaie: Dou activiti ai i aj se consider c nu se suprapun ddac este
verificat condiia:si>tj sau sj>ti.
146
XV.
i =1,..., n , xi Ai
unde S = A1 A2 .. An , i Ai sunt mulimi finite nu neaprat distincte.
Cerina problemei este determinarea tuturor soluiilor posibile, care satisfac
anumite condiii specifice problemei (denumite condiii interne).
O abordare simplist ar genera toate soluiile posibile - elementele produsului
cartezian S = A1 A2 .. An , i ulterior s-ar verifica aceste soluii n vederea
identificrii acelora care verific condiiile interne. Abordarea aceasta este
neeficient. Generarea tuturor soluiilor implic memorie i timp suplimentar.
Backtracking este o alternativ inspirat de rezolvare a problemelor de acest gen.
Metoda evit generarea tuturor soluiilor posibile, soluiile finale se obin prin
alegerea succesiv de elemente din mulimile A1 , A2 , , An cu posibilitatea
revenirii asupra unei alegeri dac aceasta nu a condus la obinerea unei soluii
finale.
Algoritmii Backtracking pornesc cu o soluie iniial reprezentat de vectorul
vid. Ulterior acesta va fi mrit prin adugarea elementelor x k din mulimea
corespunztoare Ak , treptat pentru k=1,2,. Dac vectorul parial
{x1 , x 2 ,..., x k } verific anumite condiii de validare, deduse din condiiile
interne, se va continua cu augmentarea vectorului curent prin adugarea unui nou
element x k +1 din mulimea corespunztoare Ak +1 . Dac vectorul nu
ndeplinete condiiile de validare se ncearc un nou element din mulimea Ak i
se reverific condiiile de validare. Exist posiblitatea ca Ak s nu mai conin
alte elemente care au rmas neverificate, ceea ce produce o revenire la o decizie
anterioar i ncercarea unui alt element pe poziia anterioar k-1 din vector.
147
Asupra vectorului soluie se acioneaz prin doar dou operaii: adugare nou
element dup ultimul adugat, respectiv, eliminare ultim element adugat. Aceste
operaii corespund unor operaii cunoscute: push i pop; astfel, vectorul soluie
funcioneaz pe principiul unei stive.
O problem se identific ca o problem rezolvabil prin metoda Backtracking
dac putem identifica urmtoarele aspecte din specificaiile sale:
1. spaiul soluiilor este un produs cartezian S = A1 A2 .. An
2. soluia
probleme
poate
fi
reprezentat
ca
un
vector
x = {x1 , x 2 ,..., x n } S
3. exist un set de condiii prin care putem decide dac o soluie parial
dat de vectorul {x1 , x 2 ,..., x k } , k n este valid condiiile de
validitate
4. exist un set de condiii prin care putem decide dac o soluie parial
este final condiiile de finalitate
5. soluia (vectorul) se poate construi pas cu pas, astfel nct dac
{x1 , x 2 ,..., x k } este valid, are sens completarea vectorului cu un
element pe poziia k+1.
Considerm soluia parial {x1 , x 2 ,..., x k } reprezentat ca stiv n imaginile
alturate:
Cazul 1
Nivelul k+1
Nivelul k
xk+1
Nivelul k+1
xk+1
xk
Nivelul k
xk
Nivelul k-1
xk-1
Nivelul k-1
xk-1
Nivelul 2
x2
Nivelul 2
x2
Nivelul 1
x1
Nivelul 1
x1
Cazl 2.1
Nivelul k+1
Nivelul k
xk+1
Nivelul k+1
xk+1
xk
x k*
Nivelul k
Nivelul k-1
xk-1
Nivelul k-1
xk-1
Nivelul 2
x2
Nivelul 2
x2
Nivelul 1
x1
Nivelul 1
x1
148
Cazul 2.2
Nivelul k+1
Nivelul k
xk+1
Nivelul k+1
xk+1
xk
Nivelul k
Nivelul k-1
xk
Nivelul k-1
xk-1
xk-1
Nivelul 2
x2
Nivelul 2
x2
Nivelul 1
x1
Nivelul 1
x1
SfDac
SfFuncie
Rezolvare1: Programul C pentru rezolvarea problemei damelor (varianta
nerecursiv)
#include <stdio.h>
#include <math.h>
int stiva[100]; //solutia se construieste in stiva
int nrsolutii;
int n;
int valid(int k)
{
int i;int cod=1;
for(i=1;i<=k-1;i++)
if ((stiva[k]==stiva[i]) ||
(abs(k-i)==abs(stiva[k]-stiva[i]) ) )
cod=0;
return cod;
}
int final(int k)
{
if (k==n)
return 1;
else
return 0;
}
void tipareste()
{
int i;
printf("\n");
for(i=1;i<=n;i++)
printf("%d ",stiva[i]);
}
void main()
{
nrsolutii=0;
int i;
int gasit;
printf("dati n:"); scanf("%d",&n);
int k=1; //incepem de la primul nivel;
while(k>=1)
{
gasit=0;
for(i=stiva[k]+1;(i<=n) ;i++)
{
stiva[k]=i;
152
153
a( 2,1) a( 2,2 )
L =
, a( i, j) {0,1}
a( m,1) a( m,2 )
a( m, n )
scurte dect altele. Condiia ca un vector de perechi (i,j) s fie soluie final a
problemei este adevrat dac ultima camer (poziie) parcurs se afl la marginea
labirintului: pe prima sau ultima coloan, sau, pe prima sau ultima linie a matricei
de reprezentare.
Condiiile de validitate se deduc din specificaiile problemei. Drumul persoanei
poate continua ntr-o nou poziie (i,j) dac valoarea elementului de pe linia i i
coloana j din matricea L este 0 (drum liber). Mai mult, pentru a evita parcurgerea
acelorai camere de mai multe ori, se impune restricia ca poziiile prin care trece
persoana s fie distincte. Astfel, persoana poate trece ntr-o camer vecin dac: nu
este zid (camer liber) i nu a mai fost traversat.
De asemenea, tot specificaiile problemei ne indic faptul c mutarea ntr-o
nou camer se va face doar prin 4 micri posibile n poziiile vecine: stnga,
dreapta, sus i jos
n continuare este descris algoritmul backtracking de rezolvare a problemei
labirintului (varianta recursiv), punnd n eviden condiiile de validitate i
finalitate prin subalgoritmi corespunztori:
Funcie Valid(k)
// k-nivelul stivei
Fie (i,j) poziia memorat n Stiva[k]
Dac (a(i,j)=0) atunci
//camer liber
Valid=Adevrat
Pentru l de la 1 la k-1
Dac (i, j ) = Stiva[l ] atunci
//camera a mai fost traversat n drumul memorat n stiv
Valid=Fals
SfDac
SfPentru
Altfel
//camera ocupat - zid
Valid=Fals
SfDac
SfFuncie
Funcie Final (k)
Fie (i,j) poziia memorat n Stiva[k]
Dac i=1 sau j=1 sau i=m sau j=n atunci
Final=Adevarat
Altfel
Final=Fals
SfDac
SfFuncie
Subalgoritm Labirint(k) este:
Pentru fiecare vecin posibil (i,j) al camerei salvate n Stiva[k-1]
Stiva[k]=(i,j) //operaia push
155
156
157
158
Rezolvarea naiv a acestei probleme ar aceea prin care se vor determina toate
subirurile cresctoare urmnd ca ulterior s se extrag acela care are lungime
maxim. Rezolvarea aceasta este corect, ns neeficient prin necesitatea de a
construi i reine toate subirurile lui irului dat.
O soluie ingenioas are fi s se calculeze i s se rein ntr-o tabel doar
lungimile tuturor subirurilor cresctoare, fr a le genera i pe acestea.
n exemplul anterior, subirurile cresctoare i lungimile acestora sunt:
Subiru
l
7
123
45
23
5
3
Lungimea
1
3
2
2
1
1
Se poate observa c dac subirul 3 are lungime 1, subirul care l conine are
lungimea mai mare cu 1, respectiv 2 (1+1) i subirul maximal 1 2 3 are lungime 3
(2+1).
Astfel: Dac subirul 1 2 3 este subirul optimal cresctor de lungime maxim
(3), care ncepe cu 1, atunci:
2 3 este subirul optimal cresctor de lungime maxim (2) care ncepe cu 2 i
3 este subirul optimal cresctor de lungime maxim (1) care ncepe cu 3
Generaliznd, dac i1 , i 2 ,..., i n este subirul optimal cresctor de lungime n
care ncepe cu i1 : atunci:
- i 2 , i 3 ,..., i n este subirul optimal optimal de lungime n-1 care ncepe cu
i2
- i 3 , ,..., i n este optimal de lungime n-2 care ncepe cu i3
- ..
- i k , i k +1 ,..., i n este subirul optimal de lungime maxim care ncepe cu
ik, k > 1
Prin reformularea observaiilor anterioare se identific principiul optimalitii n
varianta 1, fapt pentru care se va aplica metoda nainte:
Etapa_1&2: se vor calcula pe baza relaiilor de recuren, pentru fiecare
element al irului iniial lungimea celui mai mare subir cresctor care se poate
forma ncepnd de la el.
Notm L(k) lungimea celui mai mare subir cresctor care se poate forma
ncepnd elementul de pe poziia k din irul dat. Relaia de recuren este
urmtoarea:
L(k):={1+maxL(i) , astfel nct Xi >=Xk, i={1, 2, , n}} i k {1, 2, , n} }
159
a11
a
A = 21
...
a
m1
a12
a22
...
am 2
a1n
a2 n
...
... amn
...
...
...
aij N .
Un robot situat n csua de start a11 va traversa tabla pentru a ajunge la csua
final amn realiznd doar micrile posibile:
- la dreapta cu o csu: ( aij ai ,( j +1) )
- n jos cu o csu: ( aij a( i +1), j )
i colecteaz obiectele din csuele traversate.
S se determine secvena de micri (drumul) a robotului astfel nct suma
valorilor colectate la ieire s fie maxim.
Exemplu:
Pentru urmtorul caz:
160
Soluia imediat este parcurgerea csuelor: (1,1)-(2,1)-(2,2)-(3,2)-(4,2)-(4,3)(4,4), robotul colecteaz obiecte de valoare total: 18 reprezentnd suma maxim a
sumelor colectate prin parcurgerea oricror drumuri de la csua iniial la csua
final.
Se observ c suma maxim cu care ajunge robotul n csua final poate fi
obinut fie prin mutarea anterioar de pe coloana din stnga (n-1), fie prin mutarea
anterioar din celula de pe linia (m-1). Astfel, dac suma final a valorilor este
maxim, atunci prin scderea valorii comune coninut n amn , nseamn c i
drumurile pariale efectuate pn n celula am1,n sau an ,m 1 sunt de sum
maxim parial.
Generaliznd:
- Dac n celula ai , j robotul a ajuns cu transport maxim de la celula
iniial a11 , efectund ultima mutare dreapta (robotul ajunge n celula
ai , j din celula ai , j
1 ) atunci n celula ai , j
1 robotul a ajuns cu sum
maxim posibil de la a11 la ai , j 1 .
- Dac n celula ai , j robotul a ajuns cu transport maxim de la celula
iniial a11 , efectund ultima mutare jos (robotul ajunge n celula ai , j
din celula ai 1, j ) atunci n celula ai 1, j robotul a ajuns cu sum
maxim posibil de la a11 la ai 1, j .
Observaie: Ultima formulare este chiar Principiul optimalitii exprimat n
varianta 2:
Dac secvena de celule parcurse de la celula : (1,1) la celula (m,n)
este de transport maxim: a11 , ,, aik 11 , jk 1 , aik , j k , , a m ,n
Atunci k < n , secvena de celule parcurse de la (1,1) la ( ik ,
jk ) este de transport maxim: a11 , ,, ai
, aik , j
,j
k 11 k 1
Din observaiile anterioare, se poate deduce o relaie de recuren prin care pot
fi determinate sumele maxime cu care robotul poate ajunge n oricare celul a
tablei A:
161
S11
S 21
S =
...
S
m1
S12
S 22
...
Sm2
S1n
S2n
...
... S mn
...
...
...
Tiprete Vector(i)
SfPentru
SfAlgoritm
Observaie: Tehnica descris pentru rezolvarea problemei robotului este
Programarea dinamic, varianta metodei napoi.
Probleme:
1. Scriei un program de rezolvare a problemei robotului.
2. Scriei programul de rezolvare a problemei 1: Subir cresctor de lungime
maxim.
3. Scriei un program care determin cel mai lung subir comun al dou iruri
date.
163
BIBLIOGRAFIE:
1. Liviu Negrescu, Limbajele C si C++ pentru incepatori Vol. I (p.1 si 2) Limbajul C , Editura Albastr, Cluj-Napoca, 2002.
2. Brian Kernighan, Dennis Ritchie, The C Programming Language,
Second Edition, Prentice Hall, 1988.
3. Bazil
Prv,
Alexandru
Vancea,
Fundamentele
limbajelor
de
164