Data Structure Classification [ Tree, Graph, Linked List, Array, Stack, Queue]

Hajagha Hasanli
10 min readJul 22, 2024

--

Bu blog yazısında, data strukturlarının sinifləndirilməsini araşdıracaq, fərqli növlərini müqayisə edəcək və onların istifadəsini JavaScript kod nümunələri ilə göstərəcəyik.

Data Strukturlarının Sinifləndirilməsi ( Classification of Data Structures )

Data strukturlar məlumatın effektiv idarə olunmasında və təşkil edilməsində əsas rol oynayır. Data strukturlar ümumiyyətlə iki əsas kateqoriyaya bölünür: primitiv data strukturlarqeyri-primitiv data strukturları.

Proqramlaşdırmada 2 növ data structure tipi var

  • Primitiv ( Primitive ) data structure
  • Qeyri-primitiv ( Non-primitive ) data structure

Primitiv data struktur yalnız bir növ məlumatı saxlayan fundamental bir data strukturdur, halbuki qeyri-əsas data strukturu, müxtəlif növlərdə olan məlumatı tək bir entity-də saxlayan istifadəçi tərəfindən müəyyənləşdirilmiş bir məlumat strukturudur. ( aşağıda daha ətraflı anlayacaqsınız 😉 )

1. Primitiv Data Strukturlar ( Primitive Data Structures )

Primitiv data strukturlar məlumatın ən sadə formalarını təşkil edir. Onlara daxildir:

  • Boolean: True və ya False dəyərləri alır. Boolean tipli dəyərlər məntiqi əməliyyatlarda geniş istifadə olunur və doğru və ya yanlış kimi iki mümkün nəticəni təmsil edir.
  • Number: Rəqəm tipli dəyərləri saxlayır. Sayılar hər hansı növ rəqəm ola bilər, məsələn tam ədəd, onluq kəsir və hətta mənfi ədədlər.
  • String: Mətn məlumatlarını saxlamaq üçün istifadə olunur və adətən cümlələr, adlar və ya digər simvollar ardıcıllıqları saxlamaq üçün istifadə olunur.
let isTrue = true; // Boolean
let count = 42; // Number
let message = "Salam, dünya!"; // String

2. Qeyri-Primitiv Data Strukturlar ( Non-Primitive Data Structures )

Qeyri-primitiv data strukturları mürəkkəb məlumatları təşkil edir və daha çox mürəkkəblik səviyyəsinə malikdir. Onlara linear və qeyri-linear data strukturları daxildir.

2.1. Linear Data Strukturlar ( Linear Data Structures )

Linear data strukturlar məlumatı ardıcıl şəkildə saxlayır. Linear data strukturlar Static Dynamic olaraq iki yerə bölünür.

Static-ə nümunə olaraq Array-i göstərə bilərik

Arraylar: Elementlərin kolleksiyasını saxlayır və bu elementlər ardıcıl rəqəmlərlə indekslənir. Arraylar tərtibatçılara məlumatları ardıcıl şəkildə saxlamağa və onlara sürətli şəkildə daxil olmağa imkan verir. Arraylar adətən eyni tipli məlumatları saxlamaq üçün istifadə olunur.

// indexs      0. 1. 2. 3. 4 
let numbers = [1, 2, 3, 4, 5]; // Array [ number tipli dəyərlər ]

Dynamic-ə nümunə olaraq Linked List, Stack, Queue-i göstərə bilərik.

Linked Lists: Hər bir element (nod) siyahıdakı növbəti elementə işarə edir. Linked listlər, məlumatların ardıcıl şəkildə saxlamaq üçün dinamik bir yoldur və əlavə etmə və silmə əməliyyatları üçün çox uyğun gəlir. İki növ linked list var: tək bağlı (singly linked) və ikili bağlı (doubly linked) listlər.

class ListNode {
constructor(value) {
this.value = value;
this.next = null;
}
}

let head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);

// Nəticə olaraq yaranmış Linked List aşağıda göstərildiyi kimi görünəcək.

{
value: 1,
next: {
value: 2,
next: {
value: 3,
next: null
}
}
}

Daha ətraflı məlumat üçün, head obyektinə əlaqəli nodları təsvir edək.

  1. head obyekti value sahəsinə və next sahəsinə malikdir.
  • value sahəsi 1 dəyərini saxlayır.
  • next sahəsi ikinci ListNode obyektinə işarə edir.

