101道算法JavaScript描述【二叉树】4,Web前端studio基础教程

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

function find(data) {

let node = this.tree;

while (node !== null) {

if (node.data === data) {

  return node;

} else if (data < node.data) {

  node = node.left;

} else {

  node = node.right;

}

}

}




**二叉查找树的插入**



在二叉查找树中插入元素比较复杂,分为以下几种情况:



*   首先需要检查 BST 是否有根节点,如果没有,插入的节点就是根节点,就完事了

*   如果待插入的节点不是根节点,那么就需要遍历整棵树,找到合适的位置



如果要插入的数据大于当前节点,并且当前节点的右子树为空,就将新数据插入到右子节点的位置;如果不为空,就再递归遍历右子树,查找插入位置。同理,如果要插入的数据小于当前节点,并且节点的左子树为空,就将新数据插入到左子节点的位置;如果不为空,就再递归遍历左子树,查找插入位置。



![image-20220719163303793](https://img-blog.csdnimg.cn/img_convert/7d293fb97cdace0695de209ddd7fa3b0.png)



function insert(data) {

const n = new Node(data);

if (this.tree === null) {

this.tree = n;

return;

}

let node = this.tree;

while (node !== null) {

if (data > node.data) {

  if (node.right === null) {

    node.right = n;

    return;

  }

  node = node.right;

} else {

  if (data < node.data) {

    if (ndoe.left === null) {

      node.left = n;

      return;

    }

    node = node.left;

  }

}

}

}




**二叉查找树的删除**



在二叉查找树上删除节点的操作最复杂,牵一发而动全身,节点直接存在相互关联。如果删除的是没有子节点的节点,那就直接删掉就完事了。稍微麻烦的是删除节点只有一个子节点的情况,如果删除的节点包含两个子节点,那就是最麻烦的了。



![image-20220719163309786](https://img-blog.csdnimg.cn/img_convert/32802727a071bcd92e8cf9d4b1157715.png)



删除没有子节点的节点



如果没有子节点,直接把要删除的节点干掉就行



![image-20220719163316522](https://img-blog.csdnimg.cn/img_convert/0fd505c2595a90241194b3d69d4915b8.png)



删除只有一个子节点的节点



只有一个子节点只需要把更新父节点指向要删除节点的指针,指向要删除节点的子节点就可以了。比如图中删除节点的节点是 16,把 15 指向 13 的指针指向 20 即可。



现在来看最复杂的情况:



![image-20220719163324126](https://img-blog.csdnimg.cn/img_convert/16428f702930c584bd9ddec5eb4a793c.png)



删除有两个子节点的节点



有两个子节点,我们需要查找右子树上的最小值,把它拿到待删除的节点上,然后再删除这个最小的节点。删除这个最小的节点,又回到了上面讲的两条规则,这个最小节点不可能有两个子节点,如果有左子节点,就不是最小节点了。



function remove(data) {

let node = this.tree;

let parentNode;

while (node !== null && node.data != data) {

parentNode = node;

if (data > node.data) {

  node = node.right;

} else {

  node = node.left;

}

}

if (node === null) {

return; // 没找着

}

// 我们采用非递归的方式,先处理要删除的节点有两个子节点的情况

if (node.left !== null && node.right !== null) {

// 查找右子树中最小节点

let minNodeParent = node;

let minNode = minNodeParent.right;

while (minNodeParent.left !== null) {

  minNodeParent = minNode;

  minNode = minNode.left;

}

node.data = minNode.data;

node = minNode; // 下面就变成删除 minNode 了

parentNode = minNodeParent;

}

// 删除节点是叶子节点或者只有一个子节点

let childNode = null;

if (node.left !== null) {

childNode = node.left;

} else if (node.right !== null) {

childNode = node.right;

}

if (parentNode === null) { // 父节点是 null,说明删除的是根节点

this.tree = childNode;

// 第二种情况,把父节点的指向删除节点的指针指向删除节点的子节点

// 删除的是 node,把 parentNode 指向 node 的指针,指向 childNode

} else if (parentNode.left === node) {

parentNode.left = childNode;

} else {

parentNode.right = childNode;

}

}




本章节分为 4 个部分:



*   Part 1

    *   最小栈

    *   Shuffle an Array

    *   将有序数组转换为二叉搜索树

*   Part 2

    *   对称二叉树

    *   二叉树的最大深度

    *   验证二叉搜索树

*   Part 3

    *   二叉树的层次遍历

    *   二叉树的序列化与反序列化

*   Part 4

    *   中序遍历二叉树

    *   从前序与中序遍历序列构造二叉树

    *   二叉搜索树中第 K 小的元素

*   Part 5

    *   填充每个节点的下一个右侧节点指针

    *   岛屿数量

    *   二叉树的锯齿形层次遍历



阅读完本章节,你将有以下收获:



*   熟悉栈的数据结构,可以解决基本栈相关问题

*   掌握二叉树的概念

*   会用不同的方法去遍历二叉树,解决相关问题



[]( )最小栈、Shuffle an Array和将有序数组转换为二叉搜索树

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



[]( )最小栈

---------------------------------------------------------------



设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。



*   push(x) – 将元素 x 推入栈中。

*   pop() – 删除栈顶的元素。

*   top() – 获取栈顶元素。

*   getMin() – 检索栈中的最小元素。



**示例**



MinStack minStack = new MinStack();

minStack.push(-2);

minStack.push(0);

minStack.push(-3);

minStack.getMin(); --> 返回 -3.

minStack.pop();

minStack.top(); --> 返回 0.

minStack.getMin(); --> 返回 -2.




### []( )方法一 最小元素栈



**思路**



根据数组的性质,push、pop、top都能在常数时间内完成,而此题关键是常数时间内检索最小元素,此解法是开辟一个数组存储,push数据同时,存储当前栈中最小元素,pop数据的同时pop最小元素栈栈顶数据;



**详解**



1.  创建最小元素栈时,开辟 stack 及 minStack 数组,stack 用于存储压栈元素,minStack 用于存储最小元素序列;

2.  push操作执行元素 x 压栈:

3.  如果 minStack 数组为空时,添加元素 x 到 minStack 数组。

4.  否则比较元素 x 与数组末尾元素,取最小值添加到 minStack 数组末尾。表示当前栈中元素的最小值为x。

5.  pop操作:

6.  删除 stack 数组末尾元素的同时,删除数组 minStack 末尾元素。

7.  getMin操作:

8.  直接调用 pop 方法,返回 minStack 数据中末尾元素即可。



const MinStack = function () {

this.stack = [];

this.minStack = [];

};

/**

  • @param {number} x

  • @return {void}

*/

MinStack.prototype.push = function (x) {

this.stack.push(x);

if (this.minStack.length === 0) {

this.minStack.push(x);

} else {

const min = Math.min(this.minStack[this.minStack.length - 1], x);

this.minStack.push(min);

}

};

/**

  • @return {void}

*/

MinStack.prototype.pop = function () {

this.minStack.pop();

return this.stack.pop();

};

/**

  • @return {number}

*/

MinStack.prototype.top = function () {

return this.stack[this.stack.length - 1];

};

/**

  • @return {number}

*/

MinStack.prototype.getMin = function () {

return this.minStack[this.minStack.length - 1];

};




**复杂度分析**



*   时间复杂度:O(1)O(1)

    

    push、pop、top、getMin 操作都是基于数组索引,耗费 O(1) 的时间。

    

*   空间复杂度:O(n)O(n)

    

    每次 push 一个元素到 stack 栈的同时将 stack 栈中最小元素 push 到 minStack 栈,耗费O(n)O(n)的栈空间

    



### []( )方法二 差值存储



**思路**



用一个`min`变量保存最小值,每次push操作压栈时,保存的是入栈的值和最小值min的差值,而不是入栈的值;pop出栈时,通过`min`值和栈顶的值得到;不过此算法有一个缺陷,两数差值有溢出风险。



**详解**



1.  创建最小元素栈时,开辟 stack 数组,用于存储入栈元素x与最小元素 min 的差值;同时定义变量 min,用于存储最小值,初始值为 `Number.MAX_VALUE`。

2.  push 操作执行元素x入栈::

3.  取元素 x 与最小元素 min 的差值,压入stack数组:

4.  比较元素x与min值,取其最小值赋值给min变量。

5.  pop操作:

6.  弹出stack数组末尾元素值 value,如果值value > 0,说明push操作的值大于 min,返回 value + min 值;如果值value <= 0,说明 push 操作的值小于等于原 min 值,恢复最小值min,同时返回 min 值即可。

7.  top 操作:

8.  取stack数组末尾元素值 value,如果值 value > 0,说明push操作的值大于min,返回 value + min 值;如果值 value <= 0,说明 push 操作的值小于等于原 min 值,返回 min 值即可。

9.  getMin 操作:

10.  返回 min 元素即可。



const MinStack = function () {

this.stack = [];

this.min = Number.MAX_VALUE;

};

/**

  • @param {number} x

  • @return {void}

*/

MinStack.prototype.push = function (x) {

const min = this.min;

this.stack.push(x - min);

if (x < min) {

this.min = x;

}

};

/**

  • @return {void}

*/

MinStack.prototype.pop = function () {

const value = this.stack.pop();

const min = this.min;

if (value > 0) {

return value + min;

} else {

this.min = min - value;

return min;

}

};

/**

  • @return {number}

*/

MinStack.prototype.top = function () {

const value = this.stack[this.stack.length - 1];

if (value > 0) {

return value + this.min;

} else {

return this.min;

}

};

/**

  • @return {number}

*/

MinStack.prototype.getMin = function () {

return this.min;

};




**复杂度分析**



*   时间复杂度:O(1)O(1)

    

*   空间复杂度:O(1)O(1)

    

    每次 push 一个元素到 stack 栈的同时只需要一个元素空间存储最小值,耗费O(1)O(1)的栈空间

    



[]( )Shuffle an Array

----------------------------------------------------------------------------



打乱一个没有重复元素的数组。



**示例**



// 以数字集合 1, 2 和 3 初始化数组。

int[] nums = {1,2,3};

Solution solution = new Solution(nums);

// 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。

solution.shuffle();

// 重设数组到它的初始状态[1,2,3]。

solution.reset();

// 随机返回数组[1,2,3]打乱后的结果。

solution.shuffle();




### []( )方法一



**思路**



*   `reset` 函数:缓存传入的原始数据,用于在函数调用时返回。但值得一提的是,进行缓存原始数据时,必须进行浅拷贝,因为原始数据为数组,普通的赋值会导致引用对象传递,一旦变更了 this.nums 的数组内容,缓存的数组也将同步变更

*   shuffle\` 函数:我们模拟一个这样的场景,有 n 个标着数的球,我们把这 n 个球放入一个看不见的袋子中,每次从中摸一个球出来,并按照摸出的顺序,直到摸空袋子。具体的操作,我们把原始数组复制一份为 nums,每次根据 nums 的长度随机生成一个下标从 nums 中取一个数出来,将其放入新数组 ary 中,并删除 nums 中对应下标的数



**详解**



1.  定义 `this.nums` 存储传入的数据

2.  定义 `this.original` 存储 `nums` 的克隆数组

3.  定义重置 `reset` 方法,将 `this.nums` 重制为 `this.original` 的克隆数组,并将 `this.original` 重新克隆一遍(因数组为引用对象,不重新缓存新数组会导致 `this.original` 和 `this.nums` 同步变化)

4.  定义打乱 `shuffle` 方法,根据 `this.nums` 的长度进行循环,每次从根据 `this.nums` 长度通过 `Math.random()` 随机生成一个下标

5.  根据随机生成的下标,将值存入 `ary` 数组中

6.  最后返回 `ary` 数组



**代码**



/**

  • @param {number[]} nums

*/

const Solution = (nums) => {

this.nums = nums;

this.original = nums.slice(0);

};

/**

  • 重置数组并返回

  • @return {number[]}

*/

最后

推荐一些系统学习的途径和方法。

路线图

每个Web开发人员必备,很权威很齐全的Web开发文档。作为学习辞典使用,可以查询到每个概念、方法、属性的详细解释,注意使用英文关键字搜索。里面的一些 HTML,CSS,HTTP 技术教程也相当不错。

HTML 和 CSS:

html5知识

css基础知识

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

*/

const Solution = (nums) => {

this.nums = nums;

this.original = nums.slice(0);

};

/**

  • 重置数组并返回

  • @return {number[]}

*/

最后

推荐一些系统学习的途径和方法。

路线图

每个Web开发人员必备,很权威很齐全的Web开发文档。作为学习辞典使用,可以查询到每个概念、方法、属性的详细解释,注意使用英文关键字搜索。里面的一些 HTML,CSS,HTTP 技术教程也相当不错。

HTML 和 CSS:

html5知识

css基础知识

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-HF9CYqRK-1713268842779)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值