Table of Contents

Tutorial 5.2 - Radix Sort

Obiective

Ne dorim:

Radix Sort

Radix Sort este un algoritm de sortare care ţine cont de cifre individuale ale elementelor sortate. Aceste elemente pot fi nu doar numere, ci orice altceva ce se poate reprezenta prin întregi. Majoritatea calculatoarelor digitale reprezintă datele în memorie sub formă de numere binare, astfel că procesarea cifrelor din această reprezentare se dovedeşte a fi cea mai convenabilă. Există două tipuri de astfel de sortare: LSD (least significant digit) şi MSD (most significant digit). LSD procesează reprezentările dinspre cea mai puţin semnificativă cifră spre cea mai semnificativă, iar MSD invers.

O versiune simplă a radix sort este cea care foloseşte 10 cozi (câte una pentru fiecare cifră de la 0 la 9). Aceste cozi vor reţine la fiecare pas numerele care au cifra corespunzătoare rangului curent. După această împărţire, elementele se scot din cozi în ordinea crescătoare a indicelui cozii (de la 0 la 9), şi se reţin într-un vector (care devine noua secvenţă de sortat). Exemplu:

Secvenţa iniţială:

170, 45, 75, 90, 2, 24, 802, 66

Numere sunt introduse în 10 cozi (într-un vector de 10 cozi), în funcţie de cifrele de la dreapta la stânga fiecărui număr.

Cozile pentru prima iteraţie vor fi:

  * 0: 170, 090 
  * 1: nimic 
  * 2: 002, 802 
  * 3: nimic 
  * 4: 024 
  * 5: 045, 075 
  * 6: 066 
  * 7 - 9: nimic 

a. Se face dequeue pe toate cozile, în ordinea crescătoare a indexului cozii, şi se pun numerele într-un vector, în ordinea astfel obţinută:

Noua secvenţă de sortat:

170, 090, 002, 802, 024, 045, 075, 066

b. A doua iteraţie:

Cozi:

  * 0: 002, 802 
  * 1: nimic 
  * 2: 024 
  * 3: nimic
  * 4: 045 
  * 5: nimic
  * 6: 066 
  * 7: 170, 075 
  * 8: nimic 
  * 9: 090 

Noua secvenţă:

002, 802, 024, 045, 066, 170, 075, 090

c. A treia iteraţie:

Cozi:

  * 0: 002, 024, 045, 066, 075, 090 
  * 1: 170 
  * 2 - 7: nimic
  * 8: 802 
  * 9: nimic

Noua secvenţă:

002, 024, 045, 066, 075, 090, 170, 802 (sortată) 

Implementare LSD

O posibila implementare pentru algoritmul Radix Sort, varianta LSD folosind 10 cozi poate fi urmatoare.

void radixSort(std::vector<int> &v) {
	// la pasul k Q[i] ca contine toate numerele care au cifra 
	// de pe pozita k egala cu i
	std::queue<int> Q[10];
 
	// obtine numarul de pasi (este dat de numarul cu cele mai multe cifre)
	int steps = countDigits ( getMaxValue(v) );
 
	// la fiecare pas ne va interesa un anumit rang (cifra) care
	// corespunde unei puteri a lui 10 
	int power = 1;
 
	for (int k = 1; k <= steps; ++k) {
		// fiecare element din v va fi distribuit intr-o coada cu un anumit indice i
		// corespunzator cifrei de pe pozitia k
		for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
			int i = (*it / power) % 10; // preproceseaza indexul i
			Q[i].push(*it); // adauga elementul in coada cu numarul i
		} 
 
		// pregateste pasul urmator
		power *= 10; // trec la urmatorul rang
 
		// renunt la vechea ordine din v
		// reconstruiesc v cu ordinea curenta din cozi
		int n; // numarul curent de elemente din v 
		n = 0; // simulez golirea vectorului
		for (int i = 0; i <= 9; ++i) {
			// cat timp coada curent are elemente, muta-le in v
			while (!Q[i].empty()) {
				v[n++] = Q[i].front(); // salveaza elemenul in v
				Q[i].pop(); // sterge elementul din Q[i]
			}
		}
	}
}

ATENȚIE! Este nevoie de includerea mai multor biblioteci.

#include <iostream> // std::cin
#include <queue> // std::queue
#include <cmath> // log10

în funcția de mai sus s-au folosit două funcții auxiliare:

* getMaxValue care returnează cel mai mare element dintr-un vector dat; rezultatul returnat de această funcție reprezintă numărul care are cele mai multe cifre, deci numărul de pași pe care îi va executa algoritmul nostru.

int getMaxValue(const std::vector<int> &v) {
	int result = v[0];
 
	for (int i = 1; i < v.size(); ++i) {
		result = std::max( result, v[i] );
	}
 
	return result;
}

* countDigits care returnează numărul de cifre al unui număr întreg.

int countDigits(unsigned int n) {
	return (int) (1 + log10 ((double) n));
}

Program de test

Vom folosi următorul cod sursă pentru a testa algoritmul implementat.

int main() {
	int n; // numarul de elemente din vector
	int x; // variabila temporara
	std::vector<int> v; // vectorul care va fi sortat
 
	// citire
	std::cout << "n = ";
	std::cin >> n;
	for (int i = 0; i < n; ++i) {
		// citeste inca un element
		std::cout << "v[" <<  i << "] = ";
		std::cin >>  x;
 
		// adauga-l in vector
		v.push_back(x);
	}
 
	// sorteaza 
	radixSort(v);
 
	// afiseaza vectorul sortat
	std::cout << "Dupa sortare:\n";
	for (int i = 0; i < n; ++i) {
		std::cout << v[i] << ' ';
	}
	std::cout << "\n";
 
	return 0;
}

Compilare

g++ main.cpp -o main

Exemple:

./main
 
n = 5
v[0] = 55
v[1] = 25
v[2] = 43
v[3] = 32
v[4] = 11
Dupa pasul 1
11 32 43 55 25 
Dupa pasul 2
11 25 32 43 55 
Dupa sortare:
11 25 32 43 55 
./main
 
n = 10
v[0] = 123456
v[1] = 134789
v[2] = 198555
v[3] = 223456
v[4] = 111111
v[5] = 222222
v[6] = 923456
v[7] = 111555
v[8] = 999111
v[9] = 123456
Dupa pasul 1
111111 999111 222222 198555 111555 123456 223456 923456 123456 134789 
Dupa pasul 2
111111 999111 222222 198555 111555 123456 223456 923456 123456 134789 
Dupa pasul 3
111111 999111 222222 123456 223456 923456 123456 198555 111555 134789 
Dupa pasul 4
111111 111555 222222 123456 223456 923456 123456 134789 198555 999111 
Dupa pasul 5
111111 111555 222222 123456 223456 923456 123456 134789 198555 999111 
Dupa pasul 6
111111 111555 123456 123456 134789 198555 222222 223456 923456 999111 
Dupa sortare:
111111 111555 123456 123456 134789 198555 222222 223456 923456 999111