2. İkinci ListNode obyektinin strukturu:

  • value sahəsi 2 dəyərini saxlayır.
  • next sahəsi üçüncü ListNode obyektinə işarə edir.

3. Üçüncü ListNode obyektinin strukturu:

  • value sahəsi 3 dəyərini saxlayır.
  • next sahəsi null dəyərini saxlayır, çünki bu, siyahının son nodudur.
head
|
v
+------+ +------+ +------+
| 1 |---> | 2 |---> | 3 |
| next | | next | | next |
+------+ +------+ +------+

2.2. Qeyri-Linear Data Strukturları ( Non Linear Data Structures )

Qeyri-linear data strukturlarda məlumat hierarxik şəkildə təşkil olunur.

Ağaclar ( Trees ): Hierarxik strukturlar kök nodu ilə başlayır. Ağaclar mürəkkəb məlumatların strukturlandırılmasında istifadə olunur, məsələn, fayl sistemləri, təşkilati strukturlar və XML/HTML sənədləri kimi.

Tree Anatomy

Ağac strukturu, nodlardan (düyünlərdən) ibarət olan hierarxik bir məlumat strukturdur. Hər ağacın bir kök nodu olur və bu kök noddan başlayaraq digər nodlar ilə əlaqələndirilir. Hər bir nod 0 və ya daha çox uşaq (child ) noduna malik ola bilər.

Ağac Strukturlarının İstifadəsi

Ağac strukturları mürəkkəb məlumatları təşkil etmək ( handle ) üçün çox faydalıdır:

  • Fayl Sistemləri: Kataloqlar və fayllar arasında əlaqələri təsvir etmək üçün.
  • Təşkilati Strukturlar: Şirkətlərin və təşkilatların hierarxik strukturlarını təsvir etmək üçün.
  • XML/HTML Sənədləri: Elementlər arasında nizamlı əlaqələri təsvir etmək üçün.

Ağac strukturları məlumatları hierarxik şəkildə təşkil edərək onların daha asan və səmərəli idarə olunmasını təmin edir.

Ağacın Tərkib Hissələri

  • Kök (Root) Nod: Ağacın başlanğıc nöqtəsi və ən üst nodu.
  • Uşaq (Child) Nodlar: Kök nodun və ya digər nodların altında yerləşən nodlar.
  • Yarpaqlar (Leaves): Uşaqları olmayan nodlar.
  • Dallanmalar (Branches): Kök noddan yarpaqlara qədər olan yollar.
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}

addChild(child) {
this.children.push(child);
}
}

// Kök nod yaradılması
let root = new TreeNode(1);

// Uşaq nodlarının yaradılması
let child1 = new TreeNode(2);
let child2 = new TreeNode(3);

// Uşaq nodlarının kök noduna əlavə edilməsi
root.addChild(child1);
root.addChild(child2);
  1. Kök Nodun Yaradılması:
let root = new TreeNode(1);

Burada TreeNode sinfi ilə bir kök nod yaradılır. Bu nodun dəyəri 1-dir və ilkin olaraq heç bir uşağı yoxdur (children sahəsi boş massivdir).

2. Uşaq Nodlarının Yaradılması:

let child1 = new TreeNode(2);
let child2 = new TreeNode(3);

Burada iki uşaq nod yaradılır. child1 nodu dəyəri 2 olan bir noddur və child2 nodu dəyəri 3 olan bir noddur.

3. Uşaq Nodlarının Kök Noduna Əlavə Edilməsi:

root.addChild(child1);
root.addChild(child2);

Bu addımlarda, child1child2 nodları kök nodunun uşaqları kimi əlavə edilir. addChild metodu children massivinə yəni nodlar əlavə edir.

Nəticə:

Yuxarıdakı əməliyyatları yerinə yetirdikdən sonra, root obyektinin strukturu belə görünəcək:

{
value: 1,
children: [
{
value: 2,
children: []
},
{
value: 3,
children: []
}
]
}

Bu, ağacın vizual təsviri ilə daha aydın göstərilə bilər:

    1
/ \
2 3

Yığın (Stack)

Növbəti sətirlərdə də stack-i yığın olaraq adlandıracam, lakin stack olaraq daha çox eşidəcəksiniz deyə qətiyyən unutmayın!

