【数据结构与算法】详解什么是树结构,并用代码手动实现一个二叉查找树

这就一个正确的完全二叉树


再来看一个例子,如图

在这里插入图片描述

这就不是一个完全二叉树了,虽然最后一层的结点连续集中在一起,但是它们集中在最后一层的右侧,这并不满足完全二叉树的定义


其实 满二叉树 是一种特殊的 完全二叉树,不信你可以自己举个简单的例子验证一下

七、二叉树的特性

=================================================================

二叉树作为树结构中一种特殊的类型,它是有一些自己的特性的,我们来看一下

(1)特性一


一个二叉树第 i 层的最大结点个数为 2 i − 1 ( i > = 1 ) 2^{i-1}(i>=1) 2i−1(i>=1);

在二叉树中,结点个数最多的情况就是满二叉树,即除了最后一层的叶子结点外,其余结点都有两个子结点,因此满二叉树每一层的结点个数都达到了最大值,其余类型的二叉树每一层结点个数只会小于或等于它

我们可以自己来验证一下,下图时一个树的深度为 3 的满二叉树

在这里插入图片描述

第一层结点个数最多只有 2 0 = 1 2^0 = 1 20=1 个,图中有一个结点

第二层结点个数最多只有 2 1 = 2 2^1 = 2 21=2 个,图中有两个结点

第三层结点个数最多只有 2 2 = 4 2^2 = 4 22=4 个,图中有四个结点

(2)特性二


深度为 k 的二叉树拥有的最大结点数为 2 k − 1 ( k > = 1 ) 2^k-1(k>=1) 2k−1(k>=1);

同样的,满二叉树是二叉树中结点个数最多的,因此其结点个数公式就为 2 k − 1 ( k > = 1 ) 2^k-1(k>=1) 2k−1(k>=1)

我们可以验证一下

在这里插入图片描述

这是一个深度为 1 的满二叉树,最大结点个数为 2 0 − 1 = 1 2^0-1 = 1 20−1=1 个


在这里插入图片描述

这是一个深度为 2 的满二叉树,最大结点个数为 2 2 − 1 = 3 2^2-1 = 3 22−1=3 个


在这里插入图片描述

这是一个深度为 3 的满二叉树,最大结点个数为 2 3 − 1 = 7 2^3-1 = 7 23−1=7 个

(3)特性三


在非空二叉树中, n 0 n_0 n0​ 表示叶子结点个数, n 1 n_1 n1​ 表示分支结点个数,那么它们满足的关系有 n 0 = n 1 + 1 n_0=n_1 + 1 n0​=n1​+1;

该特性也是一个总结出来的规律,大家了解一下即可,可以自行验证一下

八、二叉树的存储

=================================================================

在使用二叉树存储数据时,我们有两种选择,一种是数组存储,另一种是链表存储

(1)数组存储


当使用数组存储时,如图

在这里插入图片描述

在按照从上到下,从左到右的顺序给二叉树标上下标以后,我们发现在使用数组存储二叉树的数据时,很难或者说几乎无法分辨结点之间的结点关系

因此,有一种解决办法就是将二叉树补全成一个满二叉树,然后下标仍是按照从上到下,从左到右的顺序给的,如图

在这里插入图片描述

此时,我们就可以看到,各个结点之间就可以分辨得出来了,即父节点的下标值 index * 2 + 1,就等于其左子结点的下标值 ;同样的,父节点的下标值 index * 2 + 2,就等于其右子结点的下标值

虽然现在结点间的关系可以辨别得出来了,但是有没有发现,这造成了很大的空间浪费,整个数组长度为 15,空着的位置就有 8

所以我个人认为,用数组来存储二叉树的数据不太合适

(2)链表存储


链表存储也是二叉树最常用的一种存储方法。我们可以通过给每个结点封装一个对象,通过 leftright 分别指向其左子结点和右子结点

来看一下,用链表存储的二叉树的样子,如图

