二叉搜索樹的簡明實現(ES5 & ES6)

二叉樹 & 二叉搜索樹

二叉樹(Binary Tree)是 n(n >= 0)個節點的有限集合,集合爲空集時,叫做空二叉樹;不爲空時,由根節點及左子樹、右子樹組成,左子樹、右子樹也都是二叉樹。javascript

從這個描述,能夠看出樹的結構與遞歸之間存在密切關係,這種密切關係在樹的遍歷時可以獲得充分體現。java

二叉搜索樹(Binary Search Tree),又叫二叉查找樹;也稱爲有序二叉樹(Ordered Binary Tree),排序二叉樹(Sorted Binary Tree)。node

這是維基百科上概括的一些二叉搜索樹的性質:算法

  1. 若任意節點的左子樹不空,則左子樹上全部節點的值均小於它的根節點的值;
  2. 若任意節點的右子樹不空,則右子樹上全部節點的值均大於它的根節點的值;
  3. 任意節點的左、右子樹也分別爲二叉查找樹;
  4. 沒有鍵值相等的節點。

本次實現中有點不同的地方,右節點是大於或等於父節點的。不過對示例沒有影響,並且很容易改爲只能大於父節點。數據結構

關於《學習JavaScript數據結構與算法(第2版)》

當年看過這本書的初版,最近打算複習一下數據結構與算法,因而看了第二版。post

這是可貴的一本用 JavaScript 實現的數據結構與算法的書,它的講解十分清晰,相對來講質量較高,但問題也有不少。應該說初版的內容仍是比較可靠的,第二版新增的內容可靠性就差了不少。總的來講,這是一本從很是淺顯的層面講解數據結構與算法的書,想得到比較全面的知識仍是須要閱讀更專業的資料。學習

本文的 ES5 實現參考了這本書,由於我以爲它是比較工整的實現,用於學習和理解時,好過我看到的其餘一些實現方式。在原書示例代碼的基礎上,我作了一些小調整。本書第二版號稱擁抱 ES6,但我看過以後發現至少樹這一章沒有改成 ES6 的實現,因而本身寫了一遍,正好看成練習的機會。ui

另外,這本書中提到的樹的遍歷方式包括中序、先序、後序遍歷,這些都屬於深度優先遍歷。在本文的代碼中,我補充了廣度優先遍歷以及按照層次遍歷二叉樹的實現。this

Binary Search Tree - ES5

var BinarySearchTree = function() {
  var Node = function(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  };

  var root = null;

  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);
      }
    }
  };

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

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

  var postOrderTraverseNode = function(node, cb) {
    if (node !== null) {
      postOrderTraverseNode(node.left, cb);
      postOrderTraverseNode
    }
  };

  var levelOrderTraverseNode = function(node, cb) {
    if (node === null) {
      return null;
    }

    var list = [node];

    while (list.length > 0) {
      node = list.shift();
      cb(node.key);
      if (node.left) {
        list.push(node.left);
      }
      if (node.right) {
        list.push(node.right);
      }
    }
  };

  var separateByLevelFn = function(node, cb, separator) {
    var list = [];
    var END_FLAG = 'END_FLAG';

    list.push(node);
    list.push(END_FLAG);

    separator = separator || '---*---';

    while (list.length > 0) {
      node = list.shift();

      // 遇到結束信號,表示已經遍歷完一層;若隊列中還有元素,說明它們是剛剛遍歷完的這一層的全部子元素。
      if (node === END_FLAG && list.length > 0) {
        list.push(END_FLAG);
        cb(separator);
      } else {
        cb(node.key);

        if (node.left) {
          list.push(node.left)
        }

        if (node.right) {
          list.push(node.right);
        }
      }
    }
  };

  var minNode = function(node) {
    if (node) {
      while (node.left !== null) {
        node = node.left;
      }

      return node.key;
    }

    return null;
  };

  var maxNode = function(node) {
    if (node) {
      while (node.right !== null) {
        node = node.right;
      }

      return node.key;
    }

    return null;
  };

  var searchNode = function(node, val) {
    if (node === null) {
      return false;
    }

    if (val < node.key) {
      return searchNode(node.left, val);
    } else if (val > node.key) {
      return searchNode(node.right, val);
    } else {
      return true;
    }
  };

  var findMinNode = function(node) {
    if (node) {
      while (node.left !== null) {
        node = node.left;
      }

      return node;
    }

    return null;
  };

  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;
      }

      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;
    }
  };

  this.insert = function(key) {
    var newNode = new Node(key);

    if (root === null) {
      root = newNode;
    } else {
      insertNode(root, newNode);
    }
  };

  // 中序遍歷是一種以上行順序訪問BST全部節點的遍歷方式,也就是以從最小到最大的順序訪
  // 問全部節點。中序遍歷的一種應用就是對樹進行排序操做。
  this.inOrderTraverse = function(cb) {
    inOrderTraverseNode(root, cb);
  };

  // 先序遍歷是以優先於後代節點的順序訪問每一個節點的。先序遍歷的一種應用是打印一個結構化的文檔。
  this.preOrderTraverse = function(cb) {
    preOrderTraverseNode(root, cb);
  };

  // 後序遍歷則是先訪問節點的後代節點,再訪問節點自己。後序遍歷的一種應用是計算一個目
  // 錄和它的子目錄中全部文件所佔空間的大小。
  this.postOrderTraverse = function(cb) {
    postOrderTraverseNode(root, cb);
  };

  // Breadth-First-Search
  // 能夠用來解決尋路徑的問題。
  this.levelOrderTraverse = function(cb) {
    levelOrderTraverseNode(root, cb);
  }

  // Breadth-First-Search
  // 區分層次
  this.separateByLevel = function(cb) {
    separateByLevelFn(root, cb);
  }

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

  this.max = function() {
    return maxNode(root);
  };

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

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


