Sunteți pe pagina 1din 34

I Cunoștințe specifice limbajului C++

1. Scrie o funcție care alocă o matrice de numere întregi, folosind operatorul new. (Continental Iași)
Răspuns:
int** allocMatrix(const int rows, const int cols)
{
int **pMatrix = new int*[rows];
for(int i = 0; i < rows; i++)
{
pMatrix[i] = new int[cols];
}
return pMatrix;
}

2. Scrie o funcție care alocă o matrice de numere întregi, folosind funcția malloc. (Continental Iași)
Răspuns:
int** allocMatrix(const int rows, const int cols)
{
int **pMatrix = (int**) malloc(sizeof(int*) * rows);
for(int i = 0; i < rows; i++)
{
pMatrix[i] = (int*) malloc(sizeof(int) * cols);
}
return pMatrix;
}

3. Care sunt diferențele dintre operatorul new și funcția malloc? (Continental Iași)
Răspuns:
1. Operatorul new returnează un pointer către tipul de dată alocat, pe când, funcția malloc
returneaza un pointer către tipul de dată void (sau pointer la obiect).
2. Operatorul new deduce dimensiunea tipului alocat, pe când funcția malloc trebuie să
primească numărul total de bytes ca parametru.
3. Operatorul new după alocarea memoriei, apelează constructorul clasei. (malloc nu)

4. Scrie o funcție care să dealoce o matrice alocată dinamic.


Răspuns: Pentru alocarea cu new, folosim ,,delete”, pentru alocarea cu ,,malloc” folosim ,,free”.
void deleteMatrixDelete(int **mat, const int rows, const int cols)
{
for(int i = 0; i < rows; i++) delete [] mat[i];
delete [] mat;
}
void deleteMatrixFree(int **mat, const int rows, const int cols)
{
for(int i = 0; i < rows; i++) free(mat[i]);
free(mat);
}
5. Cum se dealocă un șir de elemente alocate dinamic? (Continental Iași)
Răspuns:
const int length = 10;
int *dataWithMalloc = (int*)malloc(sizeof(int) * length);
free(dataWithMalloc);

int *dataWithNew = new int[length];


delete [] dataWithNew;

6. Care sunt diferențele dintre funcția ,,free” și operatorul ,,delete”? (Continental Iași)
Răspuns:
1. Free se folosește pentru dealocarea memoriei atunci când aceasta a fost alocată cu malloc.
2. Delete se folosește pentru dealocarea memoriei când aceasta a fost alocată cu new.
3. Pe lângă dealocarea memoriei, delete apelează destructorul clasei instanțiate.

7. Care e diferența dintre ,,++i” și ,,i++”? (Continental Iași)


Răspuns: Operatorul (prefixat) ++(<datatype>) incrementează argumentul și returnează valoarea
incrementată, pe când operatorul (postfixată) ++(<datatype>, int) incrementează valoarea
argumentului dar returnează valoarea antecedentă incrementării.
int a = 2, b = 2;
cout << ++a << " " << b++; //prints 3 2

8. Care este diferența dintre while și do-while? (Continental Iași)


Răspuns: Instrucțiunea do-while execută minim o dată instrucțiunile din scope pentru că
verificarea se face la sfârșitul execuției. Instrucțiunea while verifică întâi condiția și, dacă este
adevarată, execută instrucțiunile.

9. Cum se declară un pointer la o funcție care returnează un int și are ca parametrii două variabile
const int? Cum se apelează funcția prin intermediul noului pointer? (Cerner Brașov)
Răspuns:
int (*pf)(const int, const int);
int data = pf(1,2);

10. Care sunt difențele dintre C și C++? (Continental Iași, Cerner Brașov)
Răspuns:
1. În C nu există clase și structurile, uniunile, nu pot conține metode, pe când în C++ structurile,
uniunile pot avea metode.
2. În C, modul de formare a semnăturii unei funcții diferă față de C++, conținând numai
denumirea funcției. În C++ semnătura funcției conține denumirea și parametrii acesteia, ceea
ce face posibil polimorfismul ad-hoc. Putem avea mai multe funcții cu același nume dar cu
parametrii diferiți.
3. În C, putem omite declararea tipul de retur din antetul funcției, fiind implicit int.
4. În C nu există static_cast, dynamic_cast, reinterpret_cast, const_cast, specificatori de acces,
moștenire, polimorfism ad-hoc, polimorfism de moștenire, namespace-uri, cast de tip
constructor: int(data).
5. În C++ există referință, în C doar pointeri.
11. Scrie o funcție care să rotească un șir de caractere? (Continental Iași)(*)oper de diferentiere
Răspuns:
void reverse(char *data)
{
const int len = strlen(data);
for(int i = 0; i < len/2; i++)
{
char temp = data[i];
data[i] = data[len-1-i];
data[len-1-i] = temp;
}
}
void reverse(char *str)
{
char *end = str;
char *start = str;
while(*end) end++;

end--;

char tmp;
while(start < end)
{
tmp = *start;
*start++ = *end;
*end-- = tmp;
}
}

12. Scrie o funcție care să afișeze elementele unui șir de numere întregi. (Continental Iași)
Răspuns:
void printArray(int data[], const int length)
{
for(int i = 0; i < length; i++)
{
cout << data[i] << " ";
}
}
Pe lângă transmiterea șirului (a unui pointer către începutul acestuia) este nevoie de transmiterea
lungimii. Dacă încercăm să aflăm lungimea șirului folosind (const int length =
sizeof(data)/sizeof(*data)) vom obține un rezultat greșit fiind că sizeof(data) este egal cu
sizeof(int*), adică, pointerul la tipul de dată array se convertește la int*. Dacă vom scrie const int
length = sizeof(data)/sizeof(*data) în același scope cu declararea șirului vom obține numărul de
elemente.
template <size_t N> void printArray(int (&data)[N])
{
for(int i = 0; i < N; i++)
{
cout << data[i] << " ";
}
}
printArray(data);