在这里插入图片描述

我们只需要调用某结点的 leftright 就可以找到其子结点,这样做既避免了空间的浪费,又能把结点关系理得特别清楚

九、什么是二叉查找树

===================================================================

二叉查找树,英文名为 Binary Search Tree,简称BST,又名二叉排序树、二叉搜索树。

二叉查找树本身就是一棵二叉树,它可以是一棵空树

当二叉查找树不为空时,必须满足以下三个条件

  1. 非空左子树的结点的 key 小于其根结点的 key

  2. 非空右子树的结点的 key 大于其根结点的 key

  3. 左子树和右子树本身也是个二叉查找树

我们可以先一个例子来体会一下这三个条件,如图

在这里插入图片描述

首先根结点的 key50 ,其左子结点为 10 小于 50,符合第一个条件 ;其右子结点为 70 大于 50,符合第二个条件

再来看 结点10,其左子结点为 8 小于 10,符合第一个条件 ;其右子结点为 13 大于 10,符合第二个条件

同理,结点70 也是符合的,因此就符合了第三个条件


总结一下呢,就是以下几条结论:

  1. 左边的结点永远比根结点以及右边的结点小

  2. 右边的结点永远比根结点已经左边的结点大


所以,接下来我们再来看几个例子,判断一下哪些时二叉查找树,哪些不是

在这里插入图片描述

这不是一个二叉查找树,因为 结点7 在右侧,比其根结点 8 要小,所以不符合条件。正确的位置应该是处于 结点8 的左子树位置


在这里插入图片描述

这是一个二叉查找树

十、树的遍历

===============================================================

以上给出的树结构的图都是我们布局好的非常美观的样子,但在程序里,树结构是非常抽象的,因此我们可以通过遍历全部结点的方式,将整个树结构展现出来

树的遍历一共分成三种,分别是 先序遍历中序遍历后序遍历

(1)先序遍历


先序遍历: 访问根结点 => 访问左子树 => 访问右子树。在访问左子树或右子树的时候,仍是按照这个规则继续访问。

我们来看一个简单的例子,如图,对其进行先序遍历

在这里插入图片描述


第一步: 先访问根结点 50 ,再访问左子树,最后访问右子树,如图

在这里插入图片描述

此时我们记录一下访问的过程,即 50 左子树10 右子树70


第二步: 左子树10 也需要按照先序遍历的步骤进行,所以先访问 左子树10 中的根节点 10,再遍历其左子树,最后遍历其右子树,如图

在这里插入图片描述

因为 结点10 的左右子树都属于叶子结点了,即没有任何的子结点了,所需就无需对其进行遍历了

我们接着第一步中的结果进行记录,即 50 10 8 13 右子树70


第三步: 最后还剩个 右子树70 没有遍历了,那么同理,先访问其根结点 70,再遍历其左子树,最后遍历其右子树,如图

在这里插入图片描述

因为 结点70 的左右子树也都属于叶子结点了,所以也没有必要对其进行遍历了,直接获取该结点即可

我们接着第二步的结果进行记录,即 50 10 8 13 70 60 80


好了,到此位置,一个先序遍历的结果就出来了,我们来总结一下它的全部访问过程,如图

在这里插入图片描述

(2)中序遍历


中序遍历: 访问左子树 => 访问根结点 => 访问右子树。在访问左子树或右子树的时候,仍是按照这个规则继续访问。

我们来看一个简单的例子,如图,对其进行中序遍历

在这里插入图片描述


第一步: 先遍历 左子树10,再访问根结点 50,最后遍历右子树 70,如图

在这里插入图片描述

此时我们记录一下访问的过程,即 左子树10 50 右子树70


第二步: 左子树10 也需要按照中序遍历的步骤进行,所以先遍历 左子树10 中的左子树,再访问其根节点 10,最后遍历其右子树,如图

在这里插入图片描述

因为 结点10 的左右子树都属于叶子结点了,即没有任何的子结点了,所需就无需对其进行遍历了