/* ========== test case ========== */


var tree = new BinarySearchTree();

/** * * 11 * / \ * / \ * / \ * / \ * / \ * / \ * 7 15 * / \ / \ * / \ / \ * 5 9 13 20 * / \ / \ / \ / \ * 3 6 8 10 12 14 18 25 * */
tree.insert(11);
tree.insert(7);
tree.insert(15);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);
tree.insert(6);

var printNode = function(val) {
  console.log(val);
};

tree.inOrderTraverse(printNode);

console.log('\n')
tree.levelOrderTraverse(printNode);

console.log('\n')
tree.separateByLevel(printNode);

console.log('\n')
tree.remove(7)
tree.inOrderTraverse(printNode);

console.log('\n')
tree.preOrderTraverse(printNode);

console.log('\n')
tree.postOrderTraverse(printNode);
複製代碼

Binary Search Tree - ES6

class Node {
  constructor(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
}

// insert(key):向樹中插入一個新的鍵。
// search(key):在樹中查找一個鍵,若是節點存在,則返回true;若是不存在,則返回false。
// inOrderTraverse:經過中序遍歷方式遍歷全部節點。
// preOrderTraverse:經過先序遍歷方式遍歷全部節點。
// postOrderTraverse:經過後序遍歷方式遍歷全部節點。
// min:返回樹中最小的值/鍵。
// max:返回樹中最大的值/鍵。
// remove(key):從樹中移除某個鍵。
class BinarySearchTree {

  constructor() {
    this.root = null;
  }

  static insertNode(node, newNode) {
    if (node.key > newNode.key) {
      if (node.left === null) {
        node.left = newNode;
      } else {
        BinarySearchTree.insertNode(node.left, newNode);
      }
    } else {
      if (node.right === null) {
        node.right = newNode;
      } else {
        BinarySearchTree.insertNode(node.right, newNode);
      }
    }
  }

  static searchNode(node, key) {
    if (node === null) {
      return false;
    }

    if (node.key === key) {
      return true;
    } else if (node.key > key) {
      BinarySearchTree.searchNode(node.left, key);
    } else if (node.key < key) {
      BinarySearchTree.searchNode(node.right, key);
    }
  }

  static inOrderTraverseNode(node, cb) {
    if (node === null) {
      return;
    }
    BinarySearchTree.inOrderTraverseNode(node.left, cb);
    cb(node.key);
    BinarySearchTree.inOrderTraverseNode(node.right, cb);
  }

  static preOrderTraverseNode(node, cb) {
    if (node === null) {
      return;
    }
    cb(node.key);
    BinarySearchTree.preOrderTraverseNode(node.left, cb);
    BinarySearchTree.preOrderTraverseNode(node.right, cb);
  }

  static postOrderTraverseNode(node, cb) {
    if (node === null) {
      return;
    }
    BinarySearchTree.postOrderTraverseNode(node.left, cb);
    BinarySearchTree.postOrderTraverseNode(node.right, cb);
    cb(node.key);
  }

  static levelOrderTraverseNode(node, cb) {
    if (node === null) {
      return null;
    }

    const list = [node];

    while (list.length > 0) {
      node = list.shift();
      cb(node.key);
      if (node.left) {
        list.push(node.left);
      }
      if (node.right) {
        list.push(node.right);
      }
    }
  }