Yığın (stack) məlumat strukturu, məlumatları müəyyən bir qaydada saxlayan və idarə edən dinamik bir strukturdur. Yığın “son daxil olan ilk çıxar” (Last In, First Out — LIFO) prinsipi ilə işləyir, yəni ən son əlavə olunan element ilk olaraq çıxarılır.

stack

Stack Məlumat Strukturunun Tərkib Hissələri

  • Push: Stack-ə element əlavə etmək əməliyyatı.
  • Pop: Stack-dən element çıxarmaq əməliyyatı.
  • Peek: Stack-in üstündəki elementi göstərmək əməliyyatı.
  • IsEmpty: Stack-in boş olub olmadığını yoxlamaq əməliyyatı.

Yığınların İstifadə Sahələri

  • Funksiya Çağırışları: Proqram icrası zamanı çağırış stack-in izlənməsi.
  • Geri Alma (Undo) Əməliyyatları: Mətni redaktə edən tətbiqlərdə son əməliyyatları geri almaq üçün.
  • Browser Tarixi: Veb browser-in geri və irəli düymələrinin işləməsi üçün.
class Stack {
constructor() {
this.items = [];
}
// Yığına element əlavə etmək
push(element) {
this.items.push(element);
}
// Yığından element çıxarmaq
pop() {
if (this.isEmpty()) {
return "Yığın boşdur";
}
return this.items.pop();
}
// Yığının üstündəki elementi göstərmək
peek() {
if (this.isEmpty()) {
return "Yığın boşdur";
}
return this.items[this.items.length - 1];
}
// Yığının boş olub olmadığını yoxlamaq
isEmpty() {
return this.items.length === 0;
}
// Yığının ölçüsünü göstərmək
size() {
return this.items.length;
}
// Yığını təmizləmək
clear() {
this.items = [];
}
}
// Yəni yığın yaradılması
let stack = new Stack();
// Yığına elementlərin əlavə edilməsi
stack.push(10);
stack.push(20);
stack.push(30);
// Yığının vəziyyəti
console.log("Yığının vəziyyəti:", stack.items); // [10, 20, 30]
// Yığının üstündəki elementi göstərmək
console.log("Yığının üstündəki element:", stack.peek()); // 30
// Yığından element çıxarmaq
console.log("Çıxarılan element:", stack.pop()); // 30
console.log("Yığının vəziyyəti:", stack.items); // [10, 20]
// Yığının ölçüsünü göstərmək
console.log("Yığının ölçüsü:", stack.size()); // 2
// Yığının boş olub olmadığını yoxlamaq
console.log("Yığın boşdurmu?", stack.isEmpty()); // false
// Yığını təmizləmək
stack.clear();
console.log("Yığın təmizləndikdən sonra:", stack.items); // []
  1. Yığın Yaradılması:
let stack = new Stack();
  • Yəni bir yığın yaradılır.

2. Yığına Elementlərin Əlavə Edilməsi:

stack.push(10);
stack.push(20);
stack.push(30);

Yığına 10, 20 və 30 dəyərləri əlavə edilir. Yığının vəziyyəti belə görünəcək:

[10, 20, 30]

3. Yığının Üstündəki Elementin Göstərilməsi:

console.log("Yığının üstündəki element:", stack.peek()); // 30

peek metodu yığının üstündəki elementi göstərir, bu halda 30 dəyəridir.

4. Yığından Element Çıxarmaq:

console.log("Çıxarılan element:", stack.pop()); // 30

pop metodu yığının üstündəki elementi çıxarır və qaytarır. Çıxarıldıqdan sonra yığının vəziyyəti belə olacaq:

[10, 20]

5. Yığının Ölçüsünü Göstərmək:

console.log("Yığının ölçüsü:", stack.size()); // 2
  • size metodu yığının ölçüsünü göstərir, bu halda 2.

6. Yığının Boş Olub Olmadığını Yoxlamaq:

console.log("Yığın boşdurmu?", stack.isEmpty()); // false
  • isEmpty metodu yığının boş olub olmadığını yoxlayır. Bu halda yığın boş deyil.

7. Yığını Təmizləmək:

stack.clear();
console.log("Yığın təmizləndikdən sonra:", stack.items); // []

clear metodu yığını tamamilə təmizləyir və yığın boşalır.

Queues (Növbələr)

