我對JS樹的簡單學習

一種非順序數據結構-樹,它對於存儲須要快速查找的數據很是有用node

二叉樹結構圖

  • 相關概念:算法

    • 根節點:位於樹頂部的節點,沒有父節點數組

    • 內部節點:至少有一個子節點的節點(7,5,9,15,13,20)數據結構

    • 外部節點(葉節點):沒有子元素的節點(第3層)函數

    • 子樹:由節點和它的後代構成(節點13,12,14構成了一個子樹)post

    • 深度:節點的深度取決於它的祖先節點的數量this

    • 高度:取決於全部節點深度的最大值spa

二叉搜索樹(BST)

無序鏈表在插入時候具備較高的靈敏性,而有序數組在查找的時候具備較高的效率。指針

二叉搜索樹(BST)這一數據結構綜合了以上兩種數據結構的優勢。可是它只容許你在左側節點存儲(比父節點)小的值,右側節點存儲(比父節點)大的值。code

上圖就展現了一個二叉搜索樹。實現二叉樹的算法大部分都有遞歸。

  • 建立一個BinarySearchTree類

function BinarySearchTree () {
  var Node = function () {
    this.key = key; // 鍵
    this.left = null; // 指針指向左側子節點
    this.right = null; // 指針指向右側子節點
  };
  var root = null;
}

var tree = new BinarySearchTree();
  • 實現insert(key)方法,插入一個鍵

this.insert = function (key) {
  var newNode = new Node(kye); // 建立用來表示新節點的Node類實例,
  if (root === null) { // 若是插入的節點是樹第一個節點
    root = newNode;
  } else {
    insertNode(root, newNode); // 私有的輔助函數
  }
}

tree.insert();

私有的輔助函數

var insertNode = function (node, newNode) { // 從根節點開始
  if (newNode.key < node.key) { // 判斷左側,遍歷左側
    if (node.left === null) { // 若是子節點爲空,就在子節點添加新節點
      node.left = newNode;
    } else {
      insertNode(node.left, newNode); // 往下遞歸
    }
  } else { // 判斷右側,遍歷右側
    if (node.right === null) {
      node.right = newNode;
    } else {
      insertNode(node.right, newNode);
    }
  }
}

二叉樹的遍歷

前序遍歷:根節點->左子樹->右子樹

中序遍歷:左子樹->根節點->右子樹

後序遍歷:左子樹->右子樹->根節點

對下面的樹進行遍歷

遍歷的示範

  • 前序遍歷:abdefgc

  • 中序遍歷:debgfac

  • 後序遍歷:edgfbca

中序遍歷:一種應用是對樹進行排序操做

this.inOrderTraverse = function {callback} {
  inOrderTraverse(root, callback); // 回調函數用來處理遍歷到的每一個節點
};
var inOrderTraverseNode = function (node, callback) {
  if (node !== null) {
    inOrderTraverseNode(node.left, callback);
    callback(node.key);
    inOrderTraverseNode(node.right, callback);
  }
}

先序遍歷:一種應用是打印一個結構化的文檔

this.preOrderTraverse = function (callback) {
  preOrderTraverseNode(root, callback);
}

var preOrderTraverseNode = function (node, callback) {
  if (node !== null) {
    callback(node.key);
    preOrderTraverseNode(node.left, callback);
    preOrderTraverseNode(node.right, callback);
  }
}

後序遍歷:一種應用是計算一個目錄和它的子目錄中全部文件所佔的空間大小。

this.postOrderTraverse = function (callback) {
  postOrderTraverse(root, callback);
}

var postOrderTraverse = function (callback) {
  if (node !== null) {
    postOrderTraverse(node.left, callback);
    postOrderTraverse(node.right, callback);
    callback(node.key);
  }
}

搜索樹中的值

對於尋找最小值,老是沿着樹的左邊;對於尋找最大值,老是沿着樹的右邊

  • 尋找樹中的最小鍵

this.min = function () {
  return minNode(root);
}

var minNode = function (node) {
  if (node) {
    while (node && node.left !== null) {
      node = node.left;
    }
    return node.key;
  }
  return null;
}
  • 尋找一個特定的值

this.search = function (key) {
  return searchNode(root, key);
}

var searchNode = function (node, kye) {
  if (node === null) { // node有效性檢查
    return false;
  }
  if (key < node.key) { // 比當前節點小,當前節點的左側子樹搜索
    return searchNode(node.left, key);
  } else if (key > node.key) { // 比當前節點大,當前節點的右側子樹搜索
    return searchNode(node.right, key);
  } else { // 要找的鍵就是當前節點
    return true;
  }
}

移除一個節點

this.remove = function (key) {
  root = removeNode (root, key);
}

var removeNode = function (node, key) {
  if (node === null) { // 鍵不存在於樹中
    return null;
  }
  if (key < node.key) {
    node.left = removeNode(node.left, key);
    return node;
  } else if (key > node.key) {
    node.right = removeNode(node.right, key);
    return node;
  } else {
    if (node.left === null && node.right === null) { // 一個葉節點
      node = null;
      return node;
    }
    if (node.left === null) { // 一個只有一個子節點的節點 
      node = node.right;
      return node;
    } else if (node.right === null) {
      node = node.left;
      return node;
    }

    // 一個有兩個子節點的節點
    var aux = findMinNode(node.right);
    node.key = aux.key;
    node.right = removeNode(node.right, aux.key);
    return node;
  }
}
相關文章
相關標籤/搜索