我们接着第一步中的结果进行记录,即 8 10 13 50 右子树70


第三步: 最后还剩个 右子树70 没有遍历了,那么同理,先遍历 右子树70 的左子树,再访问其根结点 70,最后遍历其右子树,如图

在这里插入图片描述

因为 结点70 的左右子树也都属于叶子结点了,所以也没有必要对其进行遍历了,直接获取该结点即可

我们接着第二步的结果进行记录,即 8 10 13 50 60 70 80


好了,到此位置,一个中序遍历的结果就出来了,我们来总结一下它的全部访问过程,如图

在这里插入图片描述

(3)后序遍历


后序遍历: 访问左子树 => 访问右子树 => 访问根结点 。在访问左子树或右子树的时候,仍是按照这个规则继续访问。

我们来看一个简单的例子,如图,对其进行后序遍历

在这里插入图片描述


第一步: 先遍历 左子树10,再遍历右子树 70,最后访问根结点 50,如图

在这里插入图片描述

此时我们记录一下访问的过程,即 左子树10 右子树70 50


第二步: 左子树10 也需要按照后序遍历的步骤进行,所以先遍历 左子树10 中的左子树,再遍历其右子树,最后再访问其根节点 10,如图

在这里插入图片描述

因为 结点10 的左右子树都属于叶子结点了,即没有任何的子结点了,所需就无需对其进行遍历了

我们接着第一步中的结果进行记录,即 8 13 10 右子树70 50


第三步: 最后还剩个 右子树70 没有遍历了,那么同理,先遍历 右子树70 的左子树,再遍历其右子树,最后访问其根结点 70,如图

在这里插入图片描述

因为 结点70 的左右子树也都属于叶子结点了,所以也没有必要对其进行遍历了,直接获取该结点即可

我们接着第二步的结果进行记录,即 8 13 10 60 80 70 50


好了,到此位置,一个后序遍历的结果就出来了,我们来总结一下它的全部访问过程,如图

在这里插入图片描述

十一、二叉查找树的方法

====================================================================

在封装二叉查找树之前,我们还是先来看一下二叉查找树常见的方法右哪些吧

| 方法 | 作用 |

| — | — |

| insert() | 向二叉查找树插入数据 |

| preOrder() | 先序遍历二叉查找树,并返回结果 |

| inOrder() | 中序遍历二叉查找树,并返回结果 |

| postOrder() | 后序遍历二叉查找树,并返回结果 |

| getMax() | 返回二叉查找树中的最大值 |

| getMin() | 返回二叉查找树中的最小值 |

| search() | 查找二叉查找树中的某个值 |

| remove() | 移除某个值 |

十二、用代码实现二叉查找树

======================================================================

前提:

  1. 代码中会用到大量的递归思想,请还没了解过递归的小伙伴自行了解一下

  2. 部分方法会通过再封装一个内部函数来实现,请认真理解

(1)创建一个构造函数


首先创建一个大的构造函数,用于存放二叉查找树的一些属性和方法。

function BinarySearchTree() {

// 属性

this.root = null

}

二叉查找树的属性最主要的就是 root ,用于指向树的根节点

(2)创建结点构造函数


因为我们准备通过链表来实现二叉查找树,所以我们需要先在内部封装一个结点的构造函数

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

}

一个结点包括的内容有 左子结点右子结点,分别对应的 keyvalueleftright

(3)实现insert()方法


insert()方法就是将一个数据插入到二叉查找树中合适的位置。该方法接收两个参数,即 keyvalue

实现思路:

  1. 调用内部结点构造函数,并把参数 keyvalue 传入,生成一个结点对象 node

  2. 判断二叉查找树是否右根结点,即判断是否为空,若为空,则直接将 node 作为二叉查找树的根结点,即将 node 赋值给 root 属性

  3. 若不为空,则遍历整个二叉查找树,用 node.key 与遍历到的结点的 key 值进行比对,最终找到合适的位置进行插入