Növbələr FIFO (First In First Out) prinsipinə əsaslanır. Bu o deməkdir ki, yəni element növbəyə əlavə olunduqda, ilk daxil olan element birinci çıxarılır. Növbələr bir sıra əməliyyatlar və tətbiq sahələri üçün istifadə olunur:

  • Sistem Proseslərinin İdarə Edilməsi: Proseslərə qaydalı şəkildə xidmət göstərmək üçün.
  • Mesajların Ötürülməsi: Məsələn, printerlərin işini idarə etmək və ya verilənlərin şəbəkə üzərindən ötürülməsi.
class Queue {
constructor() {
this.items = [];
}

// Növbəyə element əlavə etmək
enqueue(element) {
this.items.push(element);
}
// Növbədən element çıxarmaq
dequeue() {
if (this.isEmpty()) {
return "Növbə boşdur";
}
return this.items.shift();
}
// Növbənin önündəki elementi göstərmək
front() {
if (this.isEmpty()) {
return "Növbə boşdur";
}
return this.items[0];
}
// Növbənin boş olub olmadığını yoxlamaq
isEmpty() {
return this.items.length === 0;
}
// Növbənin ölçüsünü göstərmək
size() {
return this.items.length;
}
// Növbəni təmizləmək
clear() {
this.items = [];
}
}
// Yəni növbənin yaradılması
let queue = new Queue();
// Növbəyə elementlərin əlavə edilməsi
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
// Növbənin vəziyyəti
console.log("Növbənin vəziyyəti:", queue.items); // [10, 20, 30]
// Növbənin önündəki elementi göstərmək
console.log("Növbənin önündəki element:", queue.front()); // 10
// Növbədən element çıxarmaq
console.log("Çıxarılan element:", queue.dequeue()); // 10
console.log("Növbənin vəziyyəti:", queue.items); // [20, 30]
// Növbənin ölçüsünü göstərmək
console.log("Növbənin ölçüsü:", queue.size()); // 2
// Növbənin boş olub olmadığını yoxlamaq
console.log("Növbə boşdurmu?", queue.isEmpty()); // false
// Növbəni təmizləmək
queue.clear();
console.log("Növbə təmizləndikdən sonra:", queue.items); // []
  1. Növbə Yaradılması:
let queue = new Queue();

2. Növbəyə Elementlərin Əlavə Edilməsi:

queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);

Queue-ə 10, 20 və 30 dəyərləri əlavə edilir. Queue-nin vəziyyəti belə görünəcək:

[10, 20, 30]

3. Növbənin Önündəki Elementin Göstərilməsi:

console.log("Növbənin önündəki element:", queue.front()); // 10

front metodu növbənin önündəki elementi göstərir, bu halda 10 dəyəri.

4. Növbədən Elementi Çıxarmaq:

console.log("Çıxarılan element:", queue.dequeue()); // 10

dequeue metodu növbənin önündəki elementi çıxarır və qaytarır. Çıxarıldıqdan sonra növbənin vəziyyəti belə olacaq:

[20, 30]

5. Növbənin Ölçüsünü Göstərmək:

console.log("Növbənin ölçüsü:", queue.size()); // 2

size metodu növbənin ölçüsünü göstərir, bu halda 2.

6. Növbənin Boş Olub Olmadığını Yoxlamaq:

console.log("Növbə boşdurmu?", queue.isEmpty()); // false

isEmpty metodu növbənin boş olub olmadığını yoxlayır. Bu halda növbə boş deyil.

7. Növbəni Təmizləmək:

queue.clear();
console.log("Növbə təmizləndikdən sonra:", queue.items); // []

clear metodu növbəni tamamilə təmizləyir və növbə boşalır.

Qraflar ( Graphs ): Nodlar (vertexes) və kənarların (edges) birləşməsindən ibarət olan şəbəkədir. Qraflar mürəkkəb və qarşılıqlı əlaqələri təsvir etmək üçün istifadə olunur.

Qrafın Tərkib Hissələri

  • Nodlar (Vertexes): Qrafın əsas elementləri. Hər bir nod bir məlumat vahidini təmsil edir.
  • Kənarlar (Edges): Nodlar arasında əlaqələri təmsil edir. Kənarlar yönlü (directed) və ya yönsüz (undirected) ola bilər.

Qrafların İstifadə Sahələri

  • Sosial Şəbəkələr: İnsanlar və onların əlaqələri arasında əlaqələri təsvir edir.
  • Şəhərlərarası Yollar: Şəhərlər arasındakı yolları və marşrutları təsvir edir.
  • Kompüter Şəbəkələri: Kompüterlər və onların bir-biri ilə əlaqələrini təsvir edir.