Implementarea de mai sus funcționează corect deoarece compilatorul deduce argumentul N, chiar
dacă nu este transmis explicit. În acest caz, const length = sizeof(data)/sizeof(*data) funcționează
corect.

13. Scrie o funcție care să afișeze elementele unui tablou bidimensional. (Continental Iași)
Răspuns:
void printMatrix(int *matrix, const int mRows, const int nCols)
{
for(int i = 0; i < mRows; i++)
{
for(int j = 0; j < nCols; j++)
{
cout << matrix[j + i*nCols] << " ";
}
cout << endl;
}
}
printMatrix(&matrix[0][0], mRows, nCols);

Ffuncție este corectă deoarece liniile matricii alocate pe stivă sunt memorate una după alta. Dacă
matricea ar fi fost alocată în zona de memorie heap atunci această funcție nu va funcționa corect.

template <size_t M, size_t N>


void printMatrix(int (&matrix)[M][N])
{
for(int i = 0; i < M; i++)
{
for(int j = 0; j < N; j++)
{
cout << matrix[i][j] << " ";
}
cout << endl;
}
}
printMatrix(matrix); //works if matrix was declared matrix[<value>][<value>]
În funcția de mai sus, compilatorul deduce numărul de linii și de coloane (înlocuiește literalii
întregi in expresie) și afișează matricea corect.
14. Cum inițializezi un șir de numere întregi în momentul declarării, cu valoarea 0? Dar un tablou
bidimensional? (Continental Iași)
Răspuns:
int data[10] = {0};
int table[rows][cols];
for(int r = 0; r < rows; r++)
for(int c = 0; c < cols; c++)
table[r][c] = 0;

15. Ce se întamplă dacă se apelează un constructor de copiere de forma ,,classname (const classname
other)”? (Luxsoft București)
Răspuns: Nimic, compilatorul avertizează prin eroarea ,,invalid copy constructor”, altfel
programul va intra într-o buclă infinită (testat cu mingw).

16. Câte tipuri de cast există în C++? Când se folosesc fiecare? (Continental Iași, Cerner Brașov)
Răspuns:
1. static_cast<new_type>(data) - produce o eroare în unele cazuri, eliminând astfel potențiale
buguri. Se flosește pentru conversii aritmetice, pointer to pointer și nu pointer to non-pointer
sau const pointer la pointer. Este agreat pentru conversiile pe care compilatorul le poate
verifica.
2. const_cast<new_type>(data) – pentru a elimina atributul const sau volatile al tipului de date
către care pointează.
3. dynamic_cast<new_type>(data) – funcționează doar pentru pointeri sau referințe către
obiecte polimorfice. Dacă se face up-cast și obiectul către care pointează nu este de tipul la
care se face cast, va returna null.
4. reinterpret_cast<new_type>(data) – pot face cast la orice mai puțin ce face const_cast. Se
folosesc aceste tipuri de cast pentru ca fac verificări în plus și deci sunt mai sigure (cu
excepția lui reinterpret_cast).

17. Cine poate avea acces la metodele și membrii ,,private”? (Continental Iași)
Răspuns: Funcțiile și clasele friend sau clasa care le declară.

18. Poti inițializa variabilele membre ale unei clase în momentul declarării în C++98?
Răspuns: Da, doar dacă sunt const.
class MyClass
{
public:
const int data = 3;
};

In C++ 11 sau 14 se pot inițializa membrii unei clase în momentul declarării ({}, =).
int a{3}; /* sau */ int b = 3; //in C++11, C++14

19. Care sunt diferențele dintre STL vector și STL set?


Răspuns: STL set nu poate conține valori duplicate, pe când vector da. STL set este implementat
folosind un arbore binar de căutare, ceea ce înseamnă că adăugarea unui nou element, căutarea,
ștergerea se realizează în O(log(n)) pentru un arbore echilibrat (blanaced).
Inserarea într-un vector se realizează în timpul amortizat O(1), accesul în timpul O(1), căutarea –
în funcție de algoritm, ștergerea în O(1). Vectorul folosește, de cele mai multe ori mai mult spațiu
decât un set, dar nu păstrează elementele sortate.

20. Care este diferența dintre vector și listă? (Cerner Brașov)


Răspuns: Într-o listă, inserarea, ștergerea, adăugarea se realizează în timp O(1) însă, accesul se
realizează în timp O(n) (n – numărul de elemente). Într-un vector inserarea în O(n), adăugarea se
face în timp O(1) amortizat, ștergerea în O(n). Vectorul se dublează în capacitate atunci când se
dorește o nouă adăugare și acesta este plin.

21. Cum se citește un vector de la tastatură? Cum se ordonează un vector? (Continental Iași)
Răspuns:
int main()
{
vector<int> myVector;
int size = 0;
cout << "Dati numarul de elemente: ";
cin >> size;
myVector.reserve(size);
for(int i = 0; i < size; i++)
{
int number = 0;
cin >> number;
myVector.push_back(number);
}
sort(myVector.begin(), myVector.end());
return 0;
}

22. Poți să faci upcast între obiecte aflate într-o relație de moștenire?
Răspuns: Da, este des întâlnită această operație.
class Base
{
public: Base(){}
};

class Derived : public Base


{
public: Derived(){}
};
void upcast(Base b){}
//call: upcast(Derived());

23. Cand se foloseste forward declaration?