思路看着略微复杂,我做了动图方便大家理解,如图

  • 当二叉查找树为空时

在这里插入图片描述

  • 当二叉查找树不为空时

在这里插入图片描述

这里我选择用递归的方式来遍历整个二叉查找树,因此我会再额外封装一个用于递归内部调用的函数 insertNode ,给其传入两个参数,第一个参数是当前遍历到的结点 ; 第二个参数是我们要插入的结点

先来看下代码吧

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 插入数据

BinarySearchTree.prototype.insert = function(key, value = null) {

// 1. 创建结点

let node = new Node(key, value)

// 2. 判断根结点是否存在

// 2.1 不存在

if(!this.root) {

this.root = node

return;

}

// 2.2 存在

this.insertNode(this.root, node)

}

// 插入结点函数(内部)

BinarySearchTree.prototype.insertNode = function(oldNode, newNode) {

// 1. 判断我们插入的数据的 key是否大于当前遍历结点的 key

// 1.1 插入数据的 key 大于当前遍历结点的 key

if(newNode.key < oldNode.key) {

// 1.1.1 判断当前遍历结点的左结点是否为空

// 1.1.1.1 为空

if(oldNode.left === null) {

oldNode.left = newNode

}

// 1.1.1.2 不为空

else {

this.insertNode(oldNode.left, newNode)

}

}

// 1.2 插入数据的 key 小于当前遍历结点的 key

else {

// 1.2.1 判断当前遍历结点的右结点是否为空

// 1.2.1.1 为空

if(oldNode.right === null) {

oldNode.right = newNode

}

// 1.2.1.2 不为空

else {

this.insertNode(oldNode.right, newNode)

}

}

}

}

insert() 方法中,若二叉查找树不为空,我们就调用 insertNode() 内部方法进行递归调用,并先把 root 和 我们新创建的结点 node 传过去当成参数 , 即表示用需要插入的结点先和根节点进行比较,然后慢慢比对下去,找到属于自己的位置插入

我在代码上都标注了很详细的注解,大家可以消化消化

我们来使用一下该方法

let bst = new BinarySearchTree()

bst.insert(50)

bst.insert(10)

bst.insert(70)

bst.insert(5)

bst.insert(15)

console.log(bst.root)

因为这里我们还没有封装遍历的函数,因此可以靠浏览器的打印来查看二叉查找树是否正确,结果如下

在这里插入图片描述

首先,根节点为 50,然后根节点的左子节点为 10,右子结点为 70

在这里插入图片描述

然后看到 结点10 的左子结点是 5,右子结点是 15

在这里插入图片描述

最后,结点70 没有自己的左右子结点

总结一下,当前的二叉树如下图

在这里插入图片描述

判断一下,这个结果是正确的,这确实是一个二叉查找树,所以我们的 insert()方法就封装好啦

(4)实现preOrder()方法


preOrder()方法就是通过先序遍历的方式遍历整个二叉查找树,并返回遍历结果。该方法接收一个回调函数 handle 作为参数, 用于在遍历过程中执行某些操作

实现思路:

  1. 从根结点开始,按照 访问根结点 => 访问左子树 => 访问右子树 的顺序对各个结点进行访问

  2. 访问到结点时,执行回调函数 handle ,并将访问到的结点的 key 作为参数传入

因为上边已经详细将结果先序遍历的全过程了,因此我们直接来看代码

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 先序遍历并返回结果(外部函数)

BinarySearchTree.prototype.preOrder = function(handle) {

// 从整棵二叉查找树的根节点开始遍历

this.preOrderNodes(this.root, handle)

}

// 以先序遍历的方式遍历整个树(内部函数)

BinarySearchTree.prototype.preOrderNodes = function(node, handle) {

if(node !== null) {

// 将根结点的 key传给回调函数处理

handle(node.key)

// 遍历左子树

this.preOrderNodes(node.left, handle)

// 遍历右子树

this.preOrderNodes(node.right, handle)

}

}

}

