React
-
介绍一下react
-
React单项数据流
-
react生命周期函数和react组件的生命周期
-
react和Vue的原理,区别,亮点,作用
-
reactJs的组件交流
-
有了解过react的虚拟DOM吗,虚拟DOM是怎么对比的呢
-
项目里用到了react,为什么要选择react,react有哪些好处
-
怎么获取真正的dom
-
选择react的原因
-
react的生命周期函数
-
setState之后的流程
-
react高阶组件知道吗?
-
React的jsx,函数式编程
-
react的组件是通过什么去判断是否刷新的
-
如何配置React-Router
-
路由的动态加载模块
-
Redux中间件是什么东西,接受几个参数
-
redux请求中间件如何处理并发
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
一个炒鸡简单的二叉搜索树就写好了,写的简单是为了方便写二叉搜索树的遍历。二叉树的遍历方式有**「深度优先遍历和广度优先遍历」两种思路。深度优先遍历又分为「先序遍历、中序遍历、后序遍历」三种方式;广度优先遍历主要是「层次遍历」**。
先序遍历顺序:根、左、右
中序遍历顺序:左、根、右
后序遍历顺序:左、右、根
二叉树的遍历
如图所示的二叉树的遍历结果分别为:
先序遍历结果:F B A D C E G I H
中序遍历结果:A B C D E F G H I
后序遍历结果:A C E D B H I G F
层次遍历结果:F B G A D I C E H
二叉树的遍历
递归遍历的代码非常简单:
// 先序遍历的顺序:根节点、左节点、右节点
// 先序遍历可以打印出树的结构
root = this.root
DLR(root) {
if(root) {
console.(root.value)
this.DLR(root.left)
this.DLR(root.right)
}
}
// 中序遍历顺序:左节点、根节点、右节点
// 二叉搜索树的中序遍历结果得到的就是有序的值
LDR(root) {
if(root) {
this.LDR(root.left)
console.log(root.value)
this.LDR(root.right)
}
}
// 后序遍历顺序:左节点、右节点、根节点
// 后序遍历可以用于从叶子节点向根节点访问
LRD(root) {
if(root) {
this.LRD(root.left)
this.LRD(root.right)
console.log(root.value)
}
}
非递归遍历比递归遍历要复杂,我们需要借助到栈这种数据结构。
栈的特点是先进后出,所以我们可以**「将后处理的节点先进栈,先处理的节点后进栈」**。
root = this.root
// 先序遍历的顺序是根左右,所以先入栈右节点,再入栈左节点
Dlr() {
if(!root) return null
let s = new Stack()
s.push(root)
while(s.getCount()) {
node = s.pop()
console.log(node.value)
if(node.right) {
s.push(node.right)
}
if(node.left) {
s.push(node.left)
}
}
}
// 中序遍历顺序是左根右,先将根节点和所有的左节点入栈
LDR() {
if(!node) return null
let s = new Stack()
s.push(root)
while(s.getCount()) {
if(root) {
s.push(root)
root = root.left
}
node = s.pop()
console.log(node.value)
// 如果出栈的节点存在右子树,将该子树的所有左节点入栈
root = node.right()
}
}
// 由于左右节点没有直接的访问方法,所以先入栈右子树,然后入栈左子树,将得到的结果数组翻转
Lrd(){
if(!root) return null
let s = new Stack()
let res = []
s.push(root)
while(s.getCount()){
node = s.pop()
res.push(node.value)
if(node.left) s.push(node.left)
if(node.right) s.push(node.right)
}
return res.reverse()
}
接下来就是层次遍历,层次遍历也需要借助到别的数据结构,这里使用队列实现。
BFS() {
if (!this.root) return null
let q = new Queue()
q.enqueue(this.root)
while (!q.isEmpty()) {
let n = q.deQueue()
console.log(n.value)
if (n.left) q.enQueue(n.left)
if (n.right) q.enQueue(n.right)
}
}
层次遍历的思路是**「将一个节点出栈的同时,将它的左节点和右节点入栈」**。队列先进先出的特点可以保证一层的节点遍历完再遍历下一层的节点。
二叉树的其他操作
「获取最大或最小值」。由于二叉搜索树的特性,小值在左子树,大值在右子树,所以我们只需要比较当前节点的值,就知道向左还是向右遍历。找最小值一直向左遍历,最大值一直向右遍历。
root = this.root
getMin() {
return this._getMin(root).value
}
_getMin(node) {
if(!node.left) return node
return this.getMin(node.left)
}
getMax() {
return this._getMax(root).value
}
_getMax(node) {
if(!node.left) return node.value
return this.getMax(node.right)
}
「第n大的值」。二叉搜索树由于只能从根节点往子节点遍历,所以我们没办法直接获取到节点的排名。所以我们需要对二叉树的定义改一下,为每一个节点添加一个size属性,直接标明该节点前有多少节点,为了方便计算,包含自身。
class Node{
constructor(value) {
this.value = value
this.left = null
this.right= null
this.size = 1
}
_getSize(node) {
return node ? node.size : 0
}
addChild(node, v) {
if (!node) return new Node(v)
// 节点下插入一个子节点时,该节点的size+1
if (node.value > v) {
node.size++
node.left = this.addChild(node.left, v)
} else if (node.value < v) {
node.size++
node.right = this.addChild(node.right, v)
}
return node
}
select(k) {
let node = this._select(this.root, k)
return node ? node.value : null
}
_select(node, k) {
if (!node) return null
// 先获取根节点的左子树的size,如果k小于size,就在左子树查找,如果k大于size就在右子树寻找
let size = node.left ? node.left.size : 0
if (size > k) return this._select(node.left, k)
// 在右子树的情况下,减去左子树和根节点,问题就变成在右子树中,第(k - size - 1)个节点
if (size < k) return this._select(node.right, k - size - 1)
return node
}
}
「删除节点」。在二叉树上删除一个节点是一个复杂的操作,首先要分多种情况考虑,其次删除节点后,还要保证二叉搜索树的特性。删除节点有三种情况:
-
没有子节点
-
有一条子树
-
有两条子树
第一种情况很好操作,只需要判断没有子节点就可以直接删除。
第二种情况也好操作,只需要将子树再接上去就可以。
前端框架
前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。
以 Vue 为例,我整理了如下的面试题。