Răspuns: Atunci când vrem să definim o clasă care să conțină un pointer sau referință către o altă
clasă definită mai jos. Nu putem însă, apela metodele obiectului în clasa care conține pointerul.
class Wheel; //forward declaration
class Car
{
Wheel *wheel;
public: Car(){//eroare : wheel->turn();}
};

24. Ce este o metodă virtuală? (Continental Iași)


Răspuns: O metodă virtuală este o metodă care este precedată de cuvântul cheie virtual. O
metodă virtuală poate fi redefinită în clasele derivate și clasele care le conțin (clase polimorfe).
Clasele polimorfe conțin un pointer către un tabel ,,vtable” unde sunt înregistrate adresele
metodelor virtuale.

25. Ce este o metodă pur virtuală? (Continental Iași)


Răspuns: O metodă pur virtuală este o metodă de forma:
class Vehicle
{
public: virtual void drive() = 0;
};

O clasă care conține o metodă pur virtuală se numește clasă abstractă și nu poate fi instanțiată.
Corpul metodei pur virtuale (dacă se dorește instanțierea) trebuie definit în clasa derivată.

26. Care este diferența dintre o clasă pur virtuală și o clasă virtuală? (Continental Iași)
Răspuns: O clasă polimorfă poate fi instanțiată, pe când o clasă pur virtuală nu.

27. Care sunt specificatorii de acces și ce vizibilate au? (Cerner Brașov, Continental Iași)
Răspuns:
Specificator Clasa curentă Clasa derivată Accesibil prin obiect
public DA DA DA
protected DA DA NU
private DA NU NU

28. Care sunt diferențele dintre structură și clasă în C++? (Continental Iași, Cerner Brașov)
Răspuns:
1. Membrii unei clase sunt implicit ,,private”, iar la structură sunt ,,public”.
2. Într-o relație de moștenire, specificatorul de acces (: <specificator> Base) implicit în cazul
claselor este private iar în cazul structurilor este public.

29. Câte tipuri de moștenire sunt în C++? (Continental Iași)


Răspuns: Din punct de vedere al acesului la membrii din clasa de bază moștenirea poate fi
public, private și protected.
Din punct de vedere al numărului de instanțe ale clasei de bază (în cazul unei scheme ,,diamond”)
avem moștenirea virtuală.
Din punct de vedere al numărului de clase pe care le moștenește o clasă derivată, avem moștenire
simplă și moștenire multiplă.
30. Cum poți simula o intefață (interface) Java în C++? (Continental Iași)
Răspuns: O intefață în C++ poate fi considerată o clasă pur virtuală (cu toate metodele pur
virtuale).

31. Ce este un smart pointer? (Cerner Brașov)


Răspuns: Un smart pointer este un pointer care știe să dealoce memoria către care indică. În
C++11 există unique_ptr pentru cazul în care un singur pointer indică către zona de memorie sau
shared_ptr pentru cazul în care mai mulți pointeri indică către zona de memorie. Shared_ptr
folosește un reference count, care se decrementează până la 0 în destructor. Atunci când
referenceCount ajunge la 0, se dealocă memoria.

32. Câte caractere sunt în tipul char? (Continental Iași)


Răspuns: 255 (2 pow(sizeof(char)) – 1)

33. Care este dimensiunea tipului word? (Continental Iași)


Răspuns: Pe o arhitectură x86 este 32 biți, iar pe o arhitectură x64 este 64 biți.

34. Cum definești un nou tip de dată în C++? (Continental Iași)


Răspuns:
typedef unsigned char byte;

35. Cum poți afla tipul unui obiect la runtime? (Cerner Brașov)
Răspuns: Cu funcția typeid(<obiect>). (Pentru ierarhii de clase: dacă obiectul este polimorfic).
class Animal
{
public:
Animal(){};
virtual ~Animal(){};
};
class Cat : public Animal{};
class Dog : public Animal{};

void findType(Animal *animal)


{
if(typeid(*animal) == typeid(Cat)) cout << "Miau";
else if(typeid(*animal) == typeid(Dog)) cout << "Hau";
}

36. Ce reprezintă funcțiile și metodele inline? (Continental Iași)


Răspuns: Funcțiile și metodele inline reprezintă un mod de a executa o secvență de cod fără
a folosi mecanismul de apelare a unei funcții obișnuite. Spre deosebire de macro, funcțiile inline
oferă type checking. Orice metodă definită în interiorul clasei este automat inline. Pentru a obține
o funcție inline, trebuie ca aceasta să fie declarată și definită în același loc.
37. Cum poti inițializa o variabilă const sau referință non-statică, membră a unei clase? (Continental
Iași)
Răspuns: O variabilă const sau referință poate fi inițializată în lista de inițializare a
constructorului.
class Wheel{};
class Car
{
const int miles;
Wheel &wheel;
public:
Car(int miles, Wheel &wheel) : miles(miles), wheel(wheel){}
};

38. Ce sunt clasele sau funcțiile template? (Continental Iași)


Răspuns: Funcțiile sau clasele template stau la baza programării generice. Funcțiile sau clasele
template pot lucra cu diverse tipuri de date.
template<class T> class MyVector
{
public:
T data[];
};
template<class T> void printMe(T data){}

39. Care este diferența dintre typename și class în declararea argumentelor template?
Răspuns: Cuvântul typname indică compilatorului să trateze data ca un tip și nu ca o clasă, deci
nu o să apeleze vreun constructor.
template<class T> class X
{
public:
T data[];
typename T::id xid;
};

class Y
{
public: int id;
};

40. De ce metodele template trebuie puse în fișierele .h corespondende declaratiei clasei și nu în


fișierere.cpp corespondente implementării claselor. De aici și postfixul de *_Impl.h?
Răspuns: Implementarea unei clase template trebuie să fie în fișierul.h (in line) pentru că
argumentele template (T) sunt înlocuite cu tipurile concrete și compilatorul are nevoie să știe tipul
în momentul compilării. De aici și postfixul de *_Impl.h.

41. Care este diferența dintre un pointer și referință? (Stabiplan Brașov)


Răspuns: Un pointer poate să își schimbe obiectul către care pointează, pe când referința nu. O
referință nu poate lua valoarea NULL, pe când un pointer poate. O referință trebuie inițializată tot
timpul, pe când un pointer nu. Pointerul suportă operații aritmetice, pe când referința nu.
Referința mai este numită și ,,syntactic sugar”.
42. Scrie un program care să sumeze toate elementele negative dintr-un șir de întregi. (Continental
Iași)
Răspuns:
int getNegativeSum(const int *data, const int length)
{
int sum = 0;
for(int i = 0; i < length; i++)
{
if(data[i] < 0) sum += data[i];
}
return sum;
}

43. Câte zone de memorie sunt? (Continetal Iași)


Răspuns:
1. static memory (zona de memorie statică) unde se memorează variabilele statice și literalii
de tip șir de caractere sau variabilele static și const.
2. automatic memory (stack/stivă) unde se memorează variabelele non-statice, în cadrul
unei funcții.
3. free store (aka heap) unde se memorează datele alocate cu new sau funcțiile alloc.

44. Cum arată stiva? (Luxsoft București)


Răspuns: Dacă vrem să fim specifici, stiva va arăta în funcție de optimizările compilatorului. De
exemplu, dacă o variabilă dintr-o funcție este accesată de foarte multe ori, complilatorul o poate
pune într-un registru. În general vorbind, stiva conține ceea ce funcția dorește să pună. Stiva poate
conține:
- Adresa la care să se întoarcă după execuție
- Un pointer la stiva (începutul apelului)
- Regiștrii care trebuie păstrați când funcția returnează (sau parametrii funcției)
- Variabile locale
- Paremetrii pentru funcția următoare care trebuie apelată
Stiva sistem evoluează de la adrese mari la adrese mici. În interiorul unei funcții, variabilele
locale sunt plasate în ordine inversă apariției lor în lista declarației. La fel și pentru parametrii
funcției (plasați la valori mai mari).
45. Ce sunt ,,move constructor” și ,,move assignment operator”?
Răspuns:
class Test
{
public:
Test() {}
Test(Test &&other)
{
cout << "move constructor" << endl;
}
Test& operator=(Test &&other)
{
cout << "move assignment operator" << endl;
return *this;
}
void makeOperations(Test &other){}
};

Test makeOperations(Test &obj)


{
Test newTest;
newTest.makeOperations(obj);
return newTest;
}

int main()
{
vector<Test> tests;
tests.push_back(Test()); //(1) - move constructor
Test result;
result = makeOperations(result); //(2) - move assignment operation
return 0;
}

Move constructor și move assignment operator sunt adăugați în C++11 pentru a reduce
numărul de copii temporare. Unele compilatoare optimizează aceste operații (constructor
elission).
Constructorul de copiere, se apelează la (1) și garantează apelul constructorului Test() o singură
dată evitând astfel crearea altor obiecte temporare.
Operatorul move assignment operator se apelează la (2) și garanteaza apelul
constructorului Test() o singură dată, evitând astfel crearea altor obicete. În C++98, în acest caz
se crea un obiect temporar(newTest pe stivă) și se apela constructorul de copiere.

46. Care este valoarea lui 0x2F în decimal? Dar a lui 14 (dec) în binar? (Continental Iași)
Răspuns: Reprezentarea lui 14 în binar este 00001110. 0x2F în dec este 47.
47. Scrie o funcție care să realizeze transformările în hex/octal/bin? (Continental Iași)
Răspuns:
int convertFromBase(string number, int base)
{
if(base < 2 || (base > 10 && base != 16)) return -1;
int length = number.size();
int result = 0;
for(int i = length-1; i >= 0; i--)
{
int digit = number[i] - '0';
if(digit < 0 || digit >= base) return -1;
int exp = length-1-i;
result += digit * pow(base, exp);
}
return result;
}
cout << convertFromBase("0110", 2) << endl;
cout << convertFromBase("0110", 8) << endl;

48. Scrie o funcție care să calculeze cel mai mare divizor comun și cel mai mare multiplu comun.
Răspuns:
unsigned getCmmdc(unsigned a, unsigned b)
{
while(b != 0)
{
int rem = a%b;
a = b;
b = rem;
}
return a;
}
unsigned getCmmmc(unsigned a, unsigned b)
{
return (a*b)/getCmmdc(a,b); //
}

49. Fie următoarele informații într-un byte: 00101101, considerând biții 5 și 4 ca fiind 10. Care este
LSB? (Continental Iași)
Răspuns: 00101101.
50. Ce face următoarea secvență de cod? (Continental Iași)
int x = 0;
while(x < 5)
{
printf("x=%d\n", x);
x++;
}

Care este valoarea pe care x o are la sfârșitul programului? Scrie valorile lui x la fiecare pas.
Răspuns: Secvența de cod afișează numerele de la 0 la 4 (inclusiv). La sfârșit x este 5.
Pas Afișare Valoare x
1 x=0 1
2 x=1 2
3 x=2 3
4 x=3 4
5 x=4 5

51. Care sunt valorile variabilelor mixerproduct și Tvproduct dacă datele de intrare au valorile -10, 4,
100, 999, 1000, 1001, 3500, 7000? Specifică valorile celor 2 variabile pentru fiecare input.
(Continental Iași)
if(mixerproductcost > 0 && mixerproductcost <= 100)
{
mixerproduct = "mixerlowcost";
}
if(mixerproductcost >= 100 && mixerproductcost <= 350)
{
mixerproduct = "mixerhightcost";
}
if(TVproductcost >= 1000 && TVproductcost <= 2000)
{
Tvproduct = "TVLowcost";
}
if(TVproductcost > 2000 && TVproductcost <= 3500)
{
Tvproduct = "TVHightcost";
}

Răspuns:
mixerproductcost TVproductcost mixerproduct Tvproduct
-10 -10 - -
4 4 mixerlowcost -
100 100 mixerhightcost -
999 999 - -
1000 1000 - TVLowcost
1001 1001 - TVLowcost
3500 3500 - TVHighcost
7000 7000 - -
52. Ce face următoarea secvență de cod? Care sunt valorile pentru variabilele factorial și counter la
sfârșitul programului? (Continental Iași)
int counter = 7;
int factorial = 1;
do
{
factorial = factorial * counter;
counter = counter-1;
}while(counter > 0);
printf("factorial of 7 is %d\n", factorial);

Răspuns:
Secvența de cod calculează factorial de 7. La sfârșitul programului, variabila factorial va avea
valoarea 5040 și counter va avea valoarea 0.
II Cunoștințe specifice algoritmilor și structurilor de date
1. Scrie o funcție care să codifice 09.09.2009 în format BCD. Cum o să arate rezultatul și care va fi
dimensiunea lui? (“.” nu se ia în considerare) (Continental Iași)
Răspuns: Dimensinea rezultatului va fi de 4 bytes – 1 int.
Rezultatul va arăta: 0000(0) 1001(9) 0000(0) 1001(9) 0010(2) 0000(0) 0000(0) 1001(9)
int getDigit(char c)
{
return c >= '0' && c <= '9' ? c - '0' : -1;
}
int toBcd(const char *data)
{
int len = strlen(data);
int shifter = 0;
int bcd = 0;
for(int i = len-1; i >= 0; i--)
{
int digit = getDigit(data[i]);
if(digit == -1) continue;
bcd |= (digit << shifter);
shifter += 4;
}
return bcd;
}
2. Scrie o funcție care să sorteze crescător o listă simplu înlănțuită.
Răspuns: Pentru cazul în care se dorește o sortare modificând valorile listei.
Node *getMiddle(Node *start, Node *end)
{
if(end == nullptr || start->next == end) return start;

Node *slow = start;


Node *fast = start->next;
while(fast != end->next && fast->next != end->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
void sort(Node *start)
{
if(start == nullptr || start->next == nullptr) return;
int count = 0;
Node *end = start;
while(end->next != nullptr)
{
count++;
end = end->next;
}
count += 1;
unique_ptr<int[]> helper = make_unique<int[]>(count);
mergeSort(start, end, helper);
}
void mergeSort(Node *start, Node *end, unique_ptr<int[]> &helper)
{
if(start == end) return;

Node *middle = getMiddle(start, end);


mergeSort(start, middle, helper);
mergeSort(middle->next, end, helper);

merge(start, end, helper);


}
void merge(Node *start, Node *end, unique_ptr<int[]> &helper)
{
int count = 0;
Node *node = start;
while(node != nullptr && node != end->next)
{
helper[count] = node->data;
count++;
node = node->next;
}
int left = 0;
int mid = count/2;
int right = mid+1;
while(left <= mid && right < count)
{
if(helper[left] <= helper[right])
{
start->data = helper[left++];
}
else
{
start->data = helper[right++];
}
start = start->next;
}
while(left <= mid)
{
start->data = helper[left++];
start = start->next;
}
}
Pentru cazul în care se dorește o sortare modificând doar legăturile listei.
Node* split(Node *head)
{
if(head==nullptr || head->next==nullptr)
{
return nullptr;
}
Node *slow = head;
Node *fast = slow->next;
while(fast!=nullptr && fast->next!=nullptr)
{
slow = slow->next;
fast = fast->next->next;
}
Node *result = slow->next;
slow->next = nullptr;
return result;
}
Node* merge(Node *first, Node *second)
{
if(first == nullptr) return second;
else if(second == nullptr) return first;

Node *result = nullptr;


if(first->data <= second->data)
{
result = first;
result->next = merge(first->next, second);
result->next->prev = first;
}
else
{
result = second;
result->next = merge(first, second->next);
result->next->prev = second;
}
return result;
}
Node* mergeSort(Node *head)
{
if(head==nullptr || head->next==nullptr)
{
return head;
}
Node *rightHalf = split(head);
mergeSort(head);
mergeSort(rightHalf);
return merge(head, rightHalf);
}
3. Cum se reprezintă un nod dintr-o listă simplu înlănțuită, listă dublu înlănțuită, arbore binar,
arbore general, graf? (Continental Iași)
Răspuns:
class DoubleLinkedListNode
{
public:
int data;
DoubleLinkedListNode *next;
DoubleLinkedListNode *prev;
};
class BSTNode
{
public:
int id;
BSTNode *left;
BSTNode *right;
};
class TreeNode
{
public:
int id;
vector<TreeNode*> children;
};
class Node
{
public:
int id;
vector<Node*> children;
};
4. Scrie o funcție care să genereze N regine care să nu se atace pe o tablă de șah N x N. (Continental
Iași).
Răspuns: Vom încerca să plasăm, pe fiecare coloană, câte o regină. Dacă acest nu va fi posibil cu
un aranjament anterior, vom încerca o altă poziție pentru regina anterioară.
bool isAttacked(int columns[], int row, int column)
{
for(int c = 0; c < column; c++)
{
if((columns[c] == row) ||
abs(column - c) == abs(row - columns[c]))
{
return true;
}
}
return false;
}
void placeQueens(int columns[], int size, int column)
{
if(column == size)
{
printTable(columns, size);
return;
}
for(int row = 0; row < size; row++)
{
if(!isAttacked(columns,row,column))
{
columns[column] = row;
placeQueens(columns, size, column+1);
}
}
}
5. Scrie o funcție care să determine cel mai frecvent număr dintr-un șir de numere întregi.
(Continental Iași)
Răspuns: În cazul în care avem un șir de cifre, putem folosi un șir de întregi care să reprezinte
numărul de apariții a fiecărui digit.
int getMostFrequentDigit(const vector<int8_t> &array)
{
int counts[10] = {0};
int8_t mostFrequent = 0;
int maxCount = 0;
for(auto &i : array)
{
counts[i]++;
if(maxCount < counts[i])
{
maxCount = counts[i];
mostFrequent = i;
}
}
return mostFrequent;
}

În cazul în care avem un șir de numere care se află într-un interval bine definit relativ mic, vom
folosi în aceeași manieră ca cea de mai sus, un șir care să memoreze frecvența fiecărui număr.
int getMostFrequentRange(const vector<int> & array)
{
if(array.size() == 0) return -1;

int minValue = INT_MAX;


int maxValue = INT_MIN;
for(auto &i : array)
{
if(minValue > i) minValue = i;
if(maxValue < i) maxValue = i;
}
int length = maxValue - minValue + 1;
auto counts = make_unique<int[]>(length);
int mostFrequent = 0;
int maxCount = 0;
for(auto &i : array)
{
counts[i - minValue]++;
if(counts[i-minValue] > maxCount)
{
maxCount = counts[i-minValue];
mostFrequent = i;
}
}
return mostFrequent;
}
În cazul în care nu știm un interval bine stabilit al valorilor numerelor (diferit față de cel al int-
ului) vom folosi un hashtable (unordered_map) pentru păstrarea frecvențelor.
int getMostFrequentLarge(const vector<int> &arr)
{
if(arr.size() == 0) return -1;
unordered_map<int, int> counts;
int mostFrequent = 0;
int maxCount = 0;
for(auto &i : arr)
{
int newCount = 1;
auto it = counts.find(i);
if(it == counts.end())
{
counts[i] = newCount;
}
else
{
newCount = counts[i] + 1;
counts[i] = newCount;
}
if(newCount > maxCount)
{
maxCount = newCount;
mostFrequent = i;
}
}
return mostFrequent;
}
Dacă dimensiunea șirului este foarte mare, atunci, vom salva în fișiere text după o formulă de
element % 10. Vom calcula pentru fiecare fișier maximul cel mai frecvent număr și numărul de
apariții. Din aceste maxime locale, vom calcula maximul global și vom returna cel mai frecvent
număr.
6. Scrie o funcție care să sorteze elementele pare ale unui șir de numere întregi. (Continental Iași)
Răspuns:
void sortEvens(vector<int> &data)
{
int evenCount = 0;
for(auto &i : data)
{
if(data[i]%2 == 0) evenCount++;
}
vector<int> evens;
evens.reserve(evenCount);
for(auto &i : data)
{
if(i%2 == 0) evens.push_back(i);
}
sort(evens.begin(), evens.end());
int index = 0;
for(unsigned i = 0; i < data.size(); i++)
{
if(data[i]%2 == 0) data[i] = evens[index++]; //:) hehe
}
}

7. Scrie o funcție care să scrie 0 pe linia și pe coloana unde este 0, într-o matrice de numere întregi.
(Continental Iași)
Răspuns: De la început, observăm că parcurgând matricea, dacă atunci când întâlnim valoarea 0,
scriem 0 pe linia și pe coloana aferentă, vom face ca următoarele valori să fie 0. Pentru a evita
acest lucru putem memora liniile și coloanele aferente valorilor 0 din matrice, urmând apoi să
scriem 0 parcurgând valorile liniilor și coloanelor memorate. Făcând acest lucru, în cel mai rău
caz, vom avea nevoie de o memorie egală cu memoria alocată pentru matrice. Putem reduce
memoria folosită, memorând doar liniile și coloanele pe care apare valoarea 0 (nu coordonatele
fiecărui 0). Mai mult de atât, putem folosi matricea pentru a nota liniile și coloanele unde este
întâlnită valoarea 0. Adică, dacă întâlnim valoarea 0 pe [2][2], vom scrie 0 pe [2][0] și [0][2],
marcând astfel pe marginea matricei linia care va conține 0. Făcând acest lucru, pentru a nu scrie
0 pe linia 0 și coloana 0, neconținând valoarea 0 ințial, vom seta două valori bool care să indice
dacă linia 0 și coloana 0 conțineau 0 înainte de prelucrare.
void nullifyRow(int **m, int cols, int row)
{
for(int i = 0; i < cols; i++)
m[row][i] = 0;
}
void nullifyColumn(int **m, int rows, int col)
{
for(int i = 0; i < rows; i++)
m[i][col] = 0;
}
void nullify(int **m, int rows, int cols)
{
if(m == nullptr || rows <= 0 || cols <= 0) return;

bool zeroOnFirstColumn = false;


for(int i = 0; i < rows; i++)
{
if(m[i][0] == 0)
{
zeroOnFirstColumn = true;
break;
}
}
bool zeroOnFirstRow = false;
for(int i = 0; i < cols; i++)
{
if(m[0][i] == 0)
{
zeroOnFirstRow = true;
break;
}
}
for(int i = 1; i < rows; i++)
{
for(int j = 1; j < cols; j++)
{
if(m[i][j] == 0)
m[i][0] = m[0][j] = 0;
}
}
for(int i = 1; i < rows; i++)
{
if(m[i][0] == 0) nullifyRow(m, cols, i);
}
for(int i = 1; i < cols; i++)
{
if(m[0][i] == 0) nullifyColumn(m, rows, i);
}
if(zeroOnFirstColumn) nullifyColumn(m, rows, 0);
if(zeroOnFirstRow) nullifyRow(m, cols, 0);
}
8. Scrie o funție care să returneze numărul de caractere dintr-un fișier text dat ca argument.
(Amazon Iași – wiki intern).
Răspuns:
string readFile(string filename)
{
ifstream in(filename);
if(!in.is_open()) return "";

string str = "", line;


while(getline(in, line))
{
str.append(line);
str.append("\n");
}
in.close();
if(in.is_open()) return "";
return str;
}
int wc(string filename)
{
string str = readFile(filename);
if(str == "") return -1;

regex reg("[\\s,\\t]+");
sregex_token_iterator it(str.begin(), str.end(), reg);
sregex_token_iterator itEnd;
int wordCount = 0;
for(; it != itEnd; it++)
{
wordCount++;
}
return wordCount;
}
9. Fie următoarele perechi caracter-frecvență a(15) b(7) c(6) d(6) e(5). Să se determine codurile lor
folosind algoritmul Huffman (Amazon Iași – wiki intern).
Răspuns:
class HuffNode
{
public:
HuffNode *left = nullptr;
HuffNode *right = nullptr;
string name;
int frequency;
HuffNode(string n, int f) : name(n), frequency(f){}
HuffNode(HuffNode *l, HuffNode *r)
{
name = l->name + r->name;
frequency = l->frequency + r->frequency;
this->left = l;
this->right = r;
}
};
HuffNode* buildHuffTree(MinHeap &minHeap)
{
while(minHeap.getSize() > 1)
{
HuffNode *left = minHeap.poll();
HuffNode *right = minHeap.poll();
HuffNode *root = new HuffNode(right, left);
minHeap.add(root);
}
return minHeap.poll();
}
string getEncodings(HuffNode *node, string prefix)
{
if(node->left == nullptr && node->right == nullptr)
{
return node->name + ": " + prefix + "\n";
}
string str;
if(node->left != nullptr)
{
str += getEncodings(node->left, prefix + "0");
}
if(node->right != nullptr)
{
str += getEncodings(node->right, prefix + "1");
}
return str;
}
string getEncodings(HuffNode *root)
{
return getEncodings(root, "");
}
10. Având un string, returnează cel mai lung substring care se repetă. (Amazon Iași – wiki intern)
Răspuns:
string getLingestDupSubstr(string text)
{
string suffix = "";
const int len = text.size();
Node root("");
for(int i = len-1; i >= 0; i--)
{
suffix = text.substr(i) + "$";
root.insert(suffix);
}
return root.getLongDupSubstr();
}
string getLongestDupSubstrNaive(string data)
{
string longest = "";
unordered_map<string, int> counts;
for(unsigned c = 1; c < data.size(); c++)
{
for(unsigned j = 0; j <= data.size()-c; j++)
{
string sub = data.substr(j, c);
auto it = counts.find(sub);
if(it == counts.end())
{
counts.insert(make_pair(sub, 1));
}
else
{
counts[sub]++;
if(counts[sub] > 1 && longest.size() < sub.size())
{
longest = sub;
}
}
}
}
return longest;
}
class Node
{
public:
int descendents = 0;
std::string suffix;
std::list<Node*> branches;
Node(std::string s) : suffix(s) {}
void insert(std::string newSuffix)
{
if(newSuffix == "") return;

descendents++;
if(branches.empty())
{
branches.push_back(new Node(newSuffix));
return;
}
for(auto &node : branches)
{
int pos = getFirstDiff(node->suffix, newSuffix);
if(pos > 0)
{
std::string left = node->suffix.substr(0, pos);
std::string right = newSuffix.substr(pos);
std::string remaining = node->suffix.substr(pos);
node->suffix = left;
node->insert(remaining);
node->insert(right);
return;
}
}
branches.push_back(new Node(newSuffix));
}
std::string getLongDupSubstr()
{
if(branches.size() == 0) return "";
Node *suffRoot = branches.front();
for(auto &node : branches)
{
if(suffRoot->descendents < node->descendents)
{
suffRoot = node;
}
}
return getLongDupSubstr(suffRoot);
}
private:
std::string getLongDupSubstr(Node *node)
{
std::string result = node->suffix;
for(auto &sub : node->branches)
{
if(sub->branches.size() > 1)
{
result += getLongDupSubstr(sub);
}
}
return result;
}
int getFirstDiff(std::string first, std::string second)
{
unsigned i = 0;
while(i < first.size() && i < second.size())
{
if(first[i] != second[i]) break;
i++;
}
return i;
}
};
11. Scrie o funcție care să returneze reprezentarea romană a unui întreg. (Amazon Iași)
Răspuns:
string getRoman(int number, unordered_map<unsigned, string> &map)
{
if(number == 0) return "";
if(map.find(number) != map.end()) return map[number];
int factor = round(pow(10, (int)log10(number)));
int dif = number-factor;
int sum = number+factor;
if(map.find(sum) != map.end())
map[number] = map[factor]+map[sum];
else
map[number] = getRoman(dif, map) + map[factor];

return map[number];
}
string getRoman(int number)
{
static auto map = buildRomanTable();
string result;
int factor = 1;
while(number != 0)
{
int n = (number%10) * factor;
if(n != 0)
{
string strRoman = getRoman(n, map);
result.insert(0,strRoman.c_str());
}
factor *= 10;
number /= 10;
}
return result;
}

unordered_map<unsigned,string> buildRomanTable()
{
unordered_map<unsigned,string> numberMap;
numberMap[1] = "I";
numberMap[5] = "V";
numberMap[10] = "X";
numberMap[50] = "L";
numberMap[100] = "C";
numberMap[500] = "D";
numberMap[1000] = "M";
return numberMap;
}
III Cunoștințe specifice programării orientată pe obiect

1. Ce este o clasă? (Cerner Brașov, Continental Iași)


Răspuns: O clasă este un tip de dată abstract care servește drept șablon pentru crearea obiectului
de același tip.

2. Ce este un obiect? (Cerner Brașov, Continental Iași)


Răspuns: Un obiect este rezultatul instanțierii unei clase.

3. Care sunt principiile OOP? (Cerner Brașov, Continental Iași)


Răspuns:
1. Abstractizarea – abilitatea ca un program să ignore unele aspecte ale informației pe care o
manipulează, concentrându-se pe esențial.
2. Încapsularea – sau ascunderea informațiilor este mecanismul prin care se asigură faptul că
obiectele nu pot schimba direct starea internă a altor obiecte decât prin intermediul metodelor
puse la dispoziție de obiectul respectiv.
3. Moștenirea – mecanismul care permite crearea unor noi tipuri de date abstracte pornind de la
unele definite anterior.
4. Polimorfismul – abilitatea de a trata obiectele diferit în funcție de tipul sau clasa lor.
Abilitatea de a redefini metode în clasele derivate.

4. Ce este un singleton?
Răspuns: Singleton este un șablon de proiectare care asigură faptul că nu vom avea mai mult de
o instanță a unui obiect. În C++ acest șablon poate fi implementat cu variabile statice.
class Restaurant
{
Restaurant(){};
Restaurant(const Restaurant &){};
void operator=(const Restaurant &){};
public:
Restaurant& getInstance()
{
static Restaurant instance;
return instance;
}
};
5. Ce este o clasă factory?
Răspuns: O clasă factory este o clasă care se ocupă de crearea obiectelor.
enum GameType{Poker, BlackJack};
class CardGame{};
class PokerGame : public CardGame{};
class BlackJackGame : public CardGame{};

class GameCreator
{
public:
static CardGame* createCardGame(GameType type)
{
if(type == Poker)
{
return new PokerGame();
}
else if(type == BlackJack)
{
return new BlackJackGame();
}
return NULL;
}
};
IV Cunoștințe generale de design și tehnologii

1. Care este diferența dintre Application Server și Web Server? (Cerner Brașov)
Răspuns: Web server se ocupă de conținutul HTTP (requesturi, pagini HTML statice și nu
numai, JSP, Pearl, etc). Application server-ul nu este limitat doar de conținuturi HTTP și poate
avea suport pentru protocoale precum RMI/RPC.
Application server-ul poate avea, integrat Web Server. În plus App Server poate conține
componente specifice Application service precum tranzacții, servicii de mesagerie (între
componente software).
În principu, Web server-ul conține elemente specifice Presentation Layer-ului (web content, JSP
pages, html, javascript, etc.). Application Server expune funcționalitățile Bussines Layerului, dar
nu este limitat la aceasta.

2. Descrie modul de structurare a unei aplicații pe layere și rolul fiecărui layer.


Răspuns: O aplicație se structurează pe mai multe straturi pentru a trata separat funcționalitățile
de care este responsabilă, obținând astfel o flexibilitate mai bună.
- Presentation layer (sau U.I. layer): implementază elemente de interfață grafică specifice
interacțiunii cu utilizatorul. Aceste acțiuni sunt transmise în continuare către Business layer.
- Business layer: implementează funcționalitățile principale ale sistemului.
- Data layer: conține datele necesare (baze de date, etc.) și răspunde requesturilor efectuate de
Bussines layer.

3. Care este diferența dintre metoda HTTP GET și HTTP POST?


Răspuns: Metoda GET concatenează informațiile în URL și deci este mai putin securizat decât
POST. Apelurile GET rămân în istoria browserului. POST nu are o limită de restricție pentru
dimensiunea datelor de trimis, pe când GET-ul are maxim 2040 caractere (dimensiunea maxima a
unui URL). HTTP GET poate transmite doar caractere ASCII, pe când POST nu are restricții din
acest punct de vedere.

4. Cum funcționează youtube-ul? (Cerner Brașov)


V Cunoștințe baze de date

1. Scrie un script care să insereze o înregistrare într-un tabel. (Cerner Brașov)


Răspuns: INSERT INTO table_name (column_1, column_2, …) VALUES (value1, value2, …)

2. Cum ai proceda pentru a insera foarte multe înregistrări într-un tabel? (Cerner Brașov)
Răspuns: Pentru a nu bloca accesul la tabelă, voi împărți volumul inițial de date în bucăți mai
mici și le voi insera pe rând.

3. Ce este o cheie straină? (Cerner Brașov)


Răspuns: O cheie străină este un câmp sau o grupare de câmpuri folosit pentru a face legătura
între tabele. O cheie străină poate lua valori doar dintre cele inserate inițial în tabela principlă. De
obicei, o cheie străină într-un tabel este o cheie primară în altul și tabelul în care este cheia străină
se numește tabel copil.

4. În ce constă procesul de normalizare a unei baze de date?


Răspuns: Procesul de normalizare a unei baze de date este un proces de organizare a coloanelor
și tabelelor astfel încât să se reducă redundanța datelor (sa nu avem aceleași valori în mai multe
tabele concomitent) și să asigure integritatea datelor.

5. Care este diferența dintre INNER JOIN, LEFT JOIN, RIGHT JOIN? (Cerner Brașov)
Răspuns: INNER JOIN face o selecție a datelor din tabele cât timp există o potrivire a coloanei
pe baza căruia se face operația.
LEFT JOIN face o selecție a datelor din tabelul stâng și a datelor care se potrivesc cu tabelul
drept. Dacă nu există o potrivire atunci rezultatul va fi NULL din tabelul drept.
RIGHT JOIN face o selecție a datelor din tabelul drept și a datelor care se potrivesc cu tabelul
stâng. Dacă nu există o potrivire atunci rezultatul din tabelul stâng va fi NULL.

S-ar putea să vă placă și