我们来使用一下该方法,并详细体会一下递归调用的过程是不是跟我们前面分析的先序遍历的思想一样

let bst = new BinarySearchTree()

bst.insert(50)

bst.insert(10)

bst.insert(70)

bst.insert(5)

bst.insert(15)

let str = ‘’

bst.preOrder(function(key) {

str += ${key}

})

console.log(str) // 50 10 5 15 70

首先二叉查找树是这样的

在这里插入图片描述

  1. 刚开始我们从整棵树的根节点 root 开始遍历,先记录 rootkey值,即 50 ;然后遍历 root 的左子树,因此把 root.left 作为下一次调用 preOrderNodes() 方法的根结点

  2. 然后记录一下 结点10,即 50 10,然后我们又要继续遍历 结点10 的左子树,所以就把 结点10.left 作为下一次调用 preOrderNodes() 方法的根结点

  3. 此时访问到了 结点5,并记录一下 50 10 5,再下一次就是遍历 结点5 的左右子树,但因为 结点5 的左右子树都为 null,所以在调用preOrderNodes() 方法时不进行任何操作。到此为止, 结点10 的左子树已经全部遍历完毕,接着就要遍历其右子树,因此把 结点10.right 作为下一次调用 preOrderNodes() 方法的根结点

  4. 此时就访问到了 结点15,并记录一下 50 10 5 15,再下一次就是遍历 结点15 的左右子树,但因为 结点15 的左右子树都为 null,所以在调用preOrderNodes() 方法时不进行任何操作。到此为止,结点50 的左子树已经全部遍历完毕了,接着就要遍历其右子树,因此把 结点50.right 作为下一次调用 preOrderNodes() 方法的根结点

  5. 此时就访问到了 结点70,并记录一下 50 10 5 15 70,再下一次就是遍历 结点15 的左右子树,但因为 结点15 的左右子树都为 null,所以在调用preOrderNodes() 方法时不进行任何操作。到此为止,结点50 的右子树也全部遍历完毕了。

  6. 因为整棵树的根节点的左右子树都遍历完了,所以先序遍历的操作也就完成了,最终结果就为 50 10 5 15 70

(5)实现inOrder()方法


inOrder()方法就是通过中序遍历的方式遍历整个二叉查找树,并返回遍历结果。该方法无需传入参数

实现思路:

  1. 从根结点开始,按照 访问左子树 => 访问根节点 => 访问右子树 的顺序对各个结点进行访问

  2. 访问到结点时,执行回调函数 handle ,并将访问到的结点的 key 作为参数传入

因为上边已经详细将结果中序遍历的全过程了,因此我们直接来看代码

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 中序遍历并返回结果(外部函数)

BinarySearchTree.prototype.inOrder = function(handle) {

// 从二叉查找树的根结点开始遍历

this.inOrderNodes(this.root, handle)

}

// 以中序遍历的方式遍历整个树(内部函数)

BinarySearchTree.prototype.inOrderNodes = function(node, handle) {

if(node !== null) {

// 遍历左子树

this.inOrderNodes(node.left, handle)

// 将根结点的 key传给回调函数处理

handle(node.key)

// 遍历右子树

this.inOrderNodes(node.right, handle)

}

}

}

因为树结构的三种遍历方式思想都一样,只不过是遍历的顺序有所差别,这里我就不再花大篇幅来讲解整个遍历过程了,直接来使用一下代码,核对一下结果是否准确

let bst = new BinarySearchTree()

bst.insert(50)

bst.insert(10)

bst.insert(70)

bst.insert(5)

bst.insert(15)

let str = ‘’

bst.inOrder(function(key) {

str += ${key}

})

console.log(str) // 5 10 15 50 70

验证了一下,结果是正确的

(6)实现postOrder()方法


postOrder()方法就是通过后序遍历的方式遍历整个二叉查找树,并返回遍历结果。该方法无需传入参数

