Informatica clasa 10
Curs Complet de Informatică — Clasa a X-a¶
Specializarea: Matematică-Informatică (Curriculum de Specialitate)
Limbaje: Python (principal) și C++ (secundar)
Bazat pe programa școlară aprobată prin Ordinul MEC 4.350/2025 (Anexa 44)
📋 Cuprins¶
- Introducere
- Capitolul 1: Metode de prelucrare a listelor sortate — căutarea binară și interclasarea
- Capitolul 2: Modelul conceptual neliniar — mulțimea
- Capitolul 3: Clasa
setdin Python - Capitolul 4: Modelul conceptual liniar — șirul de caractere
- Capitolul 5: Clasa
strdin Python șistringdin C++ - Capitolul 6: Modelul conceptual asociativ — dicționarul
- Capitolul 7: Clasa
dictdin Python - Capitolul 8: Modele conceptuale mixte
- Capitolul 9: Subprograme recursive
- Capitolul 10: Metoda Divide et Impera
- Capitolul 11: Metoda Greedy
- Anexe
Introducere¶
Despre acest curs¶
Acest curs acoperă integral programa școlară de informatică pentru clasa a X-a, specializarea matematică-informatică (Ordin MEC 4.350/2025, Anexa 44). Cursul continuă firesc materia din clasa a IX-a, introducând concepte mai avansate:
- Modele conceptuale noi: mulțimi (neliniare), dicționare (asociative), șiruri de caractere, structuri mixte.
- Strategii avansate: căutare binară, interclasare, Divide et Impera, Greedy.
- Subprograme recursive — un nou paradigm de gândire algoritmică.
Recapitulare — ce am învățat în clasa a IX-a¶
Pentru a urmări cursul cu ușurință, ar trebui să stăpânești deja:
- Structuri de control (if, while, for).
- Liste Python (
list) și vectori C++ (vector). - Subprograme (funcții) iterative.
- Prelucrări de numere (cifre, divizori, CMMDC).
- Sortare elementară (selecția, bulele, lista de frecvențe).
- Fișiere text și interfețe grafice (Tkinter).
Ce e nou în clasa a X-a?¶
În clasa a IX-a, datele erau fie simple, fie organizate liniar (liste). Acum vom întâlni:
- Modele neliniare (mulțimea) — fără ordine, fără duplicate.
- Modele asociative (dicționarul) — acces prin „cheie” în loc de poziție.
- Modele mixte — combinații (liste de dicționare, dicționare de liste, matrice).
Conceptual, gândirea se schimbă:
În clasa a IX-a: „Unde se află datele? (la poziția
i)”
În clasa a X-a: „Care sunt datele legate de o anumită informație-cheie?”
Competențe generale¶
Cele 6 CG rămân aceleași ca în clasa a IX-a (bazate pe taxonomia Bloom revizuită): Identificare → Explicare → Utilizare → Analiză → Evaluare → Creare.
Capitolul 1: Metode de prelucrare a listelor sortate¶
1.1. De ce ne trebuie liste sortate?¶
O listă ordonată (sortată) permite algoritmi mult mai eficienți decât o listă neordonată. Două operații fundamentale câștigă enorm în viteză când datele sunt sortate:
- Căutarea unui element (căutare binară — O(log n) vs O(n) la parcurgerea liniară).
- Combinarea a două liste sortate (interclasarea — O(n+m)).
1.2. Căutarea secvențială vs. căutarea binară¶
Căutarea secvențială (liniară)¶
Parcurgem lista element cu element până găsim valoarea dorită.
def cautare_secventiala(a, x):
for i in range(len(a)):
if a[i] == x:
return i
return -1 # nu a fost găsit
Complexitate: O(n) — în cel mai rău caz, parcurgem toată lista.
Căutarea binară¶
Condiție obligatorie: lista să fie sortată.
Idee: la fiecare pas, ne uităm la mijloc:
- Dacă elementul de la mijloc este cel căutat → gata.
- Dacă este mai mic decât cel căutat → căutăm în jumătatea dreaptă.
- Dacă este mai mare → căutăm în jumătatea stângă.
La fiecare pas, dimensiunea intervalului se înjumătățește.
Vizualizare¶
Căutăm 23 în [2, 5, 7, 12, 17, 23, 31, 45, 58, 72]:
Pas 1: [2, 5, 7, 12, 17, 23, 31, 45, 58, 72]
↑ ↑ ↑
st=0 mij=4 dr=9
a[4]=17 < 23 → mergem dreapta
Pas 2: [23, 31, 45, 58, 72]
↑ ↑ ↑
st=5 mij=7 dr=9
a[7]=45 > 23 → mergem stânga
Pas 3: [23, 31]
↑ ↑
st=5 mij=5 (dr=6)
a[5]=23 → GĂSIT! poziția 5
Implementare Python¶
def cautare_binara(a, x):
st, dr = 0, len(a) - 1
while st <= dr:
mij = (st + dr) // 2
if a[mij] == x:
return mij
elif a[mij] < x:
st = mij + 1
else:
dr = mij - 1
return -1
lista = [2, 5, 7, 12, 17, 23, 31, 45, 58, 72]
print(cautare_binara(lista, 23)) # 5
print(cautare_binara(lista, 100)) # -1
Implementare C++¶
#include <iostream>
#include <vector>
using namespace std;
int cautare_binara(vector<int>& a, int x) {
int st = 0, dr = a.size() - 1;
while (st <= dr) {
int mij = st + (dr - st) / 2; // evităm overflow
if (a[mij] == x) return mij;
else if (a[mij] < x) st = mij + 1;
else dr = mij - 1;
}
return -1;
}
int main() {
vector<int> lista = {2, 5, 7, 12, 17, 23, 31, 45, 58, 72};
cout << cautare_binara(lista, 23); // 5
return 0;
}
Complexitate¶
| Dimensiune (n) | Pași secvențială | Pași binară |
|---|---|---|
| 10 | max 10 | max 4 |
| 100 | max 100 | max 7 |
| 1.000 | max 1.000 | max 10 |
| 1.000.000 | max 1.000.000 | max 20 |
| 1.000.000.000 | max 1 miliard | max 30 |
Căutarea binară are complexitate O(log₂ n) — fantastic de rapidă.
1.3. Versiunea recursivă a căutării binare¶
def cautare_binara_rec(a, x, st, dr):
if st > dr:
return -1
mij = (st + dr) // 2
if a[mij] == x:
return mij
elif a[mij] < x:
return cautare_binara_rec(a, x, mij + 1, dr)
else:
return cautare_binara_rec(a, x, st, mij - 1)
lista = [2, 5, 7, 12, 17, 23, 31, 45, 58, 72]
print(cautare_binara_rec(lista, 23, 0, len(lista) - 1)) # 5
1.4. Erori frecvente la căutarea binară¶
- Lista nu este sortată — rezultat imprevizibil.
- Condiția
st <= dr(nust < dr) — altfel pierzi elementul de la poziția finală. - Actualizare greșită:
st = mij(în loc demij + 1) → ciclu infinit.
1.5. Interclasarea a două liste sortate¶
Problemă: Avem două liste deja sortate crescător. Vrem să obținem o singură listă care conține toate elementele, tot sortată.
Soluție ineficientă: concatenare + sortare → O((n+m)·log(n+m)).
Soluție eficientă — interclasarea: O(n+m).
Algoritmul de interclasare¶
Folosim doi indici, câte unul pentru fiecare listă. La fiecare pas:
- Comparăm elementele curente din fiecare listă.
- Adăugăm în lista rezultat pe cel mai mic.
- Avansăm indicele listei din care am luat elementul.
- Când o listă se epuizează, copiem elementele rămase din cealaltă.
Vizualizare¶
A = [1, 4, 7, 9]
B = [2, 3, 8, 10, 15]
i=0, j=0: A[0]=1 < B[0]=2 → adaugăm 1 Rezultat: [1]
i=1, j=0: A[1]=4 > B[0]=2 → adaugăm 2 Rezultat: [1, 2]
i=1, j=1: A[1]=4 > B[1]=3 → adaugăm 3 Rezultat: [1, 2, 3]
i=1, j=2: A[1]=4 < B[2]=8 → adaugăm 4 Rezultat: [1, 2, 3, 4]
i=2, j=2: A[2]=7 < B[2]=8 → adaugăm 7 Rezultat: [1, 2, 3, 4, 7]
i=3, j=2: A[3]=9 > B[2]=8 → adaugăm 8 Rezultat: [1, 2, 3, 4, 7, 8]
i=3, j=3: A[3]=9 < B[3]=10 → adaugăm 9 Rezultat: [1, 2, 3, 4, 7, 8, 9]
A epuizată → copiem rest din B Rezultat: [1, 2, 3, 4, 7, 8, 9, 10, 15]
Implementare Python¶
def interclasare(a, b):
rezultat = []
i, j = 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]:
rezultat.append(a[i])
i += 1
else:
rezultat.append(b[j])
j += 1
# Copiem elementele rămase (doar una din bucle va executa)
while i < len(a):
rezultat.append(a[i])
i += 1
while j < len(b):
rezultat.append(b[j])
j += 1
return rezultat
A = [1, 4, 7, 9]
B = [2, 3, 8, 10, 15]
print(interclasare(A, B))
# [1, 2, 3, 4, 7, 8, 9, 10, 15]
Implementare C++¶
vector<int> interclasare(vector<int>& a, vector<int>& b) {
vector<int> rezultat;
int i = 0, j = 0;
while (i < a.size() && j < b.size()) {
if (a[i] <= b[j]) rezultat.push_back(a[i++]);
else rezultat.push_back(b[j++]);
}
while (i < a.size()) rezultat.push_back(a[i++]);
while (j < b.size()) rezultat.push_back(b[j++]);
return rezultat;
}
1.6. Stabilitatea interclasării¶
Interclasarea este stabilă: păstrează ordinea relativă a elementelor egale. Acest lucru este important când avem liste de obiecte cu mai multe câmpuri (de exemplu, elevi sortați după notă — la egalitate, rămân în ordinea originală).
✏️ Exerciții propuse — Capitolul 1¶
- Modifică funcția
cautare_binarasă returneze numărul de pași (iterații) efectuați. - Scrie o funcție care, pentru o listă sortată, returnează prima poziție unde apare o valoare (nu oricare).
- Implementează căutarea binară pentru a găsi în ce interval se află o valoare (de ex., pentru
[10, 20, 30, 40]șix=25, returnează(1, 2)). - Interclasează două liste sortate care pot conține duplicate, păstrând duplicatele.
- Interclasează trei liste sortate.
Capitolul 2: Modelul conceptual neliniar — mulțimea¶
2.1. Ce este o mulțime?¶
Mulțimea este o colecție de elemente cu două caracteristici esențiale:
- Fiecare element apare o singură dată (fără duplicate).
- Ordinea nu contează —
{1, 2, 3}este aceeași mulțime ca{3, 1, 2}.
Exemple din realitate:
- Mulțimea elevilor înscriși la un curs.
- Mulțimea orașelor pe care le-ai vizitat.
- Mulțimea drepturilor de acces atribuite utilizatorilor.
Diferența față de listă:
| Aspect | Listă | Mulțime |
|---|---|---|
| Duplicate | Permise | Nu |
| Ordine | Contează | Nu contează |
| Acces prin index | Da | Nu |
| Verificare apartenență | O(n) | O(1) mediu |
2.2. Operații matematice cu mulțimi¶
Fie A = {1, 2, 3, 4} și B = {3, 4, 5, 6}.
| Operație | Notație matematică | Rezultat | Semnificație |
|---|---|---|---|
| Reuniune | A ∪ B | {1, 2, 3, 4, 5, 6} | Toate elementele din A sau B |
| Intersecție | A ∩ B | {3, 4} | Elementele din A și B |
| Diferență | A − B | {1, 2} | Elementele din A dar nu din B |
| Diferență simetrică | A △ B | {1, 2, 5, 6} | Elementele doar dintr-una singură |
| Apartenență | x ∈ A | True/False |
x face parte din A? |
| Incluziune | A ⊆ B | True/False |
Toate elementele din A sunt și în B? |
| Egalitate | A = B | True/False |
Conțin exact aceleași elemente? |
2.3. Diagrame Venn¶
Diagramele Venn sunt reprezentări vizuale ale mulțimilor:
A B
┌─────┐ ┌─────┐
│ 1 │ │ 5 │
│ 2 │ 3 │ 6 │
│ │ 4 │ │
└─────┘─────└─────┘
↑
A ∩ B = {3, 4}
2.4. Aplicații practice¶
Exemplu 1: Avem două magazine online. Fiecare are o mulțime de clienți.
- Reuniune → toți clienții cărora putem trimite promoții.
- Intersecție → clienții fideli (au cumpărat din ambele magazine).
- Diferență → clienții exclusivi ai primului magazin.
Exemplu 2: Validarea unui puzzle Sudoku — într-o linie, coloană sau sub-pătrat, cifrele trebuie să formeze o mulțime {1, 2, 3, 4, 5, 6, 7, 8, 9} (fără repetiții).
✏️ Exerciții propuse — Capitolul 2¶
- Desenează diagrama Venn pentru A = {a, b, c, d} și B = {b, c, e, f}. Scrie A∪B, A∩B, A−B, B−A.
- Explică cu cuvintele tale: când două mulțimi sunt egale? Când una este inclusă în alta?
- Dă 3 exemple din viața reală în care modelul de mulțime este mai potrivit decât modelul de listă.
Capitolul 3: Clasa set din Python¶
3.1. Crearea unei mulțimi¶
# Mulțime vidă — ATENȚIE: {} este dicționar, nu set!
m = set()
# Mulțime cu valori
fructe = {"mere", "pere", "banane"}
numere = {1, 2, 3, 4, 5}
# Din listă (elimină automat duplicatele)
lista = [1, 2, 2, 3, 3, 3, 4]
unice = set(lista) # {1, 2, 3, 4}
# Set comprehension
patrate = {x*x for x in range(1, 6)} # {1, 4, 9, 16, 25}
3.2. Reguli importante¶
- Elementele unei mulțimi trebuie să fie imutabile (int, float, str, tuple — DA; list, dict — NU).
- Nu putem accesa elementele prin index:
m[0]dă eroare.
# Corect
m = {1, 2, "text", (3, 4)}
# EROARE — lista este mutabilă
# m = {1, 2, [3, 4]} # TypeError: unhashable type: 'list'
3.3. Operatori pe mulțimi¶
| Operator Python | Semnificație matematică | Exemplu |
|---|---|---|
| |
Reuniune (∪) | A | B |
& |
Intersecție (∩) | A & B |
- |
Diferență (−) | A - B |
^ |
Diferență simetrică (△) | A ^ B |
in |
Apartenență (∈) | 3 in A |
<= |
Incluziune (⊆) | A <= B |
< |
Incluziune strictă | A < B |
== |
Egalitate | A == B |
A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
print(A | B) # {1, 2, 3, 4, 5, 6}
print(A & B) # {3, 4}
print(A - B) # {1, 2}
print(A ^ B) # {1, 2, 5, 6}
print(3 in A) # True
print({1, 2} <= A) # True (subset)
print(A == B) # False
3.4. Metode uzuale¶
m = {1, 2, 3}
# Adăugare
m.add(4) # {1, 2, 3, 4}
m.add(2) # {1, 2, 3, 4} — fără efect, deja exista
# Eliminare
m.remove(2) # {1, 3, 4} — eroare dacă nu există
m.discard(99) # {1, 3, 4} — nu dă eroare dacă nu există
x = m.pop() # elimină și returnează un element oarecare
# Operații cu returnare (nu modifică mulțimea originală)
A = {1, 2, 3}
B = {3, 4, 5}
print(A.union(B)) # {1, 2, 3, 4, 5}
print(A.intersection(B)) # {3}
print(A.difference(B)) # {1, 2}
# Operații care modifică mulțimea
A.update(B) # A devine A ∪ B
A.intersection_update(B) # A devine A ∩ B
# Copiere
C = A.copy()
# Verificări
print(A.issubset({1, 2, 3, 4})) # True
print(A.issuperset({1, 2})) # True
print(A.isdisjoint({9, 10})) # True — nu au elemente comune
3.5. Mulțimi în C++¶
C++ oferă std::set (sortată, bazată pe arbore) și std::unordered_set (neordonată, bazată pe hash).
#include <iostream>
#include <set>
#include <unordered_set>
using namespace std;
int main() {
set<int> A = {3, 1, 4, 1, 5, 9, 2, 6}; // duplicatele se elimină
// set este ORDONAT
for (int x : A) cout << x << " ";
// Ieșire: 1 2 3 4 5 6 9
A.insert(7);
A.erase(4);
if (A.count(3)) cout << "3 există";
if (A.find(10) == A.end()) cout << "10 nu există";
// Reuniune și intersecție — din <algorithm>
set<int> B = {4, 5, 6, 7};
set<int> reuniune;
set_union(A.begin(), A.end(), B.begin(), B.end(),
inserter(reuniune, reuniune.begin()));
return 0;
}
3.6. Aplicații practice¶
Problemă 1 — Eliminare duplicate dintr-o listă¶
def elimina_duplicate(lista):
return list(set(lista))
print(elimina_duplicate([1, 2, 2, 3, 3, 3, 4])) # [1, 2, 3, 4]
⚠️ Atenție: conversia în set PIERDE ordinea. Pentru a o păstra, folosim:
python def elimina_duplicate_ordine(lista): vazute = set() rezultat = [] for x in lista: if x not in vazute: vazute.add(x) rezultat.append(x) return rezultat
Problemă 2 — Cifrele distincte ale unui număr¶
def cifre_distincte(n):
return set(str(n)) # convertim în șir, apoi în mulțime
print(cifre_distincte(122334)) # {'1', '2', '3', '4'}
Problemă 3 — Anagrame¶
Două cuvinte sunt anagrame dacă conțin aceleași litere (cu aceleași frecvențe).
def sunt_anagrame(s1, s2):
# Pentru verificare cu frecvențe, nu doar mulțimi:
return sorted(s1) == sorted(s2)
# set(s1) == set(s2) este INSUFICIENT!
# Ex: "aab" și "abb" au același set dar nu sunt anagrame
print(sunt_anagrame("listen", "silent")) # True
print(sunt_anagrame("aab", "abb")) # False
Problemă 4 — Verificare cifre comune¶
def cifre_comune(a, b):
cifre_a = set(str(a))
cifre_b = set(str(b))
return cifre_a & cifre_b
print(cifre_comune(12345, 56789)) # {'5'}
Problemă 5 — Joc Bingo (numere aleatoare distincte)¶
import random
def bingo(cate, minim, maxim):
numere = set()
while len(numere) < cate:
numere.add(random.randint(minim, maxim))
return sorted(numere)
print(bingo(6, 1, 49)) # 6 numere distincte din 1..49
✏️ Exerciții propuse — Capitolul 3¶
- Scrie un program care citește două liste de nume și afișează: numele comune, numele doar din prima, numele doar din a doua.
- Găsește toate literele vocale distincte dintr-un text.
- Verifică dacă toate zilele săptămânii apar într-o listă de evenimente.
- Generează un număr maxim folosind cifrele distincte ale unui număr dat.
- Având lista prietenilor pe 3 rețele sociale, afișează: prietenii comuni tuturor, prietenii doar într-o rețea.
Capitolul 4: Modelul conceptual liniar — șirul de caractere¶
4.1. Ce este un șir de caractere?¶
Un șir de caractere (string) este o colecție ordonată și imutabilă de caractere.
Exemple:
- Un nume:
"Alex" - O propoziție:
"Bună ziua!" - Un cod:
"ABC-123" - Un text lung:
"Acesta este un paragraf cu mai multe cuvinte."
4.2. Caracteristici fundamentale¶
- Ordonat — fiecare caracter are o poziție precisă.
- Indexat — primul caracter are indexul 0.
- Imutabil (în Python) — după creare, NU poate fi modificat direct.
- Iterabil — poți parcurge fiecare caracter.
4.3. Codificarea caracterelor — ASCII și Unicode¶
Fiecare caracter este reprezentat în memorie printr-un cod numeric.
ASCII (standard vechi, 128 caractere):
'A'→ 65'a'→ 97'0'→ 48' '(spațiu) → 32
Unicode/UTF-8 (standard modern, ~150.000 caractere): include toate limbile.
print(ord('A')) # 65 — codul numeric
print(ord('ă')) # 259 — caracter românesc
print(chr(65)) # 'A' — caracterul de la codul 65
print(chr(8364)) # '€' — simbolul euro
Relații utile:
ord('a') = 97,ord('z') = 122ord('A') = 65,ord('Z') = 90ord('0') = 48,ord('9') = 57ord(litera_mica) - ord('a') = poziția in alfabet(0 pentru ‘a’, 25 pentru ‘z’)
4.4. Indexare și slicing¶
s = "Programare"
# 0123456789
# -10-9...-1
# Acces la caracter
print(s[0]) # 'P'
print(s[4]) # 'r'
print(s[-1]) # 'e' (ultimul)
print(s[-2]) # 'r' (penultimul)
# Slicing [start:stop:step]
print(s[0:4]) # "Prog" (poziții 0, 1, 2, 3)
print(s[4:]) # "ramare"
print(s[:4]) # "Prog"
print(s[::2]) # "Pormr" (din 2 în 2)
print(s[::-1]) # "eramargorP" (inversat)
4.5. Imutabilitatea¶
În Python, nu poți modifica un șir existent:
s = "Alex"
# s[0] = 'M' # EROARE!
# Trebuie să creezi un șir NOU
s_nou = "M" + s[1:] # "Mlex"
4.6. Parcurgerea unui șir¶
s = "Python"
# Prin caracter
for c in s:
print(c)
# Prin index
for i in range(len(s)):
print(f"s[{i}] = {s[i]}")
# Prin index și caracter
for i, c in enumerate(s):
print(f"{i}: {c}")
✏️ Exerciții propuse — Capitolul 4¶
- Citește un cuvânt și afișează fiecare literă pe câte un rând.
- Afișează codul ASCII al fiecărui caracter dintr-un șir.
- Verifică dacă primul și ultimul caracter al unui cuvânt sunt identici.
- Afișează un șir inversat, fără a folosi
[::-1]sau metode speciale.
Capitolul 5: Clasa str din Python și string din C++¶
5.1. Crearea unui string în Python¶
Python permite mai multe moduri de a scrie șiruri:
s1 = 'Salut' # apostrofuri simple
s2 = "Alex" # ghilimele duble
s3 = '''Text pe
mai multe linii''' # apostrofuri triple
s4 = """La fel cu
ghilimele triple"""
s5 = "El a spus: \"Salut\"" # escape cu backslash
s6 = 'El a spus: "Salut"' # mai elegant
5.2. Operatori pe șiruri¶
s = "Salut"
t = "lume"
# Concatenare
print(s + " " + t) # "Salut lume"
# Multiplicare
print("ab" * 3) # "ababab"
print("-" * 20) # "--------------------"
# Apartenență
print("lut" in s) # True
print("xyz" in s) # False
print("ab" not in s) # True
# Comparare (lexicografică — ordine alfabetică)
print("abc" < "abd") # True
print("abc" < "abcd") # True (mai scurt = mai mic la prefix)
print("Abc" < "abc") # True (majuscula < minuscula în ASCII)
# Dimensiune
print(len(s)) # 5
5.3. Metode pentru schimbarea formatului¶
s = "Bună Ziua, Alex!"
print(s.upper()) # "BUNĂ ZIUA, ALEX!"
print(s.lower()) # "bună ziua, alex!"
print(s.title()) # "Bună Ziua, Alex!" — fiecare cuvânt cu majusculă
print(s.capitalize()) # "Bună ziua, alex!" — doar prima literă
print(s.swapcase()) # "bUNĂ zIUA, aLEX!" — inversează
5.4. Metode pentru verificarea conținutului¶
print("abc123".isalnum()) # True — litere și cifre
print("abc".isalpha()) # True — doar litere
print("123".isdigit()) # True — doar cifre
print(" ".isspace()) # True — doar spații
print("ABC".isupper()) # True
print("abc".islower()) # True
print("Title Text".istitle()) # True
5.5. Metode pentru căutare¶
s = "Bună ziua, bună seara"
# Indexul primei apariții
print(s.find("bună")) # 11
print(s.find("xyz")) # -1 (nu există)
print(s.index("bună")) # 11 (ERoare dacă nu există)
# Indexul ultimei apariții
print(s.rfind("bună")) # 11 (doar una este aici)
# Numărul de apariții
print(s.count("bună")) # 1 (case-sensitive!)
print(s.lower().count("bună")) # 2
# Verifică început/sfârșit
print(s.startswith("Bună")) # True
print(s.endswith("seara")) # True
5.6. Metode pentru modificare (returnează șir NOU)¶
s = " Salut, lume! "
# Eliminare spații
print(s.strip()) # "Salut, lume!"
print(s.lstrip()) # "Salut, lume! "
print(s.rstrip()) # " Salut, lume!"
# Înlocuire
print("abcabc".replace("a", "X")) # "XbcXbc"
print("abcabc".replace("a", "X", 1)) # "Xbcabc" — doar prima apariție
# Divizare
print("a,b,c,d".split(",")) # ["a", "b", "c", "d"]
print("unul doi trei".split()) # ["unul", "doi", "trei"] — spații
print("abc\ndef\nghi".splitlines()) # ["abc", "def", "ghi"]
# Alipire (inversul lui split)
print("-".join(["2024", "01", "15"])) # "2024-01-15"
print(" ".join(["Alex", "e", "aici"])) # "Alex e aici"
5.7. Formatare — f-strings¶
nume = "Alex"
varsta = 15
media = 9.723456
# f-strings (Python 3.6+)
print(f"Salut, {nume}! Ai {varsta} ani.")
print(f"Media: {media:.2f}") # 2 zecimale
print(f"Media: {media:8.2f}") # 8 caractere total, 2 zecimale
print(f"Procent: {0.85:.0%}") # "85%"
# Format alternativ
print("Salut, {}! Ai {} ani.".format(nume, varsta))
print("Media: %.2f" % media)
5.8. Clasa string din C++¶
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string s = "Salut";
string t = "lume";
// Concatenare
string u = s + " " + t; // "Salut lume"
s += "!"; // "Salut!"
// Acces la caracter
cout << s[0] << endl; // 'S'
cout << s.length() << endl; // 6 (len)
cout << s.size() << endl; // 6 (tot len)
// Substring
cout << s.substr(0, 3) << endl; // "Sal" (poz 0, lungime 3)
// Căutare
cout << s.find("lut") << endl; // 2
cout << s.find("xy") << endl; // string::npos (-1 in practica)
// Înlocuire
string v = "abcabc";
size_t pos = v.find("a");
if (pos != string::npos) v.replace(pos, 1, "X"); // "Xbcabc"
// Conversii (C++11+)
int nr = stoi("42");
double r = stod("3.14");
string snr = to_string(123);
// Transformare (manuală cu <algorithm>)
string w = "Hello";
transform(w.begin(), w.end(), w.begin(), ::tolower); // "hello"
// Comparare lexicografică
cout << (string("abc") < string("abd")) << endl; // 1 (true)
return 0;
}
5.9. Probleme rezolvate¶
Problemă 1 — Validare e-mail simplu¶
def email_valid(s):
return s.count("@") == 1 and "." in s.split("@")[1] and " " not in s
print(email_valid("alex@exemplu.ro")) # True
print(email_valid("alex@@exemplu.ro")) # False
print(email_valid("alex@exemplu")) # False
print(email_valid("alex exemplu.ro")) # False
Problemă 2 — Numărare vocale¶
def numar_vocale(s):
vocale = set("aeiouăîâAEIOUĂÎÂ")
return sum(1 for c in s if c in vocale)
print(numar_vocale("Bună ziua, Alex!")) # 7
Problemă 3 — Criptare Caesar (cifru cu deplasare)¶
def caesar_cripteaza(text, k):
rezultat = ""
for c in text:
if c.isupper():
rezultat += chr((ord(c) - ord('A') + k) % 26 + ord('A'))
elif c.islower():
rezultat += chr((ord(c) - ord('a') + k) % 26 + ord('a'))
else:
rezultat += c # caracterele non-literă rămân neschimbate
return rezultat
def caesar_decripteaza(text, k):
return caesar_cripteaza(text, -k)
mesaj = "Salut, Alex!"
cifrat = caesar_cripteaza(mesaj, 3)
print(cifrat) # "Vdoxw, Doha!"
print(caesar_decripteaza(cifrat, 3)) # "Salut, Alex!"
Problemă 4 — Palindrom¶
def este_palindrom(s):
s_curat = ''.join(c.lower() for c in s if c.isalnum())
return s_curat == s_curat[::-1]
print(este_palindrom("capac")) # True
print(este_palindrom("Ele fac cafele")) # True
print(este_palindrom("python")) # False
Problemă 5 — Frecvența cuvintelor¶
def frecventa_cuvinte(text):
cuvinte = text.lower().split()
frecv = {}
for c in cuvinte:
c_curat = c.strip(".,!?;:")
frecv[c_curat] = frecv.get(c_curat, 0) + 1
return frecv
text = "Ana are mere. Mere sunt roșii. Ana iubește mere."
print(frecventa_cuvinte(text))
# {'ana': 2, 'are': 1, 'mere': 3, 'sunt': 1, 'roșii': 1, 'iubește': 1}
✏️ Exerciții propuse — Capitolul 5¶
- Scrie o funcție care primește un nume complet (
"Ion Popescu Marin") și returnează-l reorganizat ca"Popescu Marin Ion". - Validează un număr de telefon: 10 cifre, poate conține spații/cratime.
- Numără câte cuvinte dintr-un text încep cu literă mare.
- Scrie o funcție care transformă un text în „limbaj de păsări” (fiecare vocală este dublată cu „p”: „a” → „apa”).
- Găsește cel mai lung cuvânt dintr-o propoziție.
- Verifică complexitatea unei parole (conține litere mari, mici, cifre, simboluri, minim 8 caractere).
Capitolul 6: Modelul conceptual asociativ — dicționarul¶
6.1. Ce este un dicționar?¶
Un dicționar este o colecție de perechi cheie → valoare. Accesul se face prin cheie, nu prin poziție.
Analogii din realitate:
- Dicționarul lingvistic: cuvânt (cheie) → definiție (valoare).
- Agenda telefonică: nume (cheie) → număr (valoare).
- Evidența populației: CNP (cheie) → date personale (valoare).
- Meniu restaurant: denumire preparat (cheie) → preț (valoare).
6.2. Caracteristici¶
- Cheile sunt unice — nu poți avea două chei identice.
- Cheile sunt imutabile — în general,
str,int,tuple(nulist,dict). - Valorile pot fi orice — chiar alte dicționare sau liste.
- Accesul este foarte rapid — O(1) în medie (bazat pe tabele hash).
6.3. Când folosim dicționare?¶
Folosim dicționare când:
- Avem o relație clară „cheie → valoare”.
- Vrem acces rapid după un identificator (nu după poziție).
- Cheile au nume logice (nu doar 0, 1, 2…).
Folosim liste când:
- Ordinea este importantă.
- Nu avem identificatori unici.
- Vrem să parcurgem toate elementele.
6.4. Comparație vizuală¶
Lista:
indice: 0 1 2 3
valori: "Ana" "Bogdan" "Ion" "Maria"
Dicționarul:
chei: "Ana" "Bogdan" "Ion" "Maria"
valori: 9 10 7 8
În listă, lista[2] înseamnă „al 3-lea element”. În dicționar, note["Ana"] înseamnă „nota lui Ana”.
✏️ Exerciții propuse — Capitolul 6¶
- Dă 5 exemple din viața reală în care modelul de dicționar este potrivit.
- Pentru fiecare exemplu, identifică ce sunt cheile și ce sunt valorile.
- Explică de ce nu putem avea două chei egale într-un dicționar.
Capitolul 7: Clasa dict din Python¶
7.1. Crearea unui dicționar¶
# Dicționar vid
d1 = {}
d2 = dict()
# Cu valori
note = {"Ana": 9, "Bogdan": 10, "Ion": 7}
persoana = {
"nume": "Alex",
"varsta": 15,
"oras": "București"
}
# Din liste de perechi
perechi = [("a", 1), ("b", 2), ("c", 3)]
d = dict(perechi) # {"a": 1, "b": 2, "c": 3}
# Cu zip
chei = ["a", "b", "c"]
valori = [1, 2, 3]
d = dict(zip(chei, valori)) # {"a": 1, "b": 2, "c": 3}
# Dict comprehension
patrate = {x: x*x for x in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
7.2. Acces și modificare¶
note = {"Ana": 9, "Bogdan": 10, "Ion": 7}
# Acces direct
print(note["Ana"]) # 9
# print(note["Maria"]) # EROARE — KeyError
# Acces sigur cu get()
print(note.get("Ana")) # 9
print(note.get("Maria")) # None — fără eroare
print(note.get("Maria", 0)) # 0 — valoare implicită
# Adăugare / modificare
note["Maria"] = 8 # adaugă cheie nouă
note["Ana"] = 10 # modifică valoarea existentă
# Verificare existență
print("Ana" in note) # True
print("Xyz" in note) # False
print("Ana" not in note) # False
7.3. Metode ale clasei dict¶
note = {"Ana": 9, "Bogdan": 10, "Ion": 7}
# Obținere vizualizări (view objects)
print(list(note.keys())) # ["Ana", "Bogdan", "Ion"]
print(list(note.values())) # [9, 10, 7]
print(list(note.items())) # [("Ana", 9), ("Bogdan", 10), ("Ion", 7)]
# Eliminare
x = note.pop("Ion") # x = 7, cheia "Ion" este eliminată
# note.pop("Xyz") # EROARE
y = note.pop("Xyz", None) # y = None, fără eroare
ultimul = note.popitem() # elimină și returnează (cheie, valoare) ultima adăugată
note.clear() # golește dicționarul
# Actualizare cu alt dicționar
note = {"Ana": 9}
alte_note = {"Bogdan": 10, "Ana": 8}
note.update(alte_note) # {"Ana": 8, "Bogdan": 10} — actualizat!
# setdefault — adaugă cheia doar dacă nu există
note.setdefault("Cristina", 7) # adaugă "Cristina": 7
note.setdefault("Ana", 5) # NU modifică, "Ana" există deja
# fromkeys — creează dict cu chei date și valoare comună
d = dict.fromkeys(["a", "b", "c"], 0) # {"a": 0, "b": 0, "c": 0}
d = dict.fromkeys(["a", "b", "c"]) # {"a": None, "b": None, "c": None}
# Copiere
note_copie = note.copy()
7.4. Iterarea unui dicționar¶
note = {"Ana": 9, "Bogdan": 10, "Ion": 7}
# Implicit — iterează pe CHEI
for k in note:
print(k, "->", note[k])
# Explicit pe chei
for k in note.keys():
print(k)
# Pe valori
for v in note.values():
print(v)
# Pe perechi (recomandat)
for k, v in note.items():
print(f"{k}: {v}")
7.5. Operații utile¶
note = {"Ana": 9, "Bogdan": 10, "Ion": 7, "Maria": 8}
# Suma valorilor
total = sum(note.values()) # 34
# Media
medie = sum(note.values()) / len(note) # 8.5
# Elevul cu nota maximă
elev_max = max(note, key=note.get) # "Bogdan"
# Alternativ:
elev_max = max(note.items(), key=lambda x: x[1])[0]
# Elevi cu notă >= 9
premiați = {k: v for k, v in note.items() if v >= 9}
# {"Ana": 9, "Bogdan": 10}
# Inversare chei-valori (atenție la duplicate!)
inversat = {v: k for k, v in note.items()}
7.6. Dicționare imbricate¶
elevi = {
"Alex": {
"varsta": 15,
"clasa": "9A",
"note": [9, 8, 10]
},
"Maria": {
"varsta": 16,
"clasa": "10B",
"note": [10, 9, 9]
}
}
# Acces
print(elevi["Alex"]["clasa"]) # "9A"
print(elevi["Alex"]["note"][0]) # 9
# Modificare
elevi["Alex"]["note"].append(7)
# Adăugare elev nou
elevi["Cristina"] = {
"varsta": 15,
"clasa": "9A",
"note": []
}
# Afișare
for nume, date in elevi.items():
print(f"{nume}: clasa {date['clasa']}, media {sum(date['note'])/len(date['note']):.2f}")
7.7. Unificarea dicționarelor (Python 3.9+)¶
stoc1 = {"pâine": 5, "lapte": 3}
stoc2 = {"ouă": 12, "lapte": 2}
# Operator | (unire)
total = stoc1 | stoc2 # {"pâine": 5, "lapte": 2, "ouă": 12}
# La cheile comune, rămâne valoarea din al doilea
# Operator |= (actualizează pe loc)
stoc1 |= stoc2
# Unpacking (funcționează și în versiuni mai vechi)
total = {**stoc1, **stoc2}
7.8. Probleme rezolvate¶
Problemă 1 — Frecvența caracterelor¶
def frecvente(s):
f = {}
for c in s:
f[c] = f.get(c, 0) + 1
return f
print(frecvente("informatica"))
# {'i': 2, 'n': 1, 'f': 1, 'o': 1, 'r': 1, 'm': 1, 'a': 2, 't': 1, 'c': 1}
Problemă 2 — Traducător simplu¶
dict_ro_en = {
"mere": "apple",
"pere": "pear",
"struguri": "grapes",
"căpșune": "strawberries"
}
def tradu(cuvant):
return dict_ro_en.get(cuvant.lower(), "cuvânt necunoscut")
print(tradu("Mere")) # "apple"
print(tradu("banane")) # "cuvânt necunoscut"
Problemă 3 — Agendă telefonică¶
def agenda_interactiva():
agenda = {}
while True:
print("\n1. Adaugă 2. Caută 3. Șterge 4. Afișează tot 0. Ieșire")
op = input("Opțiune: ")
if op == "1":
nume = input("Nume: ")
tel = input("Telefon: ")
agenda[nume] = tel
elif op == "2":
nume = input("Nume: ")
print(agenda.get(nume, "Nu există"))
elif op == "3":
nume = input("Nume: ")
if agenda.pop(nume, None):
print("Șters.")
else:
print("Nu există.")
elif op == "4":
for n, t in sorted(agenda.items()):
print(f"{n}: {t}")
elif op == "0":
break
# agenda_interactiva()
Problemă 4 — Gestiune magazin¶
stoc = {
"pâine": {"pret": 3.50, "cantitate": 50},
"lapte": {"pret": 6.00, "cantitate": 30},
"ouă": {"pret": 1.20, "cantitate": 200},
}
def valoare_totala(stoc):
return sum(d["pret"] * d["cantitate"] for d in stoc.values())
def actualizeaza_pret(stoc, produs, pret_nou):
if produs in stoc:
stoc[produs]["pret"] = pret_nou
else:
print(f"Produsul {produs} nu există.")
def produse_scumpe(stoc, prag):
return [p for p, d in stoc.items() if d["pret"] > prag]
print(f"Valoare totală: {valoare_totala(stoc):.2f} lei")
print(f"Produse > 5 lei: {produse_scumpe(stoc, 5)}")
✏️ Exerciții propuse — Capitolul 7¶
- Citește un text și afișează cel mai frecvent cuvânt.
- Creează un catalog elev → listă de note. Adaugă funcții pentru adăugare notă, calcul medie, afișare top 3.
- Glosar informatic (termen → definiție) cu funcții de căutare, adăugare, export în fișier.
- Pentru două dicționare de stocuri, afișează: produsele din ambele, produsele doar într-unul, produsele cu preț diferit.
- Simulează un sistem de vot: dicționar candidat → număr de voturi. Afișează câștigătorul.
Capitolul 8: Modele conceptuale mixte¶
8.1. Ce sunt structurile mixte?¶
Datele din realitate sunt adesea complexe — nu se potrivesc într-o singură listă sau dicționar. Structurile mixte combină două sau mai multe modele conceptuale:
- Listă de liste → matrice, table, grile.
- Listă de dicționare → colecții de obiecte cu atribute.
- Dicționar de liste → grupuri asociate cu un identificator.
- Dicționar de dicționare → ierarhie pe mai multe niveluri.
8.2. Lista de liste (matrice)¶
O listă de liste este o colecție de liste, toate de aceeași lungime — practic, un tablou bidimensional.
# Matrice 3x4 — 3 rânduri, 4 coloane
matrice = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]
# Acces: matrice[rând][coloană]
print(matrice[0][0]) # 1
print(matrice[2][3]) # 12
print(matrice[1]) # [5, 6, 7, 8] — un rând întreg
# Dimensiuni
n = len(matrice) # 3 rânduri
m = len(matrice[0]) # 4 coloane
# Parcurgere
for i in range(n):
for j in range(m):
print(matrice[i][j], end=" ")
print()
8.3. Crearea unei matrice¶
n, m = 3, 4
# CORECT
M = [[0] * m for _ in range(n)]
# GREȘIT! — toate rândurile se referă la ACEEAȘI listă!
# M = [[0] * m] * n
# Verificare
M[0][1] = 99
print(M) # cu metoda corectă, doar primul rând are 99
8.4. Matrice în C++¶
#include <iostream>
#include <vector>
using namespace std;
int main() {
// Tablou static
int m[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Vector dinamic
vector<vector<int>> M(3, vector<int>(4, 0));
M[1][2] = 99;
// Parcurgere
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << m[i][j] << " ";
}
cout << endl;
}
return 0;
}
8.5. Aplicații ale matricelor — vecinătăți¶
Matrice de adiacență (vecinătate): M[i][j] = 1 dacă există o legătură între i și j, altfel 0.
Exemplu: Relații de prietenie între 4 persoane.
# 4 persoane: Ana(0), Bogdan(1), Cristina(2), Daniel(3)
# Ana e prietenă cu Bogdan și Cristina
# Bogdan e prieten cu Ana și Daniel
# Cristina e prietenă cu Ana
# Daniel e prieten cu Bogdan
prietenii = [
[0, 1, 1, 0], # Ana
[1, 0, 0, 1], # Bogdan
[1, 0, 0, 0], # Cristina
[0, 1, 0, 0] # Daniel
]
# Câți prieteni are fiecare persoană?
nume = ["Ana", "Bogdan", "Cristina", "Daniel"]
for i, n in enumerate(nume):
print(f"{n}: {sum(prietenii[i])} prieteni")
8.6. Parcurgerea vecinilor¶
Pentru elementul M[i][j], cei 4 vecini ortogonali sunt:
- Sus:
M[i-1][j] - Jos:
M[i+1][j] - Stânga:
M[i][j-1] - Dreapta:
M[i][j+1]
Cei 8 vecini (plus diagonalele):
def vecini_elementului(M, i, j):
n, m = len(M), len(M[0])
directii = [(-1, -1), (-1, 0), (-1, 1),
( 0, -1), ( 0, 1),
( 1, -1), ( 1, 0), ( 1, 1)]
vecini = []
for di, dj in directii:
ni, nj = i + di, j + dj
if 0 <= ni < n and 0 <= nj < m:
vecini.append(M[ni][nj])
return vecini
8.7. Bordarea (padding)¶
Bordarea = adăugarea unui „chenar” exterior de valori speciale (de obicei 0 sau -1) în jurul matricei, pentru a evita verificările 0 <= i < n.
# Matricea originală 3x3
M = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Bordată cu 0 — devine 5x5
bordata = [
[0, 0, 0, 0, 0],
[0, 1, 2, 3, 0],
[0, 4, 5, 6, 0],
[0, 7, 8, 9, 0],
[0, 0, 0, 0, 0]
]
# Acum putem accesa M[i-1][j-1] fără să verificăm
8.8. Listă de dicționare¶
Ideală pentru colecții de obiecte cu mai multe atribute.
elevi = [
{"nume": "Ana", "varsta": 15, "media": 9.5},
{"nume": "Bogdan", "varsta": 16, "media": 8.7},
{"nume": "Cristina", "varsta": 15, "media": 9.8},
{"nume": "Daniel", "varsta": 17, "media": 7.2}
]
# Filtrare
buni = [e for e in elevi if e["media"] >= 9]
# Sortare după un câmp
elevi_sortati = sorted(elevi, key=lambda e: e["media"], reverse=True)
# Media generală
medie_generala = sum(e["media"] for e in elevi) / len(elevi)
# Găsire după criteriu
ana = next((e for e in elevi if e["nume"] == "Ana"), None)
8.9. Dicționar de liste¶
Util pentru grupare după un criteriu.
# Elevii grupați pe clase
clase = {
"9A": ["Ana", "Bogdan", "Cristina"],
"10B": ["Daniel", "Elena"],
"11C": ["Florin", "Gabriela", "Horia"]
}
# Adăugare elev
clase["9A"].append("Ion")
# Număr total elevi
total = sum(len(l) for l in clase.values())
# Crearea automată a grupurilor
elevi_plati = [("Ana", "9A"), ("Bogdan", "9A"), ("Daniel", "10B")]
grupate = {}
for nume, clasa in elevi_plati:
grupate.setdefault(clasa, []).append(nume)
8.10. Dicționar de dicționare¶
Pentru ierarhii complexe.
# Evidență magazin: produs → detalii
magazin = {
"001": {"denumire": "Pâine", "pret": 3.50, "stoc": 50},
"002": {"denumire": "Lapte", "pret": 6.00, "stoc": 30},
"003": {"denumire": "Ouă", "pret": 0.80, "stoc": 200}
}
# Acces
print(magazin["001"]["denumire"])
# Modificare
magazin["001"]["pret"] = 4.00
# Adăugare produs nou
magazin["004"] = {"denumire": "Unt", "pret": 12.00, "stoc": 15}
# Valoare totală stoc
valoare = sum(p["pret"] * p["stoc"] for p in magazin.values())
8.11. Problemă rezolvată — tablă de șah cu regine¶
Tablă 8x8. 1 = regină albă, -1 = regină neagră, 0 = liber. Verificăm câte regine negre atacă o regină albă de la poziția dată.
def atacuri_regina(tabla, i, j):
"""Returnează nr. reginei inamice atacate de cea de la (i,j)."""
culoare = tabla[i][j]
if culoare == 0:
return 0
directii = [(-1, 0), (1, 0), (0, -1), (0, 1), # ortogonale
(-1, -1), (-1, 1), (1, -1), (1, 1)] # diagonale
atacuri = 0
n = len(tabla)
for di, dj in directii:
ni, nj = i + di, j + dj
while 0 <= ni < n and 0 <= nj < n:
if tabla[ni][nj] != 0:
if tabla[ni][nj] != culoare: # culoare opusă
atacuri += 1
break # oprire la prima piesă întâlnită
ni += di
nj += dj
return atacuri
✏️ Exerciții propuse — Capitolul 8¶
- Calculează suma elementelor unei matrice.
- Transpune o matrice (rândurile devin coloane).
- Verifică dacă o matrice este simetrică (M[i][j] == M[j][i]).
- Catalog: listă de elevi cu listă de note. Calculează media fiecărui elev.
- Găsește elementul maxim dintr-o matrice și poziția lui.
- Reprezintă un joc X și 0 — verifică cine a câștigat.
- Labirint (0 = zid, 1 = culoar). Verifică dacă există drum de la (0,0) la (n-1,m-1) — fără recursivitate deocamdată.
Capitolul 9: Subprograme recursive¶
9.1. Ce este recursivitatea?¶
Recursivitatea este tehnica prin care o funcție se apelează pe sine însăși pentru a rezolva o problemă mai mică, similară.
Analogii intuitive:
- Oglinda în oglindă — o oglindă reflectă cealaltă, creând o infinitate de imagini.
- Păpușile Matrioșka — o păpușă conține o alta mai mică, care conține una și mai mică…
- Fractali — un model se repetă la scări din ce în ce mai mici.
9.2. Condiții pentru o funcție recursivă corectă¶
O funcție recursivă trebuie să aibă:
- Caz de bază (caz de oprire) — condiția în care NU se mai apelează recursiv.
- Pas recursiv — apel al funcției cu argumente care aduc problema mai aproape de cazul de bază.
- Progres către cazul de bază — fiecare apel trebuie să „micșoreze” problema.
Fără caz de oprire → recursivitate infinită → eroare RecursionError.
9.3. Primul exemplu — factorial¶
Definiție matematică recursivă:
0! = 1 (cazul de bază)
n! = n × (n-1)! (pasul recursiv)
Implementare Python¶
def factorial(n):
if n == 0 or n == 1: # caz de bază
return 1
return n * factorial(n - 1) # pas recursiv
print(factorial(5)) # 120
Implementare C++¶
int factorial(int n) {
if (n == 0 || n == 1) return 1;
return n * factorial(n - 1);
}
Cum funcționează? Stiva de apeluri¶
factorial(5)
└─ 5 * factorial(4)
└─ 4 * factorial(3)
└─ 3 * factorial(2)
└─ 2 * factorial(1)
└─ return 1 ← caz de bază
← return 2*1 = 2
← return 3*2 = 6
← return 4*6 = 24
← return 5*24 = 120
Observație cheie: fiecare apel rămâne „suspendat” pe stivă până când apelul pe care l-a făcut returnează.
9.4. Recursivitate directă vs. indirectă¶
- Recursivitate directă: funcția se apelează DIRECT pe sine.
- Recursivitate indirectă: funcția A apelează B, care apelează A.
# Directă
def A(n):
if n == 0: return
A(n - 1)
# Indirectă (A → B → A → B ...)
def par(n):
if n == 0: return True
return impar(n - 1)
def impar(n):
if n == 0: return False
return par(n - 1)
print(par(10)) # True
print(impar(7)) # True
9.5. Exemple clasice¶
a) Suma cifrelor¶
def suma_cifre(n):
if n == 0:
return 0
return n % 10 + suma_cifre(n // 10)
print(suma_cifre(1234)) # 10
b) Suma elementelor unei liste¶
def suma_lista(lista, i=0):
if i == len(lista):
return 0
return lista[i] + suma_lista(lista, i + 1)
# Sau, mai elegant:
def suma_lista_v2(lista):
if not lista:
return 0
return lista[0] + suma_lista_v2(lista[1:])
print(suma_lista([1, 2, 3, 4, 5])) # 15
print(suma_lista_v2([1, 2, 3, 4, 5])) # 15
c) CMMDC — algoritmul lui Euclid¶
def cmmdc(a, b):
if b == 0:
return a
return cmmdc(b, a % b)
print(cmmdc(48, 18)) # 6
d) Ridicarea la putere (eficientă)¶
def putere(a, n):
if n == 0:
return 1
if n % 2 == 0: # putere pară
jumatate = putere(a, n // 2)
return jumatate * jumatate
return a * putere(a, n - 1) # putere impară
print(putere(2, 10)) # 1024
Complexitate: O(log n) — mult mai rapid decât O(n) direct!
e) Fibonacci recursiv (INEFICIENT)¶
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(10)) # 55
# fib(30) durează 1-2 secunde
# fib(40) durează zeci de secunde!
De ce este lent? Pentru că reface aceleași calcule de mai multe ori:
fib(5)
├── fib(4) ← calculat 1 dată
│ ├── fib(3) ← calculat 2 ori
│ │ ├── fib(2) ← calculat 3 ori
│ │ │ ├── fib(1) → 1
│ │ │ └── fib(0) → 0
│ │ └── fib(1) → 1
│ └── fib(2) ← recalculat!
│ ├── fib(1) → 1
│ └── fib(0) → 0
└── fib(3) ← recalculat!
...
Soluție — memoizare:
def fib_memo(n, cache={}):
if n in cache:
return cache[n]
if n < 2:
return n
cache[n] = fib_memo(n-1, cache) + fib_memo(n-2, cache)
return cache[n]
print(fib_memo(100)) # 354224848179261915075 — instantaneu!
9.6. Recursiv vs. Iterativ¶
Orice problemă rezolvabilă recursiv se poate rezolva și iterativ. Alegerea depinde de:
| Criteriu | Recursiv | Iterativ |
|---|---|---|
| Cod | Mai scurt, elegant | Mai lung |
| Memorie | Mai multă (stiva de apeluri) | Mai puțină |
| Viteză | Puțin mai lent | Puțin mai rapid |
| Lizibilitate | Uneori mai bună | Uneori mai bună |
Exemplu — suma de la 1 la n:
# Recursiv
def suma_rec(n):
if n == 0:
return 0
return n + suma_rec(n - 1)
# Iterativ
def suma_iter(n):
s = 0
for i in range(1, n + 1):
s += i
return s
# Formula directă O(1)
def suma_formula(n):
return n * (n + 1) // 2
9.7. Combinări C(n, k)¶
Formula recursivă: C(n, k) = C(n-1, k-1) + C(n-1, k)
def comb(n, k):
if k == 0 or k == n:
return 1
return comb(n-1, k-1) + comb(n-1, k)
print(comb(5, 2)) # 10
9.8. Șabloane recursive¶
Tiparul 1: linear recursion (descompunere simplă)
f(n) = ceva + f(n-1)
Exemple: factorial, suma, suma cifrelor.
Tiparul 2: binary recursion (dublă descompunere)
f(n) = f(n-1) + f(n-2)
Exemple: Fibonacci, combinări.
Tiparul 3: divide & conquer
f(n) = combine(f(n/2), f(n/2))
Exemple: căutare binară, sortare prin interclasare.
9.9. Avantaje și dezavantaje¶
Avantaje:
- Cod mai clar pentru probleme de natură recursivă (ex: arbori, fractali).
- Urmează direct definiția matematică.
Dezavantaje:
- Stiva de apeluri consumă memorie.
- Python are limită implicită (~1000 apeluri). Se poate mări:
sys.setrecursionlimit(10000). - Poate fi lent fără memoizare.
✏️ Exerciții propuse — Capitolul 9¶
- Scrie recursiv o funcție care afișează cifrele unui număr de la stânga la dreapta.
- Verifică recursiv dacă un șir este palindrom.
- Calculează recursiv al n-lea termen din șirul lui Lucas: L(0)=2, L(1)=1, L(n)=L(n-1)+L(n-2).
- Calculează numărul de cifre ale unui număr.
- Afișează recursiv un triunghi de stele: n=4 →
```
*
**
```
1. Găsește recursiv maximul dintr-o listă.
1. Cifra maximă a unui număr (recursiv).
Capitolul 10: Metoda Divide et Impera¶
10.1. Ce este Divide et Impera?¶
„Divide et impera” (latină: „împarte și stăpânește”) este o strategie de rezolvare a problemelor prin:
- Divizare — împărțim problema în subprobleme mai mici de același tip.
- Rezolvare (conquer) — rezolvăm fiecare subproblemă (de obicei recursiv).
- Combinare — combinăm soluțiile subproblemelor într-o soluție finală.
Schema generală:
DIVIDE_IMPERA(problema):
dacă problema este simplă:
rezolvă direct
altfel:
împarte problema în subprobleme
rezolvă fiecare subproblemă prin DIVIDE_IMPERA
combină soluțiile
10.2. Când folosim D&I?¶
- Când problema poate fi împărțită în subprobleme similare, independente.
- Când combinarea soluțiilor este posibilă și eficientă.
Aplicații clasice:
- Căutare binară.
- Sortare prin interclasare (Merge Sort).
- Calculul puterii (O(log n)).
- Turnurile din Hanoi.
- Fractali.
- Maximum/minimum într-o listă.
10.3. Căutarea binară ca D&I¶
Divizare: împărțim lista în două jumătăți.
Rezolvare: căutăm recursiv doar în jumătatea potrivită.
Combinare: nu e nevoie — soluția vine direct din subproblema rezolvată.
(Implementarea este în Capitolul 1.)
10.4. Maximul dintr-o listă¶
def max_di(lista, st, dr):
if st == dr: # caz de bază: un singur element
return lista[st]
mij = (st + dr) // 2
max_stg = max_di(lista, st, mij) # rezolvă stânga
max_drp = max_di(lista, mij + 1, dr) # rezolvă dreapta
return max(max_stg, max_drp) # combină
lista = [3, 7, 2, 9, 1, 8, 5]
print(max_di(lista, 0, len(lista) - 1)) # 9
Observație: pentru maximum, D&I nu este mai rapid decât parcurgerea liniară (ambele sunt O(n)), dar ilustrează bine tiparul.
10.5. Suma unei liste cu D&I¶
def suma_di(lista, st, dr):
if st == dr:
return lista[st]
mij = (st + dr) // 2
return suma_di(lista, st, mij) + suma_di(lista, mij + 1, dr)
print(suma_di([1, 2, 3, 4, 5], 0, 4)) # 15
10.6. Sortarea prin interclasare (Merge Sort)¶
Cel mai important algoritm D&I. Complexitate O(n log n) — mult mai bun decât O(n²) al sortărilor elementare.
Idee:
- Împarte lista în două jumătăți.
- Sortează recursiv fiecare jumătate.
- Interclasează jumătățile sortate.
def interclasare(a, b):
rezultat = []
i = j = 0
while i < len(a) and j < len(b):
if a[i] <= b[j]:
rezultat.append(a[i]); i += 1
else:
rezultat.append(b[j]); j += 1
rezultat += a[i:]
rezultat += b[j:]
return rezultat
def merge_sort(lista):
if len(lista) <= 1:
return lista
mij = len(lista) // 2
stanga = merge_sort(lista[:mij])
dreapta = merge_sort(lista[mij:])
return interclasare(stanga, dreapta)
print(merge_sort([5, 3, 8, 1, 9, 2, 7]))
# [1, 2, 3, 5, 7, 8, 9]
Vizualizare¶
[5, 3, 8, 1, 9, 2, 7]
/ \
[5, 3, 8] [1, 9, 2, 7]
/ \ / \
[5] [3, 8] [1, 9] [2, 7]
/ \ / \ / \
[3] [8] [1] [9] [2] [7]
\ / \ / \ /
[3,8] [1,9] [2,7]
/ \ \ /
[3, 5, 8] [1, 2, 7, 9]
\ /
[1, 2, 3, 5, 7, 8, 9]
Merge Sort în C++¶
#include <vector>
using namespace std;
vector<int> interclasare(vector<int>& a, vector<int>& b) {
vector<int> rez;
int i = 0, j = 0;
while (i < a.size() && j < b.size()) {
if (a[i] <= b[j]) rez.push_back(a[i++]);
else rez.push_back(b[j++]);
}
while (i < a.size()) rez.push_back(a[i++]);
while (j < b.size()) rez.push_back(b[j++]);
return rez;
}
vector<int> merge_sort(vector<int> v) {
if (v.size() <= 1) return v;
int mij = v.size() / 2;
vector<int> stg(v.begin(), v.begin() + mij);
vector<int> drp(v.begin() + mij, v.end());
stg = merge_sort(stg);
drp = merge_sort(drp);
return interclasare(stg, drp);
}
10.7. Turnurile din Hanoi¶
Regulă: avem 3 tije (A, B, C) și n discuri de mărimi diferite, toate pe tija A, ordonate (cel mai mare jos). Trebuie să mutăm toate discurile pe tija C, cu regulile:
- Mutăm câte un disc o dată.
- Un disc mai mare nu poate fi pus peste unul mai mic.
Soluție D&I:
- Mută
n-1discuri de pe A pe B (folosind C ca tijă auxiliară). - Mută discul cel mare de pe A pe C.
- Mută
n-1discuri de pe B pe C (folosind A ca tijă auxiliară).
def hanoi(n, sursa, auxiliar, destinatie):
if n == 1:
print(f"Mută discul 1 de pe {sursa} pe {destinatie}")
return
hanoi(n - 1, sursa, destinatie, auxiliar)
print(f"Mută discul {n} de pe {sursa} pe {destinatie}")
hanoi(n - 1, auxiliar, sursa, destinatie)
hanoi(3, 'A', 'B', 'C')
# Mută discul 1 de pe A pe C
# Mută discul 2 de pe A pe B
# Mută discul 1 de pe C pe B
# Mută discul 3 de pe A pe C
# Mută discul 1 de pe B pe A
# Mută discul 2 de pe B pe C
# Mută discul 1 de pe A pe C
Număr de mutări: 2^n - 1. Pentru n=64 (legenda), sunt 18.446.744.073.709.551.615 mutări!
10.8. Ridicarea la putere — O(log n)¶
Am văzut în Capitolul 9. Este un exemplu tipic de D&I:
def putere(a, n):
if n == 0: return 1
if n % 2 == 0:
x = putere(a, n // 2)
return x * x # combinare: x * x
return a * putere(a, n - 1)
Exemplu: 2^10 = (2^5)^2 = ((2^2)^2 · 2)^2 = ... — O(log n) operații.
10.9. Covorul lui Sierpinski¶
Un fractal generat recursiv.
import turtle
def sierpinski(t, l, nivel):
if nivel == 0:
for _ in range(3):
t.forward(l)
t.left(120)
return
sierpinski(t, l / 2, nivel - 1)
t.forward(l / 2)
sierpinski(t, l / 2, nivel - 1)
t.backward(l / 2)
t.left(60)
t.forward(l / 2)
t.right(60)
sierpinski(t, l / 2, nivel - 1)
t.left(60)
t.backward(l / 2)
t.right(60)
# t = turtle.Turtle()
# sierpinski(t, 200, 4)
# turtle.done()
10.10. Complexitate D&I — Master Theorem (informal)¶
Pentru recurențe de forma:
T(n) = a · T(n/b) + O(n^d)
| Condiție | Complexitate |
|---|---|
a > b^d |
O(n^(log_b a)) |
a = b^d |
O(n^d · log n) |
a < b^d |
O(n^d) |
Exemple:
- Căutarea binară: T(n) = T(n/2) + O(1) → O(log n).
- Merge Sort: T(n) = 2T(n/2) + O(n) → O(n log n).
- Maximum D&I: T(n) = 2T(n/2) + O(1) → O(n).
✏️ Exerciții propuse — Capitolul 10¶
- Implementează D&I pentru a găsi minimul și maximul simultan dintr-o listă.
- Calculează 2^n folosind recursia eficientă (O(log n)).
- Implementează căutarea binară recursiv.
- Scrie Merge Sort pentru o listă de șiruri de caractere.
- Problema inversiunilor: câte perechi (i, j) cu i < j au a[i] > a[j]? (D&I, O(n log n))
- Modifică Hanoi să numere mutările în loc să le afișeze.
Capitolul 11: Metoda Greedy¶
11.1. Ce este Greedy?¶
Greedy („lacom”) este o strategie de rezolvare a problemelor de optimizare, care la fiecare pas face alegerea care pare cea mai bună pe moment (optimul local), sperând că suma acestor alegeri duce la un optim global.
Principii:
- Decizia se ia pas cu pas.
- Nu se revine asupra alegerilor anterioare.
- La fiecare pas, se alege „cea mai bună” opțiune disponibilă.
Important: Greedy NU garantează întotdeauna soluția optimă! Trebuie demonstrat că funcționează pentru problema dată.
11.2. Când folosim Greedy?¶
- Probleme de optimizare (maxim/minim).
- Când există o strategie de alegere locală care duce la optim global.
- Când sortarea poate ajuta la stabilirea ordinii de procesare.
Avantaje: simplu, rapid (de obicei O(n log n) din cauza sortării).
Dezavantaj: nu funcționează pentru toate problemele.
11.3. Exemplu clasic — Selectarea activităților¶
Problemă: Ai o listă de activități, fiecare cu un interval [start, sfârșit). Găsește numărul maxim de activități care se pot executa fără suprapuneri.
Exemplu:
Activități: A(1-4), B(3-5), C(0-6), D(5-7), E(3-9), F(5-9), G(6-10), H(8-11)
Strategia Greedy (corectă): sortează activitățile după momentul de sfârșit, apoi ia fiecare activitate care începe după ce s-a terminat ultima aleasă.
def selectie_activitati(activitati):
# activitati = listă de tuple (start, sfarsit)
# Sortare după sfârșit
activitati_sortate = sorted(activitati, key=lambda x: x[1])
selectate = [activitati_sortate[0]]
ultim_sfarsit = activitati_sortate[0][1]
for start, sfarsit in activitati_sortate[1:]:
if start >= ultim_sfarsit:
selectate.append((start, sfarsit))
ultim_sfarsit = sfarsit
return selectate
activitati = [(1,4), (3,5), (0,6), (5,7), (3,9), (5,9), (6,10), (8,11)]
print(selectie_activitati(activitati))
# [(1, 4), (5, 7), (8, 11)] — 3 activități
De ce după sfârșit și nu după început? Pentru că, terminând cât mai devreme, lăsăm cât mai mult timp pentru următoarele.
11.4. Problema restului (canonică)¶
Problemă: Ai monede de 1, 5, 10, 25, 50, 100 bani. Să plătești suma S cu minim de monede.
Strategia Greedy: la fiecare pas, ia moneda cea mai mare ≤ restul de plată.
def rest_minim(S, monede):
# Sortare descrescătoare
monede = sorted(monede, reverse=True)
rezultat = []
for m in monede:
while S >= m:
rezultat.append(m)
S -= m
return rezultat
print(rest_minim(63, [1, 5, 10, 25, 50])) # [50, 10, 1, 1, 1]
11.5. ⚠️ Când Greedy NU funcționează¶
Contraexemplu: monede [1, 3, 4], sumă 6.
Greedy face: 6 = 4 + 1 + 1 → 3 monede.
Optim: 6 = 3 + 3 → 2 monede.
# Greedy dă răspuns suboptimal pentru sisteme non-canonice
print(rest_minim(6, [4, 3, 1])) # [4, 1, 1] — 3 monede, nu optim!
Concluzie: Greedy funcționează pentru monedele standard (1, 5, 10, 25, 50, 100), dar nu universal. Pentru cazul general, se folosește programarea dinamică.
11.6. Problema rucsacului — varianta continuă¶
Problemă: Ai un rucsac cu capacitate W. Ai n obiecte, fiecare cu masa m[i] și valoarea v[i]. Poți lua fracțiuni din obiecte. Maximizează valoarea totală.
Strategia Greedy: sortează obiectele după raportul valoare/masă descrescător. Ia obiectele în ordine până se umple rucsacul.
def rucsac_continuu(obiecte, W):
# obiecte = listă de tuple (masa, valoare)
# Sortare descrescător după raportul valoare/masă
obiecte_sortate = sorted(obiecte, key=lambda o: o[1]/o[0], reverse=True)
valoare_totala = 0
selectate = []
for masa, valoare in obiecte_sortate:
if W >= masa:
selectate.append((masa, valoare, 1.0)) # ia tot
valoare_totala += valoare
W -= masa
elif W > 0:
fractiune = W / masa
selectate.append((masa, valoare, fractiune))
valoare_totala += valoare * fractiune
W = 0
break
return valoare_totala, selectate
obiecte = [(10, 60), (20, 100), (30, 120)] # (masa, valoare)
W = 50
print(rucsac_continuu(obiecte, W))
# (240.0, [(10, 60, 1.0), (20, 100, 1.0), (30, 120, 2/3)])
⚠️ Pentru rucsacul discret (nu se pot lua fracțiuni), Greedy NU funcționează — trebuie programare dinamică.
11.7. Suma maximă — produse de perechi¶
Problemă: Ai două mulțimi A și B cu numere. Construiește n perechi (a, b) cu a∈A, b∈B (distincte în A, distincte în B), astfel încât suma Σ a·b să fie maximă.
Strategia Greedy: sortează A crescător și B crescător. Împerechează cel mai mare din A cu cel mai mare din B, al doilea cu al doilea etc.
def suma_max_perechi(A, B, n):
A = sorted(A, reverse=True)[:n]
B = sorted(B, reverse=True)[:n]
return sum(a * b for a, b in zip(A, B))
print(suma_max_perechi([1, 3, 5], [2, 4, 6], 3)) # 5·6 + 3·4 + 1·2 = 44
11.8. Problema fixării scândurilor¶
Problemă: Ai n scânduri de lungimi date. Vrei să le fixezi pe un perete cu minim număr de cuie, știind că un cui poate fixa o scândură într-un punct și că scândurile se pot suprapune.
Strategia Greedy: cuiul să fie la dreapta scândurii curente — poate fixa și orice scândură următoare care începe înainte de această poziție.
11.9. Structura generală a unui algoritm Greedy¶
def greedy(elemente):
# 1. Sortare după criteriu
elemente_sortate = sorted(elemente, key=criteriu)
solutie = []
for e in elemente_sortate:
if este_fezabil(solutie, e):
solutie.append(e)
return solutie
11.10. Divide et Impera vs. Greedy¶
| Aspect | Divide et Impera | Greedy |
|---|---|---|
| Strategie | Împarte, rezolvă, combină | Alege local optimul |
| Probleme tipice | Sortare, căutare | Optimizare |
| Soluție optimă? | Da, întotdeauna | Nu întotdeauna |
| Complexitate | Adesea O(n log n) | Adesea O(n log n) |
| Revenire la decizii | Nu e nevoie | Nu se întoarce |
✏️ Exerciții propuse — Capitolul 11¶
- Ai n oferte (preț, reducere %). Alege cele cu cea mai mare reducere absolută.
- Într-o stație, trenurile sosesc și pleacă la ore date. Câte peroane sunt necesare minim?
- Problema spectacolelor: într-o sală, programează maximum de spectacole fără suprapuneri.
- Dă rest cu monede europene (1, 2, 5, 10, 20, 50 cenți, 1, 2 €).
- Ai
nsarcini cu durate. Vrei să le execuți în ordinea care minimizează timpul mediu de așteptare. - Găsește minimul de cuie pentru fixarea unor scânduri cu intervale date.
Anexe¶
Anexa A — Cheat sheet Python: set, str, dict¶
# ============ SET ============
s = set() # mulțime vidă
s = {1, 2, 3}
s.add(4)
s.remove(2) # eroare dacă lipsește
s.discard(99) # nu dă eroare
A | B # reuniune
A & B # intersecție
A - B # diferență
A ^ B # diferență simetrică
x in A
# ============ STR ============
s = "Alex"
len(s) # 4
s[0], s[-1] # 'A', 'x'
s[1:3] # 'le'
s.upper(), s.lower()
s.strip(), s.split()
"-".join(["a","b","c"])
s.replace("a", "b")
s.count("a")
s.find("x") # -1 dacă nu există
s.isalpha(), s.isdigit()
# ============ DICT ============
d = {"a": 1, "b": 2}
d["c"] = 3
d.get("z", 0) # 0 (default)
d.pop("a")
del d["b"]
"a" in d
for k, v in d.items(): ...
for k in d.keys(): ...
for v in d.values(): ...
d1 | d2 # Python 3.9+
Anexa B — Cheat sheet C++: set, string, map¶
// ============ set ============
#include <set>
set<int> s = {3, 1, 4, 1, 5}; // {1, 3, 4, 5} — SORTAT
s.insert(2);
s.erase(3);
if (s.count(4)) ...
for (int x : s) ...
// ============ string ============
#include <string>
string s = "Alex";
s.length(), s.size();
s[0], s.back();
s.substr(1, 2); // "le"
s.find("ex"); // 2, sau string::npos
s += " B";
stoi("42"), to_string(42);
// ============ map (dict) ============
#include <map>
map<string, int> m; // ORDONAT după cheie
m["Ana"] = 9;
if (m.count("Ana")) ...
m.erase("Ana");
for (auto& [k, v] : m) ... // C++17
Anexa C — Complexități comune¶
| Algoritm | Complexitate |
|---|---|
| Căutare secvențială | O(n) |
| Căutare binară | O(log n) |
| Sortare bulelor / selecție | O(n²) |
| Sortare prin interclasare | O(n log n) |
| Sortare listă de frecvențe | O(n + k) |
| Interclasare | O(n + m) |
| Factorial recursiv | O(n) |
| Fibonacci naiv | O(2ⁿ) |
| Fibonacci cu memoizare | O(n) |
| Ridicare la putere rapidă | O(log n) |
| Turnurile din Hanoi | O(2ⁿ) mutări |
Operații set, dict |
O(1) mediu |
Operații list (append, index) |
O(1) |
| Inserție/ștergere la mijloc în listă | O(n) |
Anexa D — Probleme recomandate pentru practică¶
Nivel începător:
- Numără vocalele distincte dintr-un text.
- Frecvența literelor.
- Anagrame.
- Palindroame (inclusiv cu caractere speciale).
Nivel intermediar:
5. Gestiune bibliotecă cu dicționare.
6. Procesator de statistici pe elevi (listă de dicționare).
7. Validator de formulare.
8. Triaj cozi (Greedy).
Nivel avansat:
9. Merge sort iterativ (nerecursiv).
10. Numărul de inversiuni (D&I).
11. Cel mai lung subșir comun (pregătire pt. programare dinamică).
12. Problema N-Reginelor (backtracking — pregătire pt. clasa a XI-a).
Anexa E — Resurse online recomandate¶
Vizualizări interactive¶
- VisuAlgo — animații pentru căutare binară, merge sort, D&I.
- Python Tutor — execuție pas cu pas Python/C++.
- Algorithm Visualizer — algoritmi în mai multe limbaje.
- Data Structure Visualizations.
Exerciții¶
- pbinfo.ro — probleme pentru liceu românesc.
- infoarena.ro — probleme româneşti de olimpiadă.
- Codeforces — concursuri internaționale.
- LeetCode — interviuri tehnice.
Documentație¶
Anexa F — Glosar de termeni noi (clasa a X-a)¶
| Termen | Definiție |
|---|---|
| Mulțime (set) | Colecție neordonată de elemente unice. |
| Hash | Funcție care transformă o cheie într-un index rapid. |
| Dicționar (dict/map) | Colecție de perechi cheie-valoare. |
| Cheie | Identificator unic într-un dicționar. |
| Imutabil | Care nu poate fi modificat după creare (ex: str, tuple). |
| Slicing | Extragerea unei secvențe prin [start:stop:step]. |
| ASCII / Unicode | Standard de codificare a caracterelor. |
| Matrice | Tablou bidimensional (listă de liste). |
| Bordare (padding) | Adăugarea de rând/coloană suplimentare pentru ușurința parcurgerii. |
| Recursivitate | Tehnica prin care o funcție se apelează pe sine. |
| Caz de bază | Condiția de oprire a recursivității. |
| Memoizare | Tehnica de a cache-ui rezultate pentru a evita recalculul. |
| Stiva de apeluri (call stack) | Structura care păstrează apelurile recursive active. |
| Divide et Impera | Strategie: împarte, rezolvă, combină. |
| Greedy | Strategie: alege local optimul la fiecare pas. |
| Căutare binară | Algoritm O(log n) pe liste sortate. |
| Interclasare (merge) | Combinare a două liste sortate într-una sortată. |
| Merge Sort | Algoritm de sortare O(n log n) bazat pe D&I. |
| Complexitate logaritmică | O(log n) — foarte eficientă. |
| Fractal | Figură geometrică autosimilară la scări diferite. |
Anexa G — Pregătire pentru clasa a XI-a¶
Pentru anul viitor (specializarea mate-info) urmează:
- Backtracking (generare permutări, combinări, N-regine).
- Programare dinamică (problema rucsacului discret, subsecvența crescătoare maximă).
- Grafuri (reprezentări, BFS, DFS, drumuri minime).
- Arbori (binar, de căutare).
- Programare orientată pe obiecte aprofundată.
Pregătire utilă acum:
- Rezolvă probleme cu Greedy și D&I (gândire algoritmică).
- Lucrează cu liste de dicționare și matrice (baza pentru reprezentarea grafurilor).
- Stăpânește bine recursivitatea (esențială pentru backtracking și DFS).
🎓 Încheiere¶
Ai parcurs integral materia de clasa a X-a! Reține în special:
✅ Modele conceptuale noi: mulțime (neliniară), dicționar (asociativ), structuri mixte.
✅ Clasele Python: set, str, dict — cu toate operatorii și metodele.
✅ Algoritmi pe liste sortate: căutare binară (O(log n)) și interclasare (O(n+m)).
✅ Subprograme recursive — o nouă paradigmă de gândire.
✅ Două strategii majore: Divide et Impera și Greedy.
Cheia succesului în informatică:
- Practică zilnică — un set de 2-3 probleme pe zi schimbă totul.
- Înțelege, nu memoriza — identifică tiparele, nu pașii exacti.
- Desenează — schițe, diagrame, arbori de recursie — toate ajută.
- Explică — dacă poți explica un algoritm unui coleg, l-ai înțeles.
- Debugging — erorile sunt profesorii tăi. Nu le evita, analizează-le.
„Recursivitatea este înțeleasă de cei care înțeleg recursivitatea.”
Document generat în conformitate cu Programa școlară pentru disciplina Informatică, clasa a X-a, curriculum de specialitate, Ordin MEC 4.350/2025, Anexa 44.