htm
Ref: https://dic.academic.ru/dic.nsf/ruwiki/46738
Алгоритм
Быстрая сортировка использует стратегию «разделяй и властвуй». Шаги алгоритма таковы:
1. Выбираем в массиве некоторый элемент, который будем называть опорным элементом. С
точки зрения корректности алгоритма выбор опорного элемента безразличен. С точки зрения
повышения эффективности алгоритма выбираться должна медиана, но без дополнительных
сведений о сортируемых данных её обычно невозможно получить. Известные стратегии:
выбирать постоянно один и тот же элемент, например, средний или последний по положению;
выбирать элемент со случайно выбранным индексом.
Медиа́на (50-й процентиль, квантиль 0,5) — возможное значение признака, которое делит
ранжированную совокупность (вариационный ряд выборки) на две равные части: 50 %
«нижних» единиц ряда данных будут иметь значение признака не больше, чем медиана, а
«верхние» 50 % — значения признака не меньше, чем медиана.
Оценка эффективности
QuickSort является существенно улучшенным вариантом алгоритма сортировки с
помощью прямого обмена (его варианты известны как «Пузырьковая сортировка» и
«Шейкерная сортировка»), известного, в том числе, своей низкой эффективностью.
Принципиальное отличие состоит в том, что после каждого прохода элементы делятся
на две независимые группы. Любопытный факт: улучшение самого неэффективного
прямого метода сортировки дало в результате эффективный улучшенный метод.
• Лучший случай. Для этого алгоритма самый лучший случай — если в каждой
итерации каждый из подмассивов делился бы на два равных по величине массива. В
результате количество сравнений, делаемых быстрой сортировкой, было бы равно
значению рекурсивного выражения CN = 2CN/2+N, что в явном выражении дает
примерно N lg N сравнений. Это дало бы наименьшее время сортировки.
• Худший случай. Худшим случаем, очевидно, будет такой, при котором на каждом
этапе массив будет разделяться на вырожденный подмассив из одного опорного
элемента и на подмассив из всех остальных элементов. Такое может произойти, если
в качестве опорного на каждом этапе будет выбран элемент либо наименьший, либо
наибольший из всех обрабатываемых.
Худший случай даёт O(n²) обменов. Но количество обменов и, соответственно, время
работы — это не самый большой его недостаток. Хуже то, что в таком случае глубина
рекурсии при выполнении алгоритма достигнет n, что будет означать n-кратное
сохранение адреса возврата и локальных переменных процедуры разделения
массивов. Для больших значений n худший случай может привести к исчерпанию
памяти во время работы алгоритма. Впрочем, на большинстве реальных данных
можно найти решения, которые минимизируют вероятность того, что понадобится
квадратичное время.
Достоинства и недостатки
Достоинства:
• Один из самых быстродействующих (на практике) из алгоритмов внутренней сортировки
общего назначения.
• Прост в реализации.
• Требует лишь O(Lgn) дополнительной памяти для своей работы. (Не улучшенный
рекурсивный алгоритм в худшем случае O(n) памяти)
• Хорошо сочетается с механизмами кэширования и виртуальной памяти.
• Существует эффективная модификация (алгоритм Седжвика) для сортировки строк —
сначала сравнение с опорным элементом только по нулевому символу строки, далее
применение аналогичной сортировки для «большего» и «меньшего» массивов тоже по
нулевому символу, и для «равного» массива по уже первому символу.
• Работает на связанных списках и других структурах с последовательным доступом.
Недостатки:
• Сильно деградирует по скорости (до n2) при неудачных выборах опорных элементов, что
может случиться при неудачных входных данных. Этого можно избежать, используя такие
модификации алгоритма, как Introsort, или вероятностно, выбирая опорный элемент случайно,
а не фиксированным образом.
• Наивная реализация алгоритма может привести к ошибке переполнения стека, так как ей
может потребоваться сделать O(n) вложенных рекурсивных вызовов. В улучшенных
реализациях, в которых рекурсивный вызов происходит только для сортировки меньшей из
двух частей массива, глубина рекурсии гарантированно не превысит
.
• Неустойчив — если требуется устойчивость, приходится расширять ключ.
Referat
la Structuri de Date si Algoritmi
1
Elaborat:
Golban Lilian
Caraman Olga
gr. INFa-111
Chisinau ,2012
2
Limbajul C versus Limbajul C++
Limbajul si istoria sa
4
Diferente intre limbajele C si C++
3) incapsularea datelor
class complex
5
{ private: double re;
double im;
double modul( )
double arg( ); };
class complex {
double re;
double im;
{ re = x;
im = y; }
… };
3)Incapsularea datelor
7
Procedeul de ascundere a datelor şi funcţiilor în clase prin
protejarea lor şi permiterea accesului la ele numai metodelor clasei se
numeşte incapsulare.
3) numele unei structuri compuse (struct sau class) este un nume de tip
6) tipul "void"
9) specificatorul "const"
9
10) un nou operator pentru delimitarea domeniului de vizibilitate
2. Comentarii
10
Exemplu:
/*
program: iterare
scop : afiseaza intregii de la 1 la 9
*/
In C++ numele unei structuri compuse "struct" sau "union" este considerat
ca nume de tip (ca si cind ar fi fost declarat in C cu instructiunea
"typedef").
Exemplu:
In C In C++
------------------------------------------------------------------------------------------
struct interesectie { struct intersectie {
int culoare_semafor ; int culoare_semafor;
int nr_masini ; int nr_masini;
}; };
struct intersectie retea[50]; intersectie retea[50];
5. Tipuri enumerare
Tipul enumerare este tratat putin diferit in C++ fata de C. Astfel numele
unei enumerari este considerat numele unui tip nou si poate fi folosit ca si
"struct" si "union" pentru declararea unor variabile;
Exemplu:
enum semafor {rosu, galben, verde};
11
semafor s1, s2;
C defineste tipurile enumerate ca fiind tipuri intregi (int). In C++ insa
fiecare tip enumerare este considerat ca unul separat! Acesta inseamna
ca C++ nu permite unei valori de tip "int" sa fie atribuita automat unei
valori de tip "enum". Totusi o valoare "enumerare" poate fi folosita in loc
de "int".
Exemplu:
enum Place {First, Second, Third};
Place John=First; // Ok
int Winner=John; // se poate in acest sens
Place Tom=1; // EROARE in C++, dar merge in C!
Place Mark=(Place) 1; // merge cu modificarea tipului
A treia atribuire este gresita in C++ pentru ca 1 nu e valoare definita
pentru Place.
6. Tipul "void"
Tipul void reprezinta "multimea vida", adica un obiect de tipul "void" nu are
nici o valoare. Chiar daca pare ciudat sa existe un tip pentru care nu
exista valori definite, acesta este foarte util astfel incit chiar si ANSI C l-a
incorporat. Aceasta pentru 2 utilizari remarcabile:
a) o functie ce nu intoarce nici o valoare utilizata de apelant poate fi
declarata ca intorcind o valoare "void". Aceasta elimina posibilitatea ca
o astfel de functie sa intoarca "fortat" o valoare nefolositoare (de obicei
de tip int);
12
Dar spre deosebire de C "short int", "int", "long int" sint tipuri DIFERITE!
Chiar daca "short int" e identic ca format si marime cu "int", C++ le
considera diferite, iar pentru asignari trebuie realizate conversii explicite de
tip (cast). La fel si pentru "unsigned char", "char", "signed char" (au
dimensiunea 1) dar nu sint identice in C++. Acest mod de evaluare a
tipurilor este o precautie suplimentara pentru scrierea unor programe cu
adevarat portabile, deoarece diversele arhitecturi pot implementa diferit
aceste tipuri. In plus, pentru tipul "char" se modifica esential dimensiunea
variabilelor de acest tip:
9. Specificatorul "const"
#include <stdio.h>
void hide_identifier(void)
{
//...alte declaratii, eventual
char ch; // se "ascunde" var. globala
ch='e'; // var. locala
printf("Valoarea locala pt ch=%c",ch);
//...alte prelucrari
}
void main(void)
{
hide_identifier();
printf("Valoarea globala pt ch=%c",ch);
}
14
foloseste operatorul "*" pentru a indica un pointer. Notatia "X&" inseamna
"referinta de tipul X".
Exemple:
int i=1;
int& r=i; // r si i se refera la acelasi obiect int
int x=r; // x=1
r=2; // i=2
Referintele sint asemanatoare pointerilor, in sensul ca ele contin adresele
altor variabile. Spre deosebire de pointeri referintele sint implicit dereferite
la utilizare (prin dereferire se intelege indicarea continutului referit de
"ceva") deoarece referinta, prin numele ei indica de fapt CONTINUTUL si
in acest fel ele arata ca niste variabile "uzuale".
O referinta trebuie initializata cind este declarata - astfel aceasta indica
"ceva" (ce deja exista - are alocata memorie) cu un alt nume (numele
referintei). De exemplu, mai sus i deja exista, iar aceasta variabila
(continutul de memorie de la acea adresa) poate fi identificata ulterior si
prin numele (referinta) r.
Exemplu:
15
In C++ un program ce utilizeaza 2 functii are structura:
#include <...>
void main(void)
{
// functia main
}
17
existente si de aceea se numesc "functii suprapuse" si "operatori
suprapusi".
In C (ca de fapt in majoritatea limbajelor de programare) fiecare functie
trebuie sa aiba un nume UNIC. Acest lucru poate fi necorespunzator in
anumite situatii; de exemplu functia "abs" ce intoarce valoarea absoluta a
unei valori numerice. Avind in vedere diferitele tipuri ale argumentului si
unicitatii numelui de functie, exista in C 3 functii diferite:
int abs(int i);
long labs(long l); // in C
double fabs(double d);
Toate avind acelasi rol este "ciudat" ca au nume diferite. In C++ este
posibil sa existe 3 functii cu acelasi nume dar tipul argumentului /
argumentelor diferite:
int abs(int i);
long abs(long l);
double abs (double d);
O astfel de functie se numeste "functie suprapusa" (overload function),
deoarece are mai multe definitii, un nume de functie putind astfel fi
interpretat in mai multe moduri. O functie poate fi "suprapusa" in 2 moduri:
a) definind efectiv mai multe functii ce au acelasi nume;
b) definind un tip de date ("class") ce contine una sau mai multe functii cu
numele unei functii deja existente;
Exemplu:
In functie de tipul parametrilor compilatorul modifica numele functiilor
pentru a apela corect "versiunea" ceruta de tipul parametrului efectiv,
astfel incit
abs(-10); // apeleaza int abs(int i);
abs(-100000); // apeleaza long abs(long l);
abs(-12.34); // apeleaza double abs(double d);
Daca se apeleaza "abs" cu alt tip de argument pt. rezolvarea ambiguitatii
se foloseste procesul general din C++: se apeleaza implementarea care
ofera cele mai usoare conversii.
void main(void)
{
complex a, b, c, d;
a=cplx_set(1.0,1.0); // a= 1.0 + i*1.0
b=cplx_set(2.0,2.0); // b= 2.0 + i*2.0
c=cplx_add(a,b); // c=a+b
d=cplx_sub(a,b); // d=a-b
// afisare c, d
}
20
Se observa ca au aceeasi sintaxa cu cea a functiilor uzuale, numai ca
numele functiei-operator este "operator<op>".
Exemplu:
Pentru structura "complex" definita anterior se pot defini 2 functii-operator
pentru adunarea si scaderea numerelor complexe:
Din acest moment operatorii predefiniti in C++ "+" si "-" (folositi pentru
numere intregi/reale) pot fi folositi si pentru numere "complexe" (definite de
programator), astfel:
c = a+b;
d = a-b;
Aceste "instructiuni" (expresii) pot fi interpretate (si chiar folosite daca se
considera neaparat necesar) astfel:
c = operator+(a,b);
d = operator-(a,b);
Ori de cite ori se va intilni, din momentul definirii, operatorul "+" sau "-" (in
acest caz, sau altul daca a fost definit) se va testa TIPUL OPERANZILOR
implicati in expresie:
- daca operanzii sint de tip predefinit (int, float, etc.) se realizeaza
operatia cunoscuta (predefinita) C++;
21
- daca operanzii sint de tip definit de programator (aici "complex") se
apeleaza functia-operator corespunzatoare:
pentru "+" ----> operator+
pentru "-" ----> operator-
Observatii:
a) . .* :: ?:
#include <stdio.h>
void func(void)
{
int* i; // i este pointer la un intreg
i=malloc(sizeof(int)); // alocare dinamica
*i=10;
printf("intregul alocat dinamic = %d",*i);
free(i); // apel de functie de biblioteca
}
#include <iostream.h>
void func(void)
{
int* i = new int; // declarare & alocare dinamica
*i=10;
cout << "intregul alocat dinamic" << *i;
delete i; // dealocare cu operatorul predefinit delete
}
23
tip = tipul variabilei ce se aloca dinamic (simplu sau compus -
orice structura, tablou, etc!!!)
Daca operatorul "new" nu poate aloca cantitatea de memorie solicitata (de
dimensiunea tipului "tip") va intoarce (pentru pointer) valoarea "NULL".
Motivul pentru care s-au definit acesti doi operatori noi este flexibilitatea
sporita ce o ofera. Spre deosebire de functiile C-ului
(malloc,calloc,free,etc.), in C++ orice noua structura de date definita de
utilizator (clasa) poate redefini acesti operatori (ca de altfel orice alt
operator). In plus, daca NU se definesc noi "versiuni" pentru acestia, se
pot folosi operatorii predefiniti ! Prin aceasta se da programatorului o
foarte mare flexibilitate in gestionarea dinamica a memoriei.
Avind in vedere ca functiile de biblioteca nu le-a "sters" nimeni, se pot
folosi intr-un program ambele metode de gestionare dinamica a memoriei.
Acest lucru poate conduce insa la probleme serioase (in special daca
"new" si/sau "delete" au fost redefiniti) si mai ales poate sa conduca la
inconsistente. De aceea este recomandabila folosirea metodei C++.
Un alt motiv pentru care este recomandabila folosirea operatorului "new"
este aceea ca nu trebuie sa i se transmita acestuia dimensiunea obiectului
ce se aloca (ca la functiile C "malloc","calloc",etc).
Operatorul "new" poate fi folosit pentru alocarea diferitelor tipuri de
obiecte:
int* p = new int; // se aloca un intreg
int* vec = new int[10]; // se aloca un tablou de 10 intregi
Observatii:
1. Pentru dealocarea unui tablou operatorului "delete" trebuie sa i se
specifice numarul de elemente ale tabloului respectiv (ca in exemplul
24
anterior). Aceasta dimensiune se specifica in paranteze drepte imediat
dupa numele operatorului.
int* p;
p=new(int[10]);// p=pointer la un tablou de 10 intregi
3. Memoria alocata dinamic NU se elibereaza "automat", ci numai dupa
apelul operatorului "delete".
Exemplu:
Sa se gestioneze informatiile despre un numar oarecare de salariati ai
unei firme; numarul se cunoaste la rulare (gestionare dinamica).
Informatiile despre salariati sint: numele, virsta, salariul.
#include <stdio.h>
struct angajat {
char nume[40];
int virsta;
float salariu;
};
void main(void)
{
angajat* ang_ptr;
int nr;
26
Laborator 4 „Utilizarea diferitelor structuri de date”. (Obligatoriu)
1. Creați un tablou bidimensional de tip întreg. Introduceți date inițiale în fiecare element al tabloului
(masivului) de la tastatură. Creați posibilitatea de a modifica valoarea elementului dorit de la
tastatură, adică de la tastatură se întroduc coordonatele (rândul și coloana) elementului care urmează
să fie modificat, apoi, de la tastatură, se introduce și valoarea noua a elementului tabloului.
2. Creați un vector (tablou unidimensional), elementele căruia vor fi structuri, ce vor conține informații
despre un student, și anume:
• Nume;
• Prenume;
• Anul, luna și ziua nașterii;
• Grupa în care învață.
Introduceți, de la tastatură informații despre fiecare student. Creați posibilitatea de a modifica
informația despre student la apelul utilizatorului, de la tastatură.
27
http://49l.ru/a/algoritm_boyera_mura_-_opisanie_algoritma
Строка: * * * * * * к * * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л
Строка: * * * * * а л * * * * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л
В данном случае стоп-символ — «а», и шаблон сдвигается так, чтобы он оказался прямо за
этой буквой. В алгоритме Бойера-Мура эвристика стоп-символа вообще не смотрит на
совпавший суффикс, так что первая буква шаблона окажется под «л», и будет проведена
одна заведомо холостая проверка.
Если стоп-символ «к» оказался за другой буквой «к», эвристика стоп-символа не работает.
28
!
Строка: * * * * к к о л * * * * *
Шаблон: к о л о к о л
3. Эвристика совпавшего суффикса. Если при сравнении строки и шаблона совпало один или
больше символов, шаблон сдвигается в зависимости от того, какой суффикс совпал.
Строка: * * т о к о л * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л
Таблица стоп-символов
Считается, что символы строк нумеруются с 1.
В таблице стоп-символов указывается последняя позиция в needle каждого из символов
алфавита. Для всех символов, не вошедших в needle, пишем 0. Например,
если needle=«abcdadcd», таблица стоп-символов будет выглядеть так.
Символ a b c d
Последняя позиция 5 2 7 6 0
29
Для каждого возможного суффикса S шаблона needle указываем наименьшую величину, на
которую нужно сдвинуть вправо шаблон, чтобы он снова совпал с S. Если такой сдвиг
невозможен, ставится |needle|. Например, для того же needle=«abcdadcd» будет:
Сдвиг 1 2 4 8 ...
8
Иллюстрация
Если шаблон начинается и заканчивается одной и той же комбинацией букв, |needle| вообще
не появится в таблице. Например, дляneedle=«колокол» для всех суффиксов сдвиг будет
равен 4.
Сдвиг 1 4 4 ... 4
4
Иллюстрация
m = length
pi = префикс-функция
pi1 = префикс-функция)
for j=0..m
suffshift = m - pi
for i=1..m
j = m - pi1
suffshift = min
30
Здесь suffshift соответствует всей совпавшей строке; suffshift — пустому суффиксу.
Поскольку префикс-функция вычисляется за O операций, вычислительная сложность этого
шага также равняется O.
31
Algoritmul HeapSort. –Structuri si algoritmi de prelucrarea
datelor.
Algoritmul HeapSort.
Este un algoritm, care a primit denumirea de aranjarea piramidala (HeapSort). Ideea sa, consta
din: in loc de completare aborele se formeaza un sir a[1], a[2],…,a[n] aranjat in piramida ,
aranjarea consta ca fiecare a[i] sa indeplineasca conditia a[i]<=a[2i]si a[i]<=a[2i+1]. Apoi
piramida se foloseste pentru aranjare. Cea mai clara metoda de constructi a piramidei,se uita la
aranjarea sirul intr-un arbore, prezentat in fig.2.9. Sirul este reprezentat ca arbore binar a carui
virf corespunde cu elimentul sirului a[1]. La nivelul doi se gasesc elementele a[2]si a[3]. La
nivelul trei a[4],a[5], a[6] a[7] si asa mai departe. Se observa ca petru un sir cu un numar impar
de elemente, arborele va fi echilibrat, dar pentr-un un sir cu un numar par de elimente, n elimente
a[n] va fi in partea stinga a foii asemanator cu un arbore
a1=8
a2=23 a3=5
Pasul 0
a8=6
Fig2.9
În mod evident,la construirea piramidei suntem interesati de elementele a[n / 2], a[n/2-1], ...,
a[1] pentru sirurii cu un număr par de elemente, și elementele a[(n-1) 2], a[(n-1) / 2-1], ...,a[1]
pentru sirurii cu un număr impar de elemente (ca numai aceste sunt elemente esențiale pentru a
32
limita piramida). Fie i cel mai mare indice, printre indicii de elemente care sunt limitări
importante ale piramidei. Apoi se ia elementul a[i] din arborele,apoi urmeaza procedura de
screening(depistare), care constă în faptul că ramura a arborelui este selectata corespunzator, min
(a[2i], a[2i+1]), precum și valoarea a[i] este schimbata cu valoarea elementului corespunzător.
Dacă acest element nu este o ramura arborelui, pentru el se face aceiasi procedură etc. Aceste
acțiuni sunt realizate succesiv pentru a [i], a [i-1], ..., a [1]. Este ușor de observat că rezultatul
este o vizualizare arborescentă a piramidei pentru sirul original (succesiune de etape utilizate în
exemplele de sir este prezentată în figurile 2.10 - 2.13).
a1=8
a2=23 a3=5
a8=65
Pasul 1
Fig. 2.10.
33
a1=8
a2=23 a3=1
a8=65
Pasul 2
Fig. 2.11
a1=8
a2=6 a3=1
a8=65
Fig. 2.12. Pasul 3
34
a1=1
a2=6 a3=8
a8=65
Pasul 4
Fig. 2.13.
Pasul 1 8 23 |5| 6 44 33 1 65
Pasul 2 8 |23| 1 6 44 33 5 65
Pasul 3 |8| 6 1 23 44 33 5 65
1 6 8 23 44 33 5 65
Pasul 4
1 6 5 23 44 33 8 65
Tabelul 2.8 arată cum să sortați utilizând piramide construite.Esența algoritmului este
urmatoarea. Fie i - cel mai mare indice a sirului, pentru care sunt semnificativ condițiile de
piramida. Apoi, incepind cu a[1]pina la a[i],se efectuiaza următoarele acțiuni. La fiecare pas, se
alege ultimul element al piramidei (în cazul nostru, primul element este selectat a[8]). Se
schimba cu locul cu a[1], apoi pentru a[1]se utilizeaza de screening(depistarea). La fiecare pas,
numărul de elemente din piramida este redus cu 1 (după prima etapă a piramidei sunt considerate
ca elemente ale a[1], a[2], ..., a [n-1], după a două a[1] a[2], ..., a [n-2], și așa mai departe, până
35
când piramida ramine doar un element). Este ușor de vazut acest lucru (ilustrat în Tabelul 2.8),
care rezultatul va fi un sir aranjat în ordine descrescătoare. Aveți posibilitatea să modificați
metoda de construcție a piramidelor și sortarea pentru a obține comenzi în ordine crescătoare,
dacă modificați starea unei piramide pe a[i]> = a[2i] si a[1]> = a[2i+1] pentru toate valorile
semnificative a indicelui i..
Piramida initiala 1 6 5 23 44 33 8 65
65 6 5 23 44 33 8 1
Pasul 1 5 6 65 23 44 33 8 1
5 6 8 23 44 33 65 1
65 6 8 23 44 33 5 1
Pasul 2 6 65 8 23 44 33 5 1
6 23 8 65 44 33 5 1
33 23 8 65 44 6 5 1
Pasul 3
8 23 33 65 44 6 5 1
44 23 33 65 8 6 5 1
Pasul 4
23 44 33 65 8 6 5 1
65 44 33 23 8 6 5 1
Pasul 5
33 44 65 23 8 6 5 1
65 44 33 23 8 6 5 1
Pasul 6
44 65 33 23 8 6 5 1
Pasul 7 65 44 33 23 8 6 5 1
Algoritmul sortari utilizand o piramida necesita îndeplinirea ordinea de trepte nxlog n (log -
binar) în cel mai rau caz, ceea ce îl face deosebit de atractiv pentru sortarea tablouri mari.
36
Быстрая сортировка (англ. quicksort)
Ref: https://dic.academic.ru/dic.nsf/ruwiki/46738
Алгоритм
Быстрая сортировка использует стратегию «разделяй и властвуй». Шаги алгоритма таковы:
2. Выбираем в массиве некоторый элемент, который будем называть опорным элементом. С
точки зрения корректности алгоритма выбор опорного элемента безразличен. С точки зрения
повышения эффективности алгоритма выбираться должна медиана, но без дополнительных
сведений о сортируемых данных её обычно невозможно получить. Известные стратегии:
выбирать постоянно один и тот же элемент, например, средний или последний по положению;
выбирать элемент со случайно выбранным индексом.
Медиа́на (50-й процентиль, квантиль 0,5) — возможное значение признака, которое делит
ранжированную совокупность (вариационный ряд выборки) на две равные части: 50 %
«нижних» единиц ряда данных будут иметь значение признака не больше, чем медиана, а
«верхние» 50 % — значения признака не меньше, чем медиана.
37
1. Два индекса — l и r, приравниваются к минимальному и максимальному
индексу разделяемого массива соответственно.
2. Вычисляется индекс опорного элемента m.
3. Индекс l последовательно увеличивается до тех пор, пока l-й элемент
не превысит опорный.
4. Индекс r последовательно уменьшается до тех пор, пока r-й элемент не
окажется меньше либо равен опорному.
5. Если r = l — найдена середина массива — операция разделения
закончена, оба индекса указывают на опорный элемент.
6. Если l < r — найденную пару элементов нужно обменять местами и
продолжить операцию разделения с тех значений l и r, которые были
достигнуты. Следует учесть, что если какая-либо граница (l или r) дошла
до опорного элемента, то при обмене значение m изменяется на r-й или
l-й элемент соответственно.
5. Рекурсивно упорядочиваем подмассивы, лежащие слева и справа от опорного
элемента.
6. Базой рекурсии являются наборы, состоящие из одного или двух элементов.
Первый возвращается в исходном виде, во втором, при необходимости,
сортировка сводится к перестановке двух элементов. Все такие отрезки уже
упорядочены в процессе разделения.
Оценка эффективности
38
QuickSort является существенно улучшенным вариантом алгоритма сортировки с
помощью прямого обмена (его варианты известны как «Пузырьковая сортировка» и
«Шейкерная сортировка»), известного, в том числе, своей низкой эффективностью.
Принципиальное отличие состоит в том, что после каждого прохода элементы делятся
на две независимые группы. Любопытный факт: улучшение самого неэффективного
прямого метода сортировки дало в результате эффективный улучшенный метод.
• Лучший случай. Для этого алгоритма самый лучший случай — если в каждой
итерации каждый из подмассивов делился бы на два равных по величине массива. В
результате количество сравнений, делаемых быстрой сортировкой, было бы равно
значению рекурсивного выражения CN = 2CN/2+N, что в явном выражении дает
примерно N lg N сравнений. Это дало бы наименьшее время сортировки.
• Худший случай. Худшим случаем, очевидно, будет такой, при котором на каждом
этапе массив будет разделяться на вырожденный подмассив из одного опорного
элемента и на подмассив из всех остальных элементов. Такое может произойти, если
в качестве опорного на каждом этапе будет выбран элемент либо наименьший, либо
наибольший из всех обрабатываемых.
Худший случай даёт O(n²) обменов. Но количество обменов и, соответственно, время
работы — это не самый большой его недостаток. Хуже то, что в таком случае глубина
рекурсии при выполнении алгоритма достигнет n, что будет означать n-кратное
сохранение адреса возврата и локальных переменных процедуры разделения
массивов. Для больших значений n худший случай может привести к исчерпанию
памяти во время работы алгоритма. Впрочем, на большинстве реальных данных
можно найти решения, которые минимизируют вероятность того, что понадобится
квадратичное время.
39
Достоинства и недостатки
Достоинства:
• Один из самых быстродействующих (на практике) из алгоритмов внутренней сортировки
общего назначения.
• Прост в реализации.
• Требует лишь O(Lgn) дополнительной памяти для своей работы. (Не улучшенный
рекурсивный алгоритм в худшем случае O(n) памяти)
• Хорошо сочетается с механизмами кэширования и виртуальной памяти.
• Существует эффективная модификация (алгоритм Седжвика) для сортировки строк —
сначала сравнение с опорным элементом только по нулевому символу строки, далее
применение аналогичной сортировки для «большего» и «меньшего» массивов тоже по
нулевому символу, и для «равного» массива по уже первому символу.
• Работает на связанных списках и других структурах с последовательным доступом.
Недостатки:
• Сильно деградирует по скорости (до n2) при неудачных выборах опорных элементов, что
может случиться при неудачных входных данных. Этого можно избежать, используя такие
модификации алгоритма, как Introsort, или вероятностно, выбирая опорный элемент случайно,
а не фиксированным образом.
• Наивная реализация алгоритма может привести к ошибке переполнения стека, так как ей
может потребоваться сделать O(n) вложенных рекурсивных вызовов. В улучшенных
реализациях, в которых рекурсивный вызов происходит только для сортировки меньшей из
двух частей массива, глубина рекурсии гарантированно не превысит
.
• Неустойчив — если требуется устойчивость, приходится расширять ключ.
40
Алгоритм QuickSort. - Структуры и алгоритмы
компьютерной обработки данных.
Оглавление
Алгоритм QuickSort.
Метод сортировки разделением был предложен Чарльзом Хоаром в 1962 г. Этот метод является
развитием метода простого обмена и настолько эффективен, что его стали называть "методом
быстрой сортировки - Quicksort". Основная идея алгоритма состоит в том, что случайным образом
выбирается некоторый элемент массива x, после чего массив просматривается слева, пока не
встретится элемент a[i] такой, что a[i] > x, а затем массив просматривается справа, пока не
встретится элемент a[j] такой, что a[j] < x. Эти два элемента меняются местами, и процесс
просмотра, сравнения и обмена продолжается, пока мы не дойдем до элемента x. В результате
массив окажется разбитым на две части - левую, в которой значения ключей будут меньше x, и
правую со значениями ключей, большими x. Далее процесс рекурсивно продолжается для левой и
правой частей массива до тех пор, пока каждая часть не будет содержать в точности один
элемент.Понятно, что как обычно, рекурсию можно заменить итерациями, если запоминать
соответствующие индексы массива. Проследим этот процесс на примере нашего стандартного
массива (таблица 2.6). Таблица 2.6. Пример быстрой сортировки
|--------|
8 23 5 6 44 33 1 65
Шаг 1 (в качестве x выбирается a[5])
|---|
8 23 5 6 1 33 44 65
8 23 |5| 6 1 33 44 65
|--------|
|--|
1 5 23 6 8 33 44 65
1 5 23 |6| 8 33 44 65
1 5 8 6 23 33 44 65
1 5 8 |6| 23 33 44 65
1 5 6 8 23 33 44 65
Алгоритм недаром называется быстрой сортировкой, поскольку для него оценкой числа сравнений
и обменов является O(nlog n). На самом деле, в большинстве утилит, выполняющих сортировку
массивов, используется именно этот алгоритм.
Улучшения
41
Hа практике для увеличения быстроты, но не асимптотики, можно произвести несколько
улучшений:
2. Для коротких массивов вызывается сортировка вставками. Из-за рекурсии и других "накладных
расходов" быстрый поиск оказывается не столь уж быстрым для коротких массивов. Поэтому, если
в массиве меньше 12 элементов, вызывается сортировка вставками. Пороговое значение не
критично - оно сильно зависит от качества генерируемого кода.
3. Если последний оператор функции является вызовом этой функции, говорят о хвостовой
рекурсии. Ее имеет смысл заменять на итерации - в этом случае лучше используется стек.
4. После разбиения сначала сортируется меньший раздел. Это также приводит к лучшему
использованию стека, поскольку короткие разделы сортируются быстрее и им нужен более
короткий стек. Требования к памяти уменьшаются с n до log n
Оглавление
42
Алгоритм HeapSort. - Структуры и алгоритмы
компьютерной обработки данных.
Алгоритм HeapSort.
Имеется более совершенный алгоритм, который принято называть пирамидальной сортировкой
(Heapsort). Его идея состоит в том, что вместо полного дерева сравнения исходный массив a[1],
a[2], ..., a[n] преобразуется в пирамиду, обладающую тем свойством, что для каждого a[i]
выполняются условия a[i] <= a[2i] и a[i] <= a[2i+1]. Затем пирамида используется для
сортировки. Наиболее наглядно метод построения пирамиды выглядит при древовидном
представлении массива, показанном на рисунке 2.9. Массив представляется в виде двоичного
дерева, корень которого соответствует элементу массива a[1]. На втором ярусе находятся
элементы a[2] и a[3]. На третьем - a[4], a[5], a[6], a[7] и т.д. Как видно, для массива с нечетным
количеством элементов соответствующее дерево будет сбалансированным, а для массива с
четным количеством элементов n элемент a[n] будет единственным (самым левым) листом "почти"
сбалансированного дерева.
a1=8
a2=23 a3=5
a8=6
Pasul 0
Рис. 2.9.
Очевидно, что при построении пирамиды нас будут интересовать элементы a[n/2], a[n/2-1], ...,
a[1] для массивов с четным числом элементов и элементы a[(n-1)/2], a[(n-1)/2-1], ..., a[1] для
массивов с нечетным числом элементов (поскольку только для таких элементов существенны
ограничения пирамиды). Пусть i - наибольший индекс из числа индексов элементов, для которых
существенны ограничения пирамиды. Тогда берется элемент a[i] в построенном дереве и для него
выполняется процедура просеивания, состоящая в том, что выбирается ветвь дерева,
соответствующая min(a[2?i], a[2?i+1]), и значение a[i] меняется местами со значением
соответствующего элемента. Если этот элемент не является листом дерева, для него
выполняется аналогичная процедура и т.д. Такие действия выполняются последовательно для a[i],
a[i-1], ..., a[1]. Легко видеть, что в результате мы получим древовидное представление пирамиды
для исходного массива (последовательность шагов для используемого в наших примерах массива
показана на рисунках 2.10-2.13).
43
a1=8
a2=23 a3=5
a8=65
Pasul 1
Рис. 2.10.
a1=8
a2=23 a3=1
a8=6
Pasul 2
Рис. 2.11.
44
a1=8
a2=6 a3=1
a8=65
Рис. 2.12. Pasul 3
a1=1
a2=6 a3=8
a8=65
Pasul 4
Рис. 2.13.
В 1964 г. Флойд предложил метод построения пирамиды без явного построения дерева (хотя
метод основан на тех же идеях). Построение пирамиды методом Флойда для нашего стандартного
массива показано в таблице
45
Начальное состояние массива 8 23 5 |65| 44 33 1 6
Шаг 1 8 23 |5| 6 44 33 1 65
Шаг 2 8 |23| 1 6 44 33 5 65
Шаг 3 |8| 6 1 23 44 33 5 65
1 6 8 23 44 33 5 65
Шаг 4
1 6 5 23 44 33 8 65
В таблице 2.8 показано, как производится сортировка с использованием построенной пирамиды.
Суть алгоритма заключается в следующем. Пусть i - наибольший индекс массива, для которого
существенны условия пирамиды. Тогда начиная с a[1] до a[i] выполняются следующие действия.
На каждом шаге выбирается последний элемент пирамиды (в нашем случае первым будет выбран
элемент a[8]). Его значение меняется со значением a[1], после чего для a[1] выполняется
просеивание. При этом на каждом шаге число элементов в пирамиде уменьшается на 1 (после
первого шага в качестве элементов пирамиды рассматриваются a[1], a[2], ..., a[n-1]; после второго -
a[1], a[2], ..., a[n-2] и т.д., пока в пирамиде не останется один элемент). Легко видеть (это
иллюстрируется в таблице 2.8), что в результате мы получим массив, упорядоченный в порядке
убывания. Можно модифицировать метод построения пирамиды и сортировки, чтобы получить
упорядочение в порядке возрастания, если изменить условие пирамиды на a[i] >= a[2?i] и a[1] >=
a[2?i+1] для всех осмысленных значений индекса i.
Исходная пирамида 1 6 5 23 44 33 8 65
65 6 5 23 44 33 8 1
Шаг 1 5 6 65 23 44 33 8 1
5 6 8 23 44 33 65 1
65 6 8 23 44 33 5 1
Шаг 2 6 65 8 23 44 33 5 1
6 23 8 65 44 33 5 1
33 23 8 65 44 6 5 1
Шаг 3
8 23 33 65 44 6 5 1
44 23 33 65 8 6 5 1
Шаг 4
23 44 33 65 8 6 5 1
65 44 33 23 8 6 5 1
Шаг 5
33 44 65 23 8 6 5 1
65 44 33 23 8 6 5 1
Шаг 6
44 65 33 23 8 6 5 1
Шаг 7 65 44 33 23 8 6 5 1
Процедура сортировки с использованием пирамиды требует выполнения порядка nxlog n шагов
(логарифм - двоичный) в худшем случае, что делает ее особо привлекательной для сортировки
больших массивов.
46
Algoritmul QuickSort
Metoda de sortare a fost propusă de către Charles Hoare în 1962. Această metodă
este dezvoltarea unei metode simple de schimb şi atât de eficientă, încât a devenit
cunoscută sub numele de “metoda de sortare rapidă – QuickSort”. Ideea de bază a
algoritmului constă în faptul că selectează la întîmplare un element x al matricei,
după aceasta matricea se analizează din stînga, pînă cînd nu întîlneşte elementul
a[i] astfel încît a[i]>x , şi apoi matricea se studiază din partea dreaptă, pînă cînd nu
întîlneşte elementul a[j] astfel încît a[j]<x. Aceste 2 elemente sunt schimbate,
precum şi procesul de vizualizare, compararea şi schimbul continuă pînă cînd vom
ajunge la elementul x. În rezultat, matricea se divide în două părţi – stînga şi
dreapta, în partea stînga valorile cheie vor fi mai mici decât x, iar în partea dreaptă
valorile cheie vor fi mai mari decât x. În continuare procesul continuă recursiv
pentru partea stîngă şi dreaptă a matricei, pînă cînd fiecare parte nu va conţine
exact un element. Cunoaştem, ca de obicei , recursia poate fi înlocuită prin iteraţii,
dacă să păstrăm în memorie indicii corespunzători din matrice. Să urmăm acest
proces, ca de exemplu, în matricea noastră standard (tabel 2.6).Tabelul 2.6.
Exemplu QuickSort.
8 23 5 65 |44| 33 1
1. Starea initială a matricei
6
|--------|
8 23 5 6 44 33 1 65
Pasul 1 (în calitate de x se alege a[5])
|---|
8 23 5 6 1 33 44 65
8 23 |5| 6 1 33 44
65
|--|
47
1 5 23 6 8 33 44 65
1 5 23 |6| 8 33 44
65
Pasul 3 (în submatricea a[3], a[5] în calitate de x se alege
a[4]) |----|
1 5 8 6 23 33 44 65
1 5 8 |6| 23 33 44
65
Pasul 4 (în submatricea a[3], a[4] se alege a[4])
|--|
1 5 6 8 23 33 44 65
Algoritmul nu este numit fara motiv sortarea rapidă, întrucît pentru evaluarea lui
numărul de comparaţii şi schimburi este O(nlogn). De fapt, de cele mai multe ori,
tablourile de sortare utilizează anume acest algoritm.
Îmbunătăţiri
În practică pentru a creşte viteza, dar nu cea asimptotică, putem face cîteva
optimizări:
1'. Putem selecta media din primele elemente, ultimele şi cele mijlocii. Maxim
Razin: În acest caz, numărul de treceri va scădea de 7/6 ori.
2. Pentru matrici mai scurte se numeşte sortare prin inserare. Din cauza
recursiei şi a altor „calcule adaugătoare” QuickSort nu este un algoritm
eficient pentru tablouri mici. De aceasta, dacă în masiv sunt mai puţin de 12
elemente, se numeşte sortare prin inserare. Valoarea pragului nu este critică
– aceasta este extrem de dependentă de calitatea codului generat.
48
3. Dacă ultimul operator al funcţiei este un apel la această funcţie, atunci se
numeşte coadă-recursivă. Aceasta are sens să fie înlocuită prin iteraţii – în
acest caz mai bine de utilizat stiva.
4. După rupere mai întîi se sortează prima secţiune. Aici, de asemenea, mai
bine de utilizat stiva, întrucît secţiunile mici se sortează mai rapid şi ele au
nevoie de o stivă mai mică. Cerinţele de memorie sunt reduse de la n la logn.
49
Arbori AVL
A elaborat: Andrie ș Patricia-Georgiana
Pîrlea Ana-Maria
Grupa: TI 191ro
Definirea
Arborelui Avl
v Se numeste înalțime a unui arbore ca fiind
celui mai lung drum de la nodul rădacină la
nodurile terminale.
v Se numește factor de echilibrare diferen
înalțimea subarborelui drept și înălțimea su
stâng.
v Atașând fiecărui nod un câmp care reprezint
de echilibrare al său, se spune ca arborele
echilibrat când toți factorii de echilibrare ai
sunt -1,0,+1.
Exemplu printr-
desen
Exemple de proceduri pentru calc
Înălțimii unui subarbore și a facto
echilibru
ØÎnălțimea întregului arbore este 5.
ØÎnălțimea subarborelui stâng al radă
ØPentru a afla factorul de echilibru al
scădem înălțimea subarborelui stâng
subarborelui drept.
ØFactorul de echilibru al nodului cu e
ØFactorul de echilibru al nodului cu e
üPractic, arborii AVL se comportă la fel ca arborii binari ordonați s
în cazul operațiilor de inserție și suprimare de chei .
üO inserție într-un arbore binar ordonat poate duce la dezechili
noduri, dezechilibrare manifestată prin nerespectarea formulei | hs –
respectivele noduri.
üÎn principiu, o cheie se inserează într-o primă fază, ca și într-
ordonat obișnuit, adică se pornește de la rădăcină și se urmează fi
drept, în funcție de relația dintre cheia de inserat și cheia noduri
trece, până se ajunge la un fiu nul, unde se realizează inserția propri
üÎn acest moment se parcurge drumul invers (care este unic) și s
drum primul nod care nu este echilibrat, adică primul nod ai cărui
ca înălțime prin 2 unități.
üAcest nod trebuie echilibrat și el se va afla întotdeauna într-unul di
•Cazul 1-rotație simplă dreaptă
• Cazul 2-rotație simplă stangă – este simetric în o
de cazul 1-rotatie simplă dreaptă
• Cazul 3-
rotație dublă
dreaptă
•Cazul 4-rotație dublă
stângă – simetric în
oglindă față de cazul
3-rotație dublă
dreaptă
• Dându-se un arbore cu radacina R si având subarborii stâng si drept not
de inaltimi hs si hd, la insertia unui nod în S se disting trei cazuri:
• 1. hs=hd - în urma insertiei, S si D devin de înaltimi inegale, verificând în
echilibrului;
• 2. hs<hd - în urma insertiei S si D devin de înaltimi egale, deci echilibrul
îmbunatateste;
• 3. hs>hd - criteriul echilibrului nu se mai verifica, arborele trebuie reech
• Cazurile posibile care pot duce la necesitatea reechilibrarii se pot sintetiz
urmatoarele, tinand cont de subarborele in care se face insertia (subarbo
drept) si de locul in cadrul subarborelui.
Reechilibrarea la inserția
arbori AVL - Caz 1 stâng
• Un algoritm pentru inserție si reechilibrare depinde de maniera în care e mem
referitoare la situația echilibrului arborelui.
• O soluție e cea în care aceasta informație nu e inclusă în structura arborelui, a
de echilibrare al unui nod afectat de inserție, trebuind să fie determinat de fie
la mărirea regiei.
• Cu soluția a doua, inserția unui nod presupune etapele:
• 1. parcurgerea arborelui binar pentru determinarea locului de inser ție sau a e
cheii de inserat;
• 2. inserția noului nod și determinarea factorului de echilibrare corespunzator
• 3. revenirea pe drumul de căutare și verificare a factorului de echilibru pentr
presupune unele verificări redundante: dacă echilibrul e stabilit, nu ar mai fi
factorului de echilibru pentru strămoșii nodului.
• Operatia de reechilibrare constă dintr-o secvență de reasignari de pointeri ( p
schimbați ciclic, rezultând fie o simplă, fie o dublă rotație a două sau trei nod
reajustări ale factorilor de echilibru.
•
•
• Cel mai simplu se pot șterge nodurile terminale și cele care au
fiu. Daca nodul care trebuie să fie șters are doi fii, îl înlocuim
aflat în extremă dreapta a subarborelui său stâng. Echilibrare
necesară numai dacă în urma ștergerii s-a redus înalțimea sub
cazul cel mai defavorabil ștergerea unui nod se realizează in O
operații.
Exemplu: Se consideră arborele din imagine. Î
ștergerii nodului cu cheia 6 apare necesitatea ech
Exem
plu:
• Se va considera secvența de chei 4,5,7,2,1,3,6 care se inse
arbore AVL ini țial vid. Evoluția arborelui și echilibrările sun
Алгоритм Кнута-Морриса-Пратта
Au elaborat:
Sîrbu Eudochia CIB-112
Calmic Anna CIB-113
Razmeriţa Eugeniu CIB-112
Verificat de:
Tutunaru Serghei, conferenţiar
universitar, doctor.
Алгоритм Кнута-Морриса-
Пратта
§ Алгоритм Кнута-Морриса-Пратта - алгоритм
поиска образца P[0..m] (подстроки) в исходной
строке-тексте T[0..n], где n >= m.
T[0..n]
P[0..m]
P
Пример:
Π(aba) = a
Π(abracadabra) = abra
Π(ab) = ξ (пустая строка)
Алгоритм Кнута-Морриса-
Пратта
Как и для чего применяется эта функция
?
ØДанная функция применяется для того,
чтобы помочь нам построить k-вектор длин
этих префиксов, после чего мы сможем
определить, содержится ли образец в
источнике и / или с каким количеством
позиций мы должны переместить образец
вправо, чтобы искать новое совпадение.
Алгоритм Кнута-Морриса-
Пратта
Исходная m a m m a m m c a b m m m c a b m m c
строка:
Образец: m m c a b m m c
53
Алгоритм Кнута-Морриса-
Пратта
Получаем вектор K для нашего искомого слова-
образца:-1 -1 1 0 0 -1 -1 2
K[i]=-1, когда:
- Первый элемент всегда равен -1 ;
- p[i]=p[0].
K[i]=0, когда:
- p[0] не равно p[i] & π (p[0..i-1])=ξ(пустая строка), если
π (p[0..i-1]) существует
K[i]=длина(π (p[0..i-1]))
Алгоритм Кнута-Морриса-
Пратта
Изучаем ход алгоритма, используя следующую
ситуацию:
Исходная m a m m a m m c a b m m m c a b m m c
строка:
Образец: m m c a b m m c
55
Алгоритм Кнута-Морриса-
Пратта
Исходная m a m m a m m c a b m m m c a b m m c
строка:
Образец: m m c a b m m c
56
Алгоритм Кнута-Морриса-
Пратта
Исходная m a m m a m m c a b m m m c a b m m c
строка:
Образец: m m c a b m m c
Образец m m c a b m m c
:
Образец: m m c a b m m c
Образец: m m c a b m m c
60
Алгоритм Кнута-Морриса-
Пратта
În continuare am prezentat codul programului efectuat in C++ :
Алгоритм Кнута-Морриса-
Пратта
Алгоритм Кнута-Морриса-
Пратта
Algorimul Knuth-Morris-Pratt
Au elaborat:
Sîrbu Eudochia CIB-112
Calmic Anna CIB-113
Razmeriţa Eugeniu CIB-112
Verificat de:
Tutunaru Serghei, conferenţiar
universitar, doctor.
Algoritmul Knuth-Morris-Pratt
§ Algoritmul Knuth-Morris-Pratt reprezinta o metoda
avansata de cautare a unui sir de caractere –
model(pattern) P[0..m] intr-un sir de caractere sursa
T[0..n], evident n>=m.
T[0..n]
P[0..m]
P
Exemplu:
Π(aba) = a
Π(abracadabra) = abra
Π(ab) = ξ (linie goala)
Algoritmul Knuth-Morris-Pratt
Cum se aplica aceasta functie si pentru
ce?
ØSe aplica pentru a ne ajuta sa construim o
matrice k a lungimilor acestor prefixe, dupa
care vom putea determina daca modelul se
contine in sursa sau/si cu cite pozitii trebuie
sa deplasam modelul spre dreapta pentru a
cauta o noua potrivire.
Algoritmul Knuth-Morris-Pratt
Sursa: m a m m a m m c a b m m m c a b m m c
Model: m m c a b m m c
71
Algoritmul Knuth-Morris-Pratt
Avem obtinut pentru pattern-ul nostru tabloul K:
-1 -1 1 0 0 -1 -1 2
K[i]=-1, atunci cind:
- Primul element intotdeauna este egal cu -1;
- p[i]=p[0].
K[i]=0, atunci cind:
- p[0] nu este egal cu p[i] & π (p[0..i-1])=ξ(linie goala),
daca π (p[0..i-1]) exista
K[i]=lungimea(π (p[0..i-1]))
Algoritmul Knuth-Morris-
Pratt
Vom studia mersul algoritmului pentru urmatoarea situatie:
Sursa: m a m m a m m c a b m m m c a b m m c
Model: m m c a b m m c
Model: m m c a b m m c
74
Algoritmul Knuth-Morris-Pratt
Sursa: m a m m a m m c a b m m m c a b m m c
Model: m m c a b m m c
Model: m m c a b m m c
76
Algoritmul Knuth-Morris-Pratt
Sursa: m a m m a m m c a b m m m c a b m m c
Model: m m c a b m m c
Model: m m c a b m m c
78
Algoritmul Knuth-Morris-
Pratt
În continuare am prezentat codul programului efectuat in C++ :
Algoritmul Knuth-Morris-Pratt
Algoritmul Knuth-Morris-Pratt
PROBLEMA 3 (OPȚIONAL)
Ex: a=factorr(4)=1*2*3*4=24
b=factorr(n)=1*2*3*…*n-2*n-1*n
Primul pas al programului este sa introducem “n” de
la tastatură și elementele vectorului a[n].
Ex: a=factorr(4)=1*2*3*4=24
b=factorr(n)=1*2*3*…*n-2*n-1*n
Primul pas al programului este sa introducem “n” de
la tastatură și elementele vectorului a[n].
Executat de Nastas Vasile
Grupa TI-121
2 Octombrie 2013
Ce este recursivitatea ?
Recursivitatea functiei este un mod de a defini unele
funcții,care folosește o referire la ea însăși
Pe parcursul său se verifică o condiţie a cărei
nesatisfacere implică reluarea execuţiei modulului de
la începutul său;
Ea e de 2 tipuri : liniara (cind apeleaza la ea insusi)
si arbore
Unde se aplica algoritme recursive
N! (Factorial)
Numerele Fibonacci
Problema Turnurilor Hanoi
Coeficienti Binominali
Sortarea
Algoritmul lui Euclid(cel mai mare divizor
comun)
Algoritmul Dijkstra(permutari)
Etc.
Numerele lui Fibonacci
În șirul de numere al lui
Fibonacci, fiecare număr
reprezintă suma a două
numere anterioare,
începând cu 0 și 1. Astfel,
șirul incepe cu 1, 1, 2, 3,
5, 8, 13, 21, 34, 55, 89,
144, 233, 377, 610 etc
Numerele lui Fibonacci
Iterativ Recursiv
int fib (int N) int fib (int N)
{ int k1, k2, k3; { if ((N == 1) || (N == 2))
k1 = k2 = k3 = 1; return 1;
for (int j = 3; j <= N; j++) else return (fib (N-1) + fib
{ k3 = k1 + k2; (N-2)); }
k1 = k2; k2 = k3; }
return k3; }
Timpul de executie a
Nr. Lui Fibonacci (timpul milisec)
N 5 25 125 625
Iterativ 0 2 67 344
800
700
Caracteristicile PC : 600
Win7 32 bit 500
Intel Core Duo 3.00 GHz 400
Recursiv
RAM 4.00 GB 300
Iterativ
Compiler:
200
Microsoft Visual Studio 2012
100
5 25 125 625
Schema cum compileaza programul
Numarul lui Fibonacci (Recursiv)
350000
Caracteristicile PC : 300000
Win7 32 bit
250000
Intel Core Duo 3.00 GHz
RAM 4.00 GB 200000 Recursiv
Microsoft Visual Studio 2012 100000
50000
3 5 10 15
Codul iterativ integral
#include<iostream>
using namespace std;
void move( int numNestedCalls, int n, char*s, char*i, char*d ) {
static int stepNumber = 0;
if (n > 0) {
for (int j = 0; j < 3 * numNestedCalls; j++)
cout << " ";
stepNumber += 1;
cout << "Move " << n << " disks from " << s << " to " << d << " (depth = " <<
numNestedCalls << ", step " << stepNumber << ")\n"; }
if( n > 0 ) {
move( numNestedCalls + 1, n-1,s,d,i );stepNumber += 1;
for (int j = 0; j < 3 * numNestedCalls + 3; j++)
cout << " ";
cout << "disk " << n << " is moved from " << s << " to " << d << " (depth = " <<
numNestedCalls << ", step " << stepNumber << ")\n";move( numNestedCalls + 1, n-1,i,s,d );}
if (n > 0) {
stepNumber += 1;
for (int j = 0; j < 3 * numNestedCalls; j++)
cout << " ";
cout << "End Move " << n << " disks from " << s << " to " << d << " (depth = " <<
numNestedCalls << ", step " << stepNumber << ")\n"; } }
int main() {
cout << "\n************************************************ **********\n";
cout << "This C++ program is to solve the towers of hanoi problem";
cout << "\n************************************************ **********\n";
cout << "Enter the no. of disks ";
int n;
cin >> n;
move( 1, n, "1", "2", "3" );
return 0; }
Codul recursiv integral
#include <iostream>
using namespace std;
void move(int n,char x,char z,char y)
{
if(n==1) cout<<"Move disk 1 from "<<x<<" to "<<z<<endl;
else
{
move(n-1,x,y,z);
cout<<"Move disk "<<n<<" from "<<x<<" to"<<z<<endl;
move(n-1,y,z,x);
}
}
int main()
{
int n;
cout<<"Enter the number of disks inTOH:";
cin>>n;
move(n,'A','C','B');
system("pause");
return 0;
}
Avantajele
Micsoreaza dimensiunea codului
Se reduce complexitatea,devine mai usor de inteles
Se implementeaza mai usor in mai multe cazuri de tip
real(ex. un program pentru a afișa o listă cu toate
fișierele de sistem nu poate fi rezolvată fără
recursivitate)
Recursivitatea este foarte flexibil în structura de date
cum ar fi stive, cozi, lista legat si sortare rapida.
Evitarea de asteptare inutile de funcții
Dezavantajele
Este nevoie de spațiu de stocare suplimentar
Algoritmul recursiv nu este eficient in viteza de
executie si de timp
Soluția recursivă este întotdeauna logică și este foarte
dificil de a urmări. (debug și înțelege)
Trebuie de evitat valorile statice si globale
Surse de informare
http://my.safaribooksonline.com/book/programming/c/
9788131760314/functions/section_10.20
http://pages.cs.wisc.edu/~vernon/cs367/notes/6.RECU
RSION.html#iter
http://benpfaff.org/writings/clc/recursion-vs-
iteration.html
http://letslearncomputing.blogspot.com/2013/06/advan
tages-and-disadvantages-of.html
http://www.site.uottawa.ca/~ivan/tr-educ.pdf