Izpētiet bināro meklēšanas koku (BST) pamatus un uzziniet, kā tos efektīvi ieviest JavaScript. Šī rokasgrāmata aptver BST struktūru, operācijas un praktiskus piemērus izstrādātājiem visā pasaulē.
Binārās meklēšanas koki: Visaptveroša ieviešanas rokasgrāmata JavaScript valodā
Binārās meklēšanas koki (BST) ir fundamentāla datu struktūra datorzinātnē, ko plaši izmanto efektīvai datu meklēšanai, kārtošanai un izgūšanai. To hierarhiskā struktūra ļauj daudzām operācijām nodrošināt logaritmisku laika sarežģītību, padarot tos par spēcīgu rīku lielu datu kopu pārvaldīšanai. Šī rokasgrāmata sniedz visaptverošu pārskatu par BST un demonstrē to ieviešanu JavaScript valodā, kas paredzēta izstrādātājiem visā pasaulē.
Izpratne par binārās meklēšanas kokiem
Kas ir binārais meklēšanas koks?
Binārais meklēšanas koks ir uz koku balstīta datu struktūra, kurā katram mezglam ir ne vairāk kā divi bērni, ko sauc par kreiso bērnu un labo bērnu. Galvenā BST īpašība ir tāda, ka jebkuram dotajam mezglam:
- Visiem mezgliem kreisajā apakškokā ir atslēgas, kas ir mazākas par mezgla atslēgu.
- Visiem mezgliem labajā apakškokā ir atslēgas, kas ir lielākas par mezgla atslēgu.
Šī īpašība nodrošina, ka elementi BST vienmēr ir sakārtoti, kas ļauj veikt efektīvu meklēšanu un izgūšanu.
Galvenie jēdzieni
- Mezgls (Node): Pamatvienība kokā, kas satur atslēgu (datus) un norādes uz savu kreiso un labo bērnu.
- Sakne (Root): Augšējais mezgls kokā.
- Lapa (Leaf): Mezgls bez bērniem.
- Apakškoks (Subtree): Koka daļa, kuras sakne ir konkrēts mezgls.
- Augstums (Height): Garākā ceļa garums no saknes līdz lapai.
- Dziļums (Depth): Ceļa garums no saknes līdz konkrētam mezglam.
Binārā meklēšanas koka ieviešana JavaScript
Klases Node definēšana
Vispirms mēs definējam `Node` klasi, lai attēlotu katru mezglu BST. Katrs mezgls saturēs `key`, lai saglabātu datus, un `left` un `right` norādes uz tā bērniem.
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
Binārā meklēšanas koka klases definēšana
Tālāk mēs definējam `BinarySearchTree` klasi. Šī klase saturēs saknes mezglu un metodes koka ievietošanai, meklēšanai, dzēšanai un apstaigāšanai.
class BinarySearchTree {
constructor() {
this.root = null;
}
// Šeit tiks pievienotas metodes
}
Ievietošana
Metode `insert` pievieno jaunu mezglu ar norādīto atslēgu BST. Ievietošanas process uztur BST īpašību, novietojot jauno mezglu atbilstošā pozīcijā attiecībā pret esošajiem mezgliem.
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
Piemērs: Vērtību ievietošana BST
const bst = new BinarySearchTree();
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(3);
bst.insert(9);
bst.insert(8);
bst.insert(10);
bst.insert(13);
bst.insert(12);
bst.insert(14);
bst.insert(20);
bst.insert(18);
bst.insert(25);
Meklēšana
Metode `search` pārbauda, vai BST eksistē mezgls ar norādīto atslēgu. Tā šķērso koku, salīdzinot atslēgu ar pašreizējā mezgla atslēgu un attiecīgi pārvietojoties uz kreiso vai labo apakškoku.
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node === null) {
return false;
}
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
Piemērs: Vērtības meklēšana BST
console.log(bst.search(9)); // Output: true
console.log(bst.search(2)); // Output: false
Dzēšana
Metode `remove` dzēš mezglu ar norādīto atslēgu no BST. Šī ir vissarežģītākā operācija, jo, noņemot mezglu, ir jāsaglabā BST īpašība. Ir trīs gadījumi, kas jāņem vērā:
- 1. gadījums: Dzēšamais mezgls ir lapas mezgls. Vienkārši noņemiet to.
- 2. gadījums: Dzēšamajam mezglam ir viens bērns. Aizstājiet mezglu ar tā bērnu.
- 3. gadījums: Dzēšamajam mezglam ir divi bērni. Atrodiet secīgo pēcteci (mazāko mezglu labajā apakškokā), aizstājiet mezglu ar pēcteci un pēc tam izdzēsiet pēcteci.
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// atslēga ir vienāda ar mezgla.key
// 1. gadījums - lapas mezgls
if (node.left === null && node.right === null) {
node = null;
return node;
}
// 2. gadījums - mezglam ir tikai 1 bērns
if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
}
// 3. gadījums - mezglam ir 2 bērni
const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
Piemērs: Vērtības dzēšana no BST
bst.remove(7);
console.log(bst.search(7)); // Output: false
Koka apstaigāšana
Koka apstaigāšana ietver katra mezgla apmeklēšanu noteiktā secībā. Ir vairākas izplatītas apstaigāšanas metodes:
- Šķērsapstaigāšana (In-order): Apmeklē kreiso apakškoku, pēc tam mezglu, pēc tam labo apakškoku. Rezultātā mezgli tiek apmeklēti augošā secībā.
- Tiešā apstaigāšana (Pre-order): Apmeklē mezglu, pēc tam kreiso apakškoku, pēc tam labo apakškoku.
- Apgrieztā apstaigāšana (Post-order): Apmeklē kreiso apakškoku, pēc tam labo apakškoku, pēc tam mezglu.
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
Piemērs: BST apstaigāšana
const printNode = (value) => console.log(value);
bst.inOrderTraverse(printNode); // Output: 3 5 8 9 10 11 12 13 14 15 18 20 25
bst.preOrderTraverse(printNode); // Output: 11 5 3 9 8 10 15 13 12 14 20 18 25
bst.postOrderTraverse(printNode); // Output: 3 8 10 9 12 14 13 18 25 20 15 11
Minimālās un maksimālās vērtības
Minimālās un maksimālās vērtības atrašana BST ir vienkārša, pateicoties tā sakārtotajai dabai.
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current !== null && current.left !== null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current !== null && current.right !== null) {
current = current.right;
}
return current;
}
Piemērs: Minimālās un maksimālās vērtības atrašana
console.log(bst.min().key); // Output: 3
console.log(bst.max().key); // Output: 25
Bināro meklēšanas koku praktiskie pielietojumi
Binārie meklēšanas koki tiek izmantoti dažādās lietojumprogrammās, tostarp:
- Datu bāzes: Datu indeksēšana un meklēšana. Piemēram, daudzas datu bāzu sistēmas izmanto BST variācijas, piemēram, B-kokus, lai efektīvi atrastu ierakstus. Apsveriet datu bāzu globālo mērogu, ko izmanto daudznacionālas korporācijas; efektīva datu izgūšana ir vissvarīgākā.
- Kompilatori: Simbolu tabulas, kurās tiek glabāta informācija par mainīgajiem un funkcijām.
- Operētājsistēmas: Procesu plānošana un atmiņas pārvaldība.
- Meklētājprogrammas: Tīmekļa lapu indeksēšana un meklēšanas rezultātu ranžēšana.
- Failu sistēmas: Failu organizēšana un piekļuve tiem. Iedomājieties failu sistēmu serverī, ko globāli izmanto vietņu mitināšanai; labi organizēta, uz BST balstīta struktūra palīdz ātri pasniegt saturu.
Veiktspējas apsvērumi
BST veiktspēja ir atkarīga no tā struktūras. Labākajā gadījumā sabalansēts BST nodrošina logaritmisku laika sarežģītību ievietošanas, meklēšanas un dzēšanas operācijām. Tomēr sliktākajā gadījumā (piemēram, sagāzts koks), laika sarežģītība var pasliktināties līdz lineāram laikam.
Sabalansēti un nesabalansēti koki
Sabalansēts BST ir tāds, kurā katra mezgla kreisā un labā apakškoka augstums atšķiras ne vairāk kā par vienu. Pašbalansējoši algoritmi, piemēram, AVL koki un Sarkani-melnie koki, nodrošina, ka koks paliek sabalansēts, nodrošinot konsekventu veiktspēju. Dažādiem reģioniem var būt nepieciešami dažādi optimizācijas līmeņi atkarībā no servera slodzes; balansēšana palīdz uzturēt veiktspēju augstas globālas lietošanas apstākļos.
Laika sarežģītība
- Ievietošana: O(log n) vidēji, O(n) sliktākajā gadījumā.
- Meklēšana: O(log n) vidēji, O(n) sliktākajā gadījumā.
- Dzēšana: O(log n) vidēji, O(n) sliktākajā gadījumā.
- Apstaigāšana: O(n), kur n ir mezglu skaits kokā.
Papildu BST jēdzieni
Pašbalansējoši koki
Pašbalansējoši koki ir BST, kas automātiski pielāgo savu struktūru, lai saglabātu līdzsvaru. Tas nodrošina, ka koka augstums paliek logaritmisks, nodrošinot konsekventu veiktspēju visām operācijām. Izplatītākie pašbalansējošie koki ir AVL koki un Sarkani-melnie koki.
AVL koki
AVL koki uztur līdzsvaru, nodrošinot, ka augstuma atšķirība starp jebkura mezgla kreiso un labo apakškoku ir ne vairāk kā viens. Kad šis līdzsvars tiek izjaukts, tiek veiktas rotācijas, lai atjaunotu līdzsvaru.
Sarkani-melnie koki
Sarkani-melnie koki izmanto krāsu īpašības (sarkana vai melna), lai uzturētu līdzsvaru. Tie ir sarežģītāki nekā AVL koki, bet noteiktos scenārijos piedāvā labāku veiktspēju.
JavaScript koda piemērs: Pilnīga binārā meklēšanas koka ieviešana
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node === null) {
return false;
}
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// atslēga ir vienāda ar mezgla.key
// 1. gadījums - lapas mezgls
if (node.left === null && node.right === null) {
node = null;
return node;
}
// 2. gadījums - mezglam ir tikai 1 bērns
if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
}
// 3. gadījums - mezglam ir 2 bērni
const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current !== null && current.left !== null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current !== null && current.right !== null) {
current = current.right;
}
return current;
}
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
}
// Lietošanas piemērs
const bst = new BinarySearchTree();
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(3);
bst.insert(9);
bst.insert(8);
bst.insert(10);
bst.insert(13);
bst.insert(12);
bst.insert(14);
bst.insert(20);
bst.insert(18);
bst.insert(25);
const printNode = (value) => console.log(value);
console.log("Šķērsapstaigāšana (in-order):");
bst.inOrderTraverse(printNode);
console.log("Tiešā apstaigāšana (pre-order):");
bst.preOrderTraverse(printNode);
console.log("Apgrieztā apstaigāšana (post-order):");
bst.postOrderTraverse(printNode);
console.log("Minimālā vērtība:", bst.min().key);
console.log("Maksimālā vērtība:", bst.max().key);
console.log("Meklēt 9:", bst.search(9));
console.log("Meklēt 2:", bst.search(2));
bst.remove(7);
console.log("Meklēt 7 pēc dzēšanas:", bst.search(7));
Noslēgums
Binārie meklēšanas koki ir spēcīga un daudzpusīga datu struktūra ar daudziem pielietojumiem. Šī rokasgrāmata ir sniegusi visaptverošu pārskatu par BST, aptverot to struktūru, operācijas un ieviešanu JavaScript valodā. Izprotot šajā rokasgrāmatā apspriestos principus un metodes, izstrādātāji visā pasaulē var efektīvi izmantot BST, lai atrisinātu plašu problēmu loku programmatūras izstrādē. Sākot ar globālu datu bāzu pārvaldību un beidzot ar meklēšanas algoritmu optimizēšanu, zināšanas par BST ir nenovērtējams ieguvums jebkuram programmētājam.
Turpinot savu ceļojumu datorzinātnē, papildu jēdzienu, piemēram, pašbalansējošu koku un to dažādo implementāciju, izpēte vēl vairāk uzlabos jūsu izpratni un spējas. Turpiniet praktizēties un eksperimentēt ar dažādiem scenārijiem, lai apgūtu mākslu efektīvi izmantot bināros meklēšanas kokus.