  static separateByLevelFn(node, cb, separator = '---*---') {
    const list = [];
    const END_FLAG = 'END_FLAG';

    list.push(node);
    list.push(END_FLAG);

    while (list.length > 0) {
      node = list.shift();

      // 遇到結束信號,表示已經遍歷完一層;若隊列中還有元素,說明它們是剛剛遍歷完的這一層的全部子元素。
      if (node === END_FLAG && list.length > 0) {
        list.push(END_FLAG);
        cb(separator);
      } else {
        cb(node.key);

        if (node.left) {
          list.push(node.left)
        }

        if (node.right) {
          list.push(node.right);
        }
      }
    }
  }

  static removeNode(node, key) {
    if (node === null) {
      return null;
    }

    if (node.key === key) {

      if (node.left === null && node.right === null) {
        node = null;
        return node;
      } else if (node.left === null) {
        node = node.right;
        return node;
      } else if (node.right === null) {
        node = node.left;
        return node;
      } else if (node.left && node.right) {
        let rightMinNode = node.right;

        while (rightMinNode.left !== null) {
          rightMinNode = rightMinNode.left;
        }

        node.key = rightMinNode.key;
        node.right = BinarySearchTree.removeNode(node.right, rightMinNode.key);
        return node;
      }

    } else if (node.key > key) {
      node.left = BinarySearchTree.removeNode(node.left, key);
      return node;
    } else if (node.key < key) {
      node.right = BinarySearchTree.removeNode(node.right, key);
      return node;
    }
  }

  static printNode(val) {
    console.log(val);
  }

  insert(key) {
    const newNode = new Node(key);

    if (this.root === null) {
      this.root = newNode;
    } else {
      BinarySearchTree.insertNode(this.root, newNode);
    }
  }

  search(key) {
    return BinarySearchTree.searchNode(key);
  }

  // 中序遍歷是一種以上行順序訪問BST全部節點的遍歷方式,也就是以從最小到最大的順序訪
  // 問全部節點。中序遍歷的一種應用就是對樹進行排序操做。
  inOrderTraverse(cb = BinarySearchTree.printNode) {
    BinarySearchTree.inOrderTraverseNode(this.root, cb);
  }

  // 先序遍歷是以優先於後代節點的順序訪問每一個節點的。先序遍歷的一種應用是打印一個結構化的文檔。
  preOrderTraverse(cb = BinarySearchTree.printNode) {
    BinarySearchTree.preOrderTraverseNode(this.root, cb);
  }

  // 後序遍歷則是先訪問節點的後代節點,再訪問節點自己。後序遍歷的一種應用是計算一個目
  // 錄和它的子目錄中全部文件所佔空間的大小。
  postOrderTraverse(cb = BinarySearchTree.printNode) {
    BinarySearchTree.postOrderTraverseNode(this.root, cb);
  }

  // Breadth-First-Search
  // 能夠用來解決尋路徑的問題。
  levelOrderTraverse(cb = BinarySearchTree.printNode) {
    BinarySearchTree.levelOrderTraverseNode(this.root, cb);
  }

  // Breadth-First-Search
  // 區分層次
  separateByLevel(cb = BinarySearchTree.printNode) {
    BinarySearchTree.separateByLevelFn(this.root, cb);
  }

  min() {
    let node = this.root;

    if (node === null) {
      return null;
    }

    while (node.left !== null) {
      node = node.left;
    }

    return node.key;
  }

  max() {
    let node = this.root;

    if (node === null) {
      return null;
    }

    while (node.right !== null) {
      node = node.right;
    }

    return node.key();
  }

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


/* ========== test case ========== */


const tree = new BinarySearchTree();

/** * * 11 * / \ * / \ * / \ * / \ * / \ * / \ * 7 15 * / \ / \ * / \ / \ * 5 9 13 20 * / \ / \ / \ / \ * 3 6 8 10 12 14 18 25 * */
tree.insert(11);
tree.insert(7);
tree.insert(15);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);
tree.insert(6);

tree.inOrderTraverse();

console.log('\n')
tree.levelOrderTraverse();

console.log('\n')
tree.separateByLevel();

console.log('\n')
tree.remove(7)
tree.inOrderTraverse();

console.log('\n')
tree.preOrderTraverse();

console.log('\n')
tree.postOrderTraverse();
複製代碼
相關文章
相關標籤/搜索