Latviešu

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:

Šī ī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

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ā:


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:


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:

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

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.