Aşağıdakı nümunə JavaScript-də qraf strukturlarının yaradılmasını göstərir ( ən sadə nümunəsidir ):

class Graph {
constructor() {
this.adjacencyList = {};
}

addVertex(vertex) {
if (!this.adjacencyList[vertex]) {
this.adjacencyList[vertex] = [];
}
}

addEdge(vertex1, vertex2) {
if (this.adjacencyList[vertex1] && this.adjacencyList[vertex2]) {
this.adjacencyList[vertex1].push(vertex2);
this.adjacencyList[vertex2].push(vertex1);
}
}
}

// Yəni qraf yaradılması
let graph = new Graph();

// Nodların əlavə edilməsi
graph.addVertex('A');
graph.addVertex('B');

// Nodlar arasındakı kənarların əlavə edilməsi
graph.addEdge('A', 'B');

console.log(graph.adjacencyList);
  1. Qrafın Yaradılması:
let graph = new Graph();

Graph class-ı ilə yəni bir qraf yaradılır.

2. Nodların Əlavə Edilməsi:

graph.addVertex('A');
graph.addVertex('B');

addVertex metodu nodları qrafa əlavə edir. Əgər nod daha əvvəl əlavə olunmayıbsa, yəni bir boş massiv yaradılır.

3. Kənarların Əlavə Edilməsi:

graph.addEdge('A', 'B');

addEdge metodu iki nod arasında kənar əlavə edir. Bu misalda, AB nodları arasında qarşılıqlı əlaqə yaradılır. Əgər hər iki nod mövcuddursa, onlar bir-birinin əlaqə siyahısına əlavə edilir

Nəticə:

Əməliyyatları yerinə yetirdikdən sonra, graph.adjacencyList obyektinin strukturu belə görünəcək:

{
'A': ['B'],
'B': ['A']
}

Bu, qrafın vizual təsviri ilə daha aydın göstərilə bilər: ( vizual təsviri mənə aydın olmadı 🤨😁 )

  A -- B

Qrafın Mürəkkəb İstifadə Halları

  • Marşrutların Tapılması: İki nod arasındakı ən qısa marşrutu tapmaq üçün qraf algoritmlərindən istifadə edilə bilər.
  • Kompüter Şəbəkələrində Yükləmə İdarəetməsi: Qraf strukturları, şəbəkə yüklərini tarazlaşdırmaq və optimal marşrutları təyin etmək üçün istifadə edilə bilər.
  • Sosial Şəbəkə Analizi: Qraf strukturları, istifadəçilər arasındakı əlaqələri və qarşılıqlı əlaqələri təhlil etmək üçün istifadə edilə bilər.

Data Strukturlarının Müqayisəsi

hmmmm

Performans

Fərqli data strukturları müxtəlif performans xüsusiyyətləri təklif edir. Performans məlumatların daxil edilməsi, silinməsi və əldə edilməsi üçün vaxtın ölçülməsi ( time complexity ) ilə qiymətləndirilir.

  • Arraylar vs Linked Lists: Arraylar O(1) (random access), yəni müəyyən bir indeksdəki elementə birbaşa daxil olmaq çox sürətlidir. Lakin element əlavə etmək və ya silmək O(n) vaxt ala bilər. Linked listlər isə başda və ya sonunda element əlavə etmə və silmə üçün O(1) vaxt təklif edir, lakin rastgəlmə üçün O(n) vaxt ala bilər.

Yaddaş İstifadəsi

Data strukturları yaddaşı fərqli şəkildə istifadə edir və bu, resursların istifadəsinə təsir edir.

  • Arraylar vs Ağaclar ( Trees ): Arraylar ardıcıl yaddaşdan istifadə edə bilər, bu da müəyyən elementə daxil olmaq üçün yaddaşın birbaşa adreslənməsini təmin edir. Bu, performansı artırır, lakin arraylar statik ölçülüdür və genişləndirmək çətin ola bilər. Ağaclar isə dinamik ölçülüdür və genişlənə bilərlər, lakin onların idarə edilməsi daha mürəkkəbdir.

Diqqətiniz üçün təşəkkürlər

Writers: Arif Hasanov, Hajagha Hasanli

--

--