实现思路:

  1. 从根结点开始,按照 访问左子树 => 访问右子树 => 访问根结点 的顺序对各个结点进行访问

  2. 访问到结点时,执行回调函数 handle ,并将访问到的结点的 key 作为参数传入

因为上边已经详细将结果后序遍历的全过程了,因此我们直接来看代码

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 后序遍历并返回结果(外部函数)

BinarySearchTree.prototype.postOrder = function(handle) {

// 从二叉查找树的根结点开始遍历

this.postOrderNodes(this.root, handle)

}

// 以后序遍历的方式遍历整个树(内部函数)

BinarySearchTree.prototype.postOrderNodes = function(node, handle) {

if(node !== null) {

// 访问左子树

this.postOrderNodes(node.left, handle)

// 访问右子树

this.postOrderNodes(node.right, handle)

// 将根结点的 key传给回调函数处理

handle(node.key)

}

}

}

因为树结构的三种遍历方式思想都一样,只不过是遍历的顺序有所差别,这里我就不再花大篇幅来讲解整个遍历过程了,直接来使用一下代码,核对一下结果是否准确

let bst = new BinarySearchTree()

bst.insert(50)

bst.insert(10)

bst.insert(70)

bst.insert(5)

bst.insert(15)

let str = ‘’

bst.postOrder(function(key) {

str += ${key}

})

console.log(str) // 5 15 10 70 50

验证了一下,结果是正确的

(7)实现getMax()方法


getMax()方法就是找到二叉查找树中 key 值最大的结点,并返回该结点对象

实现思路: 该方法思路比较简单,因为二叉查找树越大的值都是往右走的,即 key值较大的结点都是其父结点的右子结点,因此我们可以从整个二叉查找树的根节点开始,一直向右查找,即 node.right,直到 node.right === null,该结点就为这个二叉查找树中 key值最大的结点

我们来直接看一下代码吧

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 获取二叉树中的最大值

BinarySearchTree.prototype.getMax = function() {

// 从根结点开始遍历

let node = this.root

// 一直向二叉查找树的的右边遍历,直到结点没有右子结点

while(node.right !== null) {

node = node.right

}

// 返回 key值最大的结点对象

return node

}

}

我们来使用一下该方法

let bst = new BinarySearchTree()

bst.insert(50)

bst.insert(10)

bst.insert(70)

bst.insert(5)

bst.insert(15)

bst.insert(60)

bst.insert(80)

bst.insert(13)

bst.insert(12)

bst.insert(14)

bst.insert(19)

bst.insert(20)

bst.insert(16)

bst.insert(17)

console.log(bst.getMax())

// Node { key: 80, value: null, right: null, left: null }

可以看到,最终结果确实返回了 key值最大的结点,为 结点80

(8)实现getMin()方法


getMin()方法就是找到二叉查找树中 key 值最小的结点,并返回该结点对象

实现思路: 该方法思路比较简单,因为二叉查找树越小的值都是往左走的,即 key值较小的结点都是其父结点的左子结点,因此我们可以从整个二叉查找树的根节点开始,一直向左查找,即 node.left,直到 node.left === null,该结点就为这个二叉查找树中 key值最小的结点

我们来直接看一下代码吧

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 获取二叉树中的最小值

BinarySearchTree.prototype.getMin = function() {

// 从二叉查找树的根结点开始遍历

let node = this.root

// 一直向二叉查找树的左边遍历,直到结点没有左子结点

while(node.left !== null) {

node = node.left

}

// 返回 key值最小的结点对象

return node

}

}

我们来使用一下该方法

let bst = new BinarySearchTree()

bst.insert(50)

bst.insert(10)

bst.insert(70)

bst.insert(5)

bst.insert(15)

bst.insert(60)

bst.insert(80)

bst.insert(13)

bst.insert(12)

bst.insert(14)

bst.insert(19)

bst.insert(20)

