目次
1.二分木の構造カプセル化を導入します
function BinarySearchTree(){
// 新结点创建的构造函数
function Node(key){
this.key = key;
this.lchild = null;
this.rchild = null;
}
// 保存根的属性
this.root = null;
// 创建关于二叉搜索树的相关操作方法
}
2、挿入挿入(キー)
アイデア:
挿入メソッドを作成する
1.新しいノードを作成します
2.空のツリーかどうかを判断します
。2.1空のツリーの場合は、ルートの属性が新しいノードを指すようにします
。2.2空でないツリーでない場合は、挿入する適切な場所を見つけます。
次に、内部使用のメソッドを作成します。insertNode()
。1、新しい
ノードの値が小さな
挿入空の右サブツリーノードの比較のノード値よりも大きい場合は、新しいノードとノードサイズの値1.1を比較します。続行する必要はありません。右側の比較を空にする
1.2ノードの値よりも新しいノードの値が大きい場合、ノードの
左側のサブツリーを比較して、左側の比較を空にし続けない
次のツリーで、20ノードを挿入する場合は、次の手順を実行します。
コード:
// 插入元素,给用户使用
BinarySearchTree.prototype.insert = function(key){
// 1、创建新结点
var newNode = new Node(key);
// 2、判断是否为空树
// 2.1如果是空树,则让根的属性指向新结点
if (this.root == null){
this.root = newNode;
}else{// 2.2如果不是非空树,则寻找合适的位置插入
this.insertNode(this.root, newNode);
}
}
// 用于内部使用
BinarySearchTree.prototype.insertNode = function(node, newNode){
// 1、比较结点与新结点的值大小
// 1.1如果结点的值比新结点的值小
if (node.key < newNode.key) {
// 比较结点的右子树为空时插入,不为空时继续往下比较
if (node.rchild == null) {
node.rchild = newNode;
}else{
this.insetNode(node.rchild, newNode);
}
}else{ // 1.2 如果结点的值比新结点的值大
// 比较结点的左子树为空时插入,不为空时继续往下比较
if (node.lchild == null) {
node.lchild = newNode;
}else{
this.insertNode(node.lchild, newNode);
}
}
}
コードテスト
// 测试
// 1、创建BinarySearchTree
var bst = new BinarySearchTree();
// 2、插入数据
bst.insert(18);
bst.insert(4);
bst.insert(2);
bst.insert(7);
bst.insert(30);
bst.insert(24);
bst.insert(20);
// 3、输出结构
console.log(bst);
-》
第三に、二分木のトラバーサル法
二分探索木の探索は、二分木の探索です。各ノードを探索する場合は、特定の順序で探索する必要があります。
二分木には3つの一般的な方法があります。
- プレオーダートラバーサル
- 順序どおりの走査
- 注文後のトラバーサル
もう1つの珍しいトラバーサルは、ツリーの各レベルに応じて、上から下に左から右にトラバースする階層トラバーサルです。
1.一次走査
操作の定義:
二分木が空の場合、操作はありません。それ以外の場合。
(1)ルートノードにアクセスします。
(2)最初に左側のサブツリーをトラバースします。
(3)最初に右側のサブツリーをトラバースします。
コード:
// 先序遍历
BinarySearchTree.prototype.preOrderTraversal = function(fun){
// 让preOrderTraversalNode去实现递归操作
this.preOrderTraversalNode(this.root, fun)
}
// 内部使用
BinarySearchTree.prototype.preOrderTraversalNode = function(node, fun){
if (node) {
// 1、打印当前结点
fun(node.key);
// 2、遍历所有的左子树;
this.preOrderTraversalNode(node.lchild, fun);
// 3、遍历所有的右子树。
this.preOrderTraversalNode(node.rchild, fun);
}
}
テストコード
// 测试
// 1、创建BinarySearchTree
var bst = new BinarySearchTree();
// 2、插入数据
bst.insert(18);
bst.insert(4);
bst.insert(2);
bst.insert(7);
bst.insert(30);
bst.insert(24);
bst.insert(20);
// 3、先序遍历
var result = '';
bst.preOrderTraversal(function (key){
result += key + ' ';
})
console.log(result); //18 4 2 7 30 24 20
2.順序どおりのトラバーサル
操作の定義:
二分木が空の場合、操作はありません。それ以外の場合。
(1)左側のサブツリーをトラバースするため。
(2)ルートノードにアクセスします。
(3)右側のサブツリーをトラバースするため。
コード:
// 中序遍历
BinarySearchTree.prototype.midOrderTraversal = function(fun){
// 让midOrderTraversalNode去实现递归操作
this.midOrderTraversal(this.root, fun)
}
// 内部使用
BinarySearchTree.prototype.midOrderTraversalNode = function(node, fun){
if (node) {
// 1、遍历所有的左子树;
this.midOrderTraversalNode(node.lchild, fun);
// 2、打印当前结点
fun(node.key);
// 3、遍历所有的右子树。
this.midOrderTraversalNode(node.rchild, fun);
}
}
テストコード
// 测试
// 1、创建BinarySearchTree
var bst = new BinarySearchTree();
// 2、插入数据
bst.insert(18);
bst.insert(4);
bst.insert(2);
bst.insert(7);
bst.insert(30);
bst.insert(24);
bst.insert(20);
// 3、中序遍历
var result = '';
bst.midOrderTraversal(function (key){
result += key + ' ';
})
console.log(result); //2 4 7 18 20 24 30
3.注文後のトラバーサル
操作の定義:
二分木が空の場合、操作はありません。それ以外の場合。
(1)左のサブツリーをポストオーダーでトラバースします。
(2)ポストオーダーで右側のサブツリーをトラバースします。
(3)ルートノードにアクセスします。
コード:
// 后序遍历
BinarySearchTree.prototype.postOrderTraversal = function(fun){
// 让postOrderTraversalNode去实现递归操作
this.postOrderTraversalNode(this.root, fun)
}
// 内部使用
BinarySearchTree.prototype.postOrderTraversalNode = function(node, fun){
if (node) {
// 1、遍历所有的左子树;
this.postOrderTraversalNode(node.lchild, fun);
// 2、遍历所有的右子树。
this.postOrderTraversalNode(node.rchild, fun);
// 3、打印当前结点
fun(node.key);
}
}
テストコード
// 测试
// 1、创建BinarySearchTree
var bst = new BinarySearchTree();
// 2、插入数据
bst.insert(18);
bst.insert(4);
bst.insert(2);
bst.insert(7);
bst.insert(30);
bst.insert(24);
bst.insert(20);
// 3、先序遍历
var result1 = '';
bst.preOrderTraversal(function (key){
result1 += key + ' ';
})
console.log(result1); //18 4 2 7 30 24 20
// 4、中序遍历
var result2 = '';
bst.midOrderTraversal(function (key){
result2 += key + ' ';
})
console.log(result2); //2 4 7 18 20 24 30
// 5、后序遍历
var result3 = '';
bst.postOrderTraversal(function (key){
result3 += key + ' ';
})
console.log(result3); //2 7 4 20 24 30 18
第四に、二分探索木は最良の値を見つけます
ツリーが空の場合、最大値はありません。
ツリーが空でない場合
- ルートノードから開始する必要があり、最後のものが最も大きな値であることを正しく見つけます。
- 最後が最も小さい値を見つけるために、左のルートから開始する必要があります。
コード:
// 最大值
BinarySearchTree.prototype.max = function(){
var node = this.root;
var key = null;
while(node){
key = node.key;
node = node.rchild;
}
return key;
}
// 最小值
BinarySearchTree.prototype.min = function(){
var node = this.root;
var key = null;
while(node){
key = node.key;
node = node.lchild;
}
return key;
}
キーを見つけるための5つの二分探索木
キーの検索は、アイテムを挿入するときの検索と非常によく似ています。ここでは、ループを使用してコードを記述します。
// 查找某个key
BinarySearchTree.prototype.searchKey = function(key){
var node = this.root;
while(node != null){
if (node.key < key) {
node = node.rchild;
}else if (node.key > key) {
node = node.lchild;
}else{
return true;
}
}
return false;
}
6、二分探索木削除ノード
二分探索木のノード削除操作は、状況が多いので、少しずつ分析していくので、非常に複雑だということを先ほど申し上げましたが、操作はわかりにくくなく、最も重要なことです。それをよく考えることです。二分木の構造については、削除されたノードによって接続されたサブノードの数を3つのケースに分けます。
ケース1:削除されたノードには子ノードがありません(つまり、リーフノードです)。
ケース2:削除されたノードに子ノードがあります。
状況3:削除されたノードには2つの子ノードがあります。
特定の手順:
1.削除するノードを見つけます。
- 1.1変数を定義し、情報を保存します。
currentはルートノードを保存します。
親レコードは、ノードの親ノードを削除します。
isLeftChildは、削除されたノードが親ノードの左の子ノードであるかどうかを判別します。
var current = this.root; // 保存根结点 var parent = null; // 记录删除结点的父节点 var isLeftChild = true; //判断删除的结点是否是该父结点的左子结点
- 1.2削除するノードを見つけ、最後のノードが見つかった場合は終了します。
// 1.2 查找要删除的结点 while(current.key != key){ parent = current; if (key < current.key) { isLeftChild = true; current = current.lchild; }else{ isLeftChild = false; current = current.rchild; } // 已经找到最后一个结点还没有找到就退出 if (current == null) return false; }
2.ノードを見つけて、対応する状況に応じてノードを削除します。
- 2.1ケース1:削除されたノードはリーフノードです(つまり、子ノードはありません)。
①ツリーにルートノードが1つしかない場合は、それを削除するだけです。
②ツリーに複数のノードがあり、リーフノードを削除した場合、削除するノードの親ノードに応じてポインタフィールドを空にすることができます。
// 2.1删除的结点是叶子结点(即没有子结点) if (current.lchild == null && current.rchild == null) { if (current == this.root) { this.root = null; } else if (isLeftChild) { parent.lchild = null; } else { parent.rchild = null; } }
- 2.2ケース2:削除されたノードには子ノードがあります。
①削除されたノードがルートノードです。this.rootを削除するノードの子ノードにポイントします。
②削除されたノードがルートノードではありません。4つの特定の状況があります。
else if (current.rchild == null) { if (current == this.root) { this.root = current.lchild; } else if (isLeftChild) { parent.lchild = current.lchild; } else { parent.rchild = current.lchild; } } else if (current.lchild == null) { if (current == this.root) { this.root = current.rchild; } else if (isLeftChild) { parent.lchild = current.rchild; } else { parent.rchild = current.rchild; } }
- 2.3ケース3:削除されたノードには2つの子ノードがあります。
削除するノードに2つの子ノードがある場合、子ノードにまだノードが残っている可能性があるため、削除したノードを置き換えるノードを見つける必要があります。
削除されたノードより少し大きいもの、または削除されたノードより少し小さいもの、つまり削除されたノードに最も近い置換ノードを見つけることができます。
次に、次のことを行う必要があります。
削除されたノードより少し小さい値は、削除されたノードの左側のサブツリーの最大値(先行)である必要があります。
削除されたノードよりわずかに大きい値は、削除されたノードの右側のサブツリーの最小値(後続)である必要があります。
ここでは、その後のノードの検索と置換の方法を使用します
①削除されたノードがルートノードであり、this.rootを置換するノードにポイントし、置換されたノードの親ノードを右側のノードに接続します。
②削除されたノードがルートノードではありません。
// 2.3删除的结点两个子结点 else { // 1、寻找后继 var replaceNode = this.getReplaceNode(current); // 2、判断是否是根结点 if (current == this.root) { this.root = replaceNode; } else if (isLeftChild) { parent.lchild = replaceNode; } else { parent.rchild = replaceNode; } // 3、将替代的结点的左子树 = 删除结点的左子树 replaceNode.lchild = current.lchild; }
// 找后继的方法 BinarySearchTree.prototype.getReplaceNode = function (delNode) { // 1、定义变量,来保存找到的后继 var replaceNode = delNode; // 替代的结点 var previous = delNode.rchild; //删除结点的右结点 var replaceParent = delNode; // 2、循环查找 while(previous != null){ replaceParent = replaceNode; replaceNode = previous; previous = previous.lchild; } // 3、判断找到的替换结点(后继)是否就是delNode的rchild结点,如果是则填补结点 if (replaceNode != delNode.rchild) { // 后继结点必定是小于两个子结点的结点,将后继结点的父结点的左结点指向替代结点的右结点,连接操作。 replaceParent.lchild = replaceNode.rchild; // 替换 replaceNode.rchild = delNode.rchild; } return replaceNode; }
コードテスト:
// 测试
// 1、创建BinarySearchTree
var bst = new BinarySearchTree();
// 2、插入数据
bst.insert(18);
bst.insert(4);
bst.insert(2);
bst.insert(7);
bst.insert(30);
bst.insert(24);
bst.insert(20);
// 3、先序遍历
var result1 = '';
bst.preOrderTraversal(function (key){
result1 += key + ' ';
})
console.log('先序遍历:' + result1); //18 4 2 7 30 24 20
18を削除
// 6、删除操作
// 删除根结点18
bst.remove(18);
// 先序遍历
var result4 = '';
bst.preOrderTraversal(function (key){
result4 += key + ' ';
})
console.log('先序遍历为18 4 2 7 30 24 20 ,删除结点18后得到先序遍历结果:' + result4);
//先序遍历为18 4 2 7 30 24 20 ,删除结点18后得到先序遍历结果:20 4 2 7 30 24
// 插入数据
bst.insert(26);
bst.insert(21);
bst.insert(25);
bst.insert(27);
// 先序遍历
var result5 = '';
bst.preOrderTraversal(function (key){
result5 += key + ' ';
})
console.log('在先序为20 4 2 7 30 24 的二叉树搜索树中插入26、21、25、27,得到先序遍历结果:' + result5);
//在先序为20 4 2 7 30 24 的二叉树搜索树中插入26、27、28、29,得到先序遍历结果:20 4 2 7 30 24 21 26 25 27
24を削除
// 删除结点24
bst.remove(24);
// 先序遍历
var result6 = '';
bst.preOrderTraversal(function (key){
result6 += key + ' ';
})
console.log('先序遍历为20 4 2 7 30 24 21 26 25 27 ,删除结点24后得到先序遍历结果:' + result6);
//先序遍历为20 4 2 7 30 24 21 26 25 27 ,删除结点24后得到先序遍历结果:20 4 2 7 30 25 21 26 27
フルプリント
7つの完全なコード
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>二叉搜索树的相关方法</title>
</head>
<body>
<script type="text/javascript">
function BinarySearchTree(){
// 新结点创建的构造函数
function Node(key){
this.key = key;
this.lchild = null;
this.rchild = null;
}
// 保存根的属性
this.root = null;
// 创建关于二叉搜索树的相关操作方法
// 插入元素,给用户使用
BinarySearchTree.prototype.insert = function(key){
// 1、创建新结点
var newNode = new Node(key);
// 2、判断是否为空树
// 2.1如果是空树,则让根的属性指向新结点
if (this.root == null){
this.root = newNode;
}else{// 2.2如果不是非空树,则寻找合适的位置插入
this.insertNode(this.root, newNode);
}
}
// 用于内部使用
BinarySearchTree.prototype.insertNode = function(node, newNode){
// 1、比较结点与新结点的值大小
// 1.1如果结点的值比新结点的值小
if (node.key < newNode.key) {
// 比较结点的右子树为空时插入,不为空时继续往下比较
if (node.rchild == null) {
node.rchild = newNode;
}else{
this.insertNode(node.rchild, newNode);
}
}else{ // 1.2 如果结点的值比新结点的值大
// 比较结点的左子树为空时插入,不为空时继续往下比较
if (node.lchild == null) {
node.lchild = newNode;
}else{
this.insertNode(node.lchild, newNode);
}
}
}
// 先序遍历
BinarySearchTree.prototype.preOrderTraversal = function(fun){
// 让preOrderTraversalNode去实现递归操作
this.preOrderTraversalNode(this.root, fun)
}
// 内部使用
BinarySearchTree.prototype.preOrderTraversalNode = function(node, fun){
if (node) {
// 1、打印当前结点
fun(node.key);
// 2、遍历所有的左子树;
this.preOrderTraversalNode(node.lchild, fun);
// 3、遍历所有的右子树。
this.preOrderTraversalNode(node.rchild, fun);
}
}
// 中序遍历
BinarySearchTree.prototype.midOrderTraversal = function(fun){
// 让midOrderTraversalNode去实现递归操作
this.midOrderTraversalNode(this.root, fun)
}
// 内部使用
BinarySearchTree.prototype.midOrderTraversalNode = function(node, fun){
if (node) {
// 1、遍历所有的左子树;
this.midOrderTraversalNode(node.lchild, fun);
// 2、打印当前结点
fun(node.key);
// 3、遍历所有的右子树。
this.midOrderTraversalNode(node.rchild, fun);
}
}
// 后序遍历
BinarySearchTree.prototype.postOrderTraversal = function(fun){
// 让postOrderTraversalNode去实现递归操作
this.postOrderTraversalNode(this.root, fun)
}
// 内部使用
BinarySearchTree.prototype.postOrderTraversalNode = function(node, fun){
if (node) {
// 1、遍历所有的左子树;
this.postOrderTraversalNode(node.lchild, fun);
// 2、遍历所有的右子树。
this.postOrderTraversalNode(node.rchild, fun);
// 3、打印当前结点
fun(node.key);
}
}
// 最大值
BinarySearchTree.prototype.max = function(){
var node = this.root;
var key = null;
while(node){
key = node.key;
node = node.rchild;
}
return key;
}
// 最小值
BinarySearchTree.prototype.min = function(){
var node = this.root;
var key = null;
while(node){
key = node.key;
node = node.lchild;
}
return key;
}
// 查找某个key
BinarySearchTree.prototype.searchKey = function(key){
var node = this.root;
while(node != null){
if (node.key < key) {
node = node.rchild;
}else if (node.key > key) {
node = node.lchild;
}else{
return true;
}
}
return false;
}
// 删除某个结点
BinarySearchTree.prototype.remove = function(key){
// 1、寻找要删除的结点
// 1.1 定义变量,保存信息
var current = this.root; // 保存根结点
var parent = null; // 记录删除结点的父节点
var isLeftChild = true; //判断删除的结点是否是该父结点的左子结点
// 1.2 查找要删除的结点
while(current.key != key){
parent = current;
if (key < current.key) {
isLeftChild = true;
current = current.lchild;
}else{
isLeftChild = false;
current = current.rchild;
}
// 已经找到最后一个结点还没有找到就退出
if (current == null) return false;
}
// 2、根据对应的情况删除结点
// 2.1删除的结点是叶子结点(即没有子结点)
if (current.lchild == null && current.rchild == null) {
if (current == this.root) {
this.root = null;
} else if (isLeftChild) {
parent.lchild = null;
} else {
parent.rchild = null;
}
}
// 2.2删除的结点有一个子结点
else if (current.rchild == null) {
if (current == this.root) {
this.root = current.lchild;
} else if (isLeftChild) {
parent.lchild = current.lchild;
} else {
parent.rchild = current.lchild;
}
} else if (current.lchild == null) {
if (current == this.root) {
this.root = current.rchild;
} else if (isLeftChild) {
parent.lchild = current.rchild;
} else {
parent.rchild = current.rchild;
}
}
// 2.3删除的结点两个子结点
else {
// 1、寻找后继
var replaceNode = this.getReplaceNode(current);
// 2、判断是否是根结点
if (current == this.root) {
this.root = replaceNode;
} else if (isLeftChild) {
parent.lchild = replaceNode;
} else {
parent.rchild = replaceNode;
}
// 3、将替代的结点的左子树 = 删除结点的左子树
replaceNode.lchild = current.lchild;
}
}
// 找后继的方法
BinarySearchTree.prototype.getReplaceNode = function (delNode) {
// 1、定义变量,来保存找到的后继
var replaceNode = delNode; // 替代的结点
var previous = delNode.rchild; //删除结点的右结点
var replaceParent = delNode;
// 2、循环查找
while(previous != null){
replaceParent = replaceNode;
replaceNode = previous;
previous = previous.lchild;
}
// 3、判断找到的替换结点(后继)是否就是delNode的rchild结点,如果是则填补结点
if (replaceNode != delNode.rchild) {
// 后继结点必定是小于两个子结点的结点,将后继结点的父结点的左结点指向替代结点的右结点,连接操作。
replaceParent.lchild = replaceNode.rchild;
// 替换
replaceNode.rchild = delNode.rchild;
}
return replaceNode;
}
}
// 测试
// 1、创建BinarySearchTree
var bst = new BinarySearchTree();
// 2、插入数据
bst.insert(18);
bst.insert(4);
bst.insert(2);
bst.insert(7);
bst.insert(30);
bst.insert(24);
bst.insert(20);
// 3、先序遍历
var result1 = '';
bst.preOrderTraversal(function (key){
result1 += key + ' ';
})
console.log('先序遍历:' + result1); //18 4 2 7 30 24 20
// 4、中序遍历
var result2 = '';
bst.midOrderTraversal(function (key){
result2 += key + ' ';
})
console.log('中序遍历:' + result2); //2 4 7 18 20 24 30
// 5、后序遍历
var result3 = '';
bst.postOrderTraversal(function (key){
result3 += key + ' ';
})
console.log('后序遍历:' + result3); //2 7 4 20 24 30 18
console.log('最大值是' + bst.max());
console.log('最小值是' + bst.min());
console.log('是否存在24?' + bst.searchKey(24)); //true
console.log('是否存在30?' + bst.searchKey(30)); //true
console.log('是否存在5?' + bst.searchKey(5)); //false
console.log('是否存在8?' + bst.searchKey(8)); //false
console.log('是否存在500?' + bst.searchKey(500)); //false
// 6、删除操作
// 删除根结点18
bst.remove(18);
// 先序遍历
var result4 = '';
bst.preOrderTraversal(function (key){
result4 += key + ' ';
})
console.log('先序遍历为18 4 2 7 30 24 20 ,删除结点18后得到先序遍历结果:' + result4);
//先序遍历为18 4 2 7 30 24 20 ,删除结点18后得到先序遍历结果:20 4 2 7 30 24
// 插入数据
bst.insert(26);
bst.insert(21);
bst.insert(25);
bst.insert(27);
// 先序遍历
var result5 = '';
bst.preOrderTraversal(function (key){
result5 += key + ' ';
})
console.log('在先序为20 4 2 7 30 24 的二叉树搜索树中插入26、21、25、27,得到先序遍历结果:' + result5);
//在先序为20 4 2 7 30 24 的二叉树搜索树中插入26、27、28、29,得到先序遍历结果:20 4 2 7 30 24 21 26 25 27
// 删除结点24
bst.remove(24);
// 先序遍历
var result6 = '';
bst.preOrderTraversal(function (key){
result6 += key + ' ';
})
console.log('先序遍历为20 4 2 7 30 24 21 26 25 27 ,删除结点24后得到先序遍历结果:' + result6);
//先序遍历为20 4 2 7 30 24 21 26 25 27 ,删除结点24后得到先序遍历结果:20 4 2 7 30 25 21 26 27
</script>
</body>
</html>