bst.insert(16)

bst.insert(17)

console.log(bst.getMin())

// Node { key: 5, value: null, right: null, left: null }

可以看到,最终结果确实返回了 key值最小的结点,为 结点5

(9)实现search()方法


search()方法就是查找二叉查找树中指定的 key对应的结点的 value 值。该方法接收一个参数,即需要查找的结点的 key

实现思路: 从二叉查找树的根节点 root 开始遍历,用我们的参数 key1 与遍历到的结点的 key2 进行比较,若 key1 > key2,则向右继续遍历 ;若 key1 < key2,则向左继续遍历 ;若 key1 === key2,则返回该结点的 value 值 ;若遍历到最后,找不到任何结点了,则返回 false

我们来看一下实现代码

function BinarySearchTree() {

// 属性

this.root = null

// 结点构造函数

function Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 查找指定的 key对应的数据

BinarySearchTree.prototype.search = function(key) {

// 1. 从二叉查找树的根结点开始遍历

let node = this.root

// 2. 一直与遍历到的结点的 key值进行比较

while(node !== null) {

// 查找的 key值大于当前结点 key值

if(key > node.key) {

node = node.right

}

// 查找的 key值小于当前结点 key值

else if(key < node.key) {

node = node.left

}

// 查找到对应 key值的结点

else {

return node.value

}

}

// 未找到对应 key值的结点

return false

}

}

因为这个方法要返回结点的 value 值,因此这里我们就给每个结点赋值一定的 value

let bst = new BinarySearchTree()

bst.insert(50, ‘我是结点50’)

bst.insert(10, ‘我是结点10’)

bst.insert(70, ‘我是结点70’)

bst.insert(5, ‘我是结点5’)

bst.insert(15, ‘我是结点15’)

bst.insert(60, ‘我是结点60’)

bst.insert(80, ‘我是结点80’)

bst.insert(13, ‘我是结点13’)

bst.insert(12, ‘我是结点12’)

bst.insert(14, ‘我是结点14’)

bst.insert(19, ‘我是结点19’)

bst.insert(20, ‘我是结点20’)

bst.insert(16, ‘我是结点16’)

bst.insert(17, ‘我是结点17’)

console.log(bst.search(13)) // 我是结点13

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
n Node(key, value) {

this.key = key

this.value = value

this.right = null

this.left = null

}

// 查找指定的 key对应的数据

BinarySearchTree.prototype.search = function(key) {

// 1. 从二叉查找树的根结点开始遍历

let node = this.root

// 2. 一直与遍历到的结点的 key值进行比较

while(node !== null) {

// 查找的 key值大于当前结点 key值

if(key > node.key) {

node = node.right

}

// 查找的 key值小于当前结点 key值

else if(key < node.key) {

node = node.left

}

// 查找到对应 key值的结点

else {

return node.value

}

}

// 未找到对应 key值的结点

return false

}

}

因为这个方法要返回结点的 value 值,因此这里我们就给每个结点赋值一定的 value

let bst = new BinarySearchTree()

bst.insert(50, ‘我是结点50’)

bst.insert(10, ‘我是结点10’)

bst.insert(70, ‘我是结点70’)

bst.insert(5, ‘我是结点5’)

bst.insert(15, ‘我是结点15’)

bst.insert(60, ‘我是结点60’)

bst.insert(80, ‘我是结点80’)

bst.insert(13, ‘我是结点13’)

bst.insert(12, ‘我是结点12’)

bst.insert(14, ‘我是结点14’)

bst.insert(19, ‘我是结点19’)

bst.insert(20, ‘我是结点20’)

bst.insert(16, ‘我是结点16’)

bst.insert(17, ‘我是结点17’)

console.log(bst.search(13)) // 我是结点13

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-Mt7ybhh3-1715792499564)]

[外链图片转存中…(img-CuYu9qTc-1715792499565)]

[外链图片转存中…(img-Wq4qb588-1715792499565)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值