如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?
第10天要刷的手写题如下:
-
数组转树
-
树转数组
-
使用js实现冒泡排序 下面是我自己写的答案:
1. 数组转树
-
给定如下数组:
let source = [{
id: 1,
pid: 0,
name: 'body',
}, {
id: 2,
pid: 1,
name: 'title',
}, {
id: 3,
pid: 2,
name: 'div',
}, {
id: 4,
pid: 0,
name: 'html',
}, {
id: 5,
pid: 4,
name: 'div',
}, {
id: 6,
pid: 5,
name: 'span',
}, {
id: 7,
pid: 5,
name: 'img',
}, ]
-
将其转成如下的树结构:
[{
id: 1,
pid: 0,
name: 'body',
children: [{
id: 2,
pid: 1,
name: 'title',
children: [{
id: 3,
pid: 2,
name: 'div'
}],
}, ],
}, {
id: 4,
pid: 0,
name: 'html',
children: [{
id: 5,
pid: 4,
name: 'div',
children: [{
id: 7,
pid: 5,
name: 'img'
}],
}, ],
}]
分析 策略 实现
-
循环遍历:很明显,对原来的数组进行一次遍历很难达到目的,原因在于,遍历到某个元素A的时候,不能保证其父元素已经被遍历过了,所以这实际上是一个循环遍历的问题
-
处理循环遍历问题的策略是:队列数据结构,从队列尾部弹出,经过检查函数,如果符合就弹出下一个,如果不符合就放到队列头部等待下次检查;直到队列中没有元素
- 具体来说:
-
创建一张哈希表tree,索引为各个元素的id值
- 循环遍历队列中的元素,对每一个元素做如下处理:
-
-
判断此元素是否在哈希表中,如果没有的话,添加进哈希表
-
-
-
判断此元素的父元素是否在哈希表中:
-
(1) 如果在的话就加入到父元素的children属性中去;
-
(2) 如果不再的话,说明子元素在父元素之前被遍历到了,这个时候需要将此元素移动至队列头部等待下次遍历检查
-
-
-
-
结果:最后返回的树是一颗完整的树,保存了所有的父子关系!
function convert(arr) {
let tree = {
0: {
id: 0,
pid: null,
children: [],
}
};
const _queue = [...arr];
while (_queue.length) {
const _cur = _queue.pop();
const {
pid,
id
} = _cur;
tree[id] = tree[id] ?? _cur;
const _p = tree[pid];
if (_p) {
if (_p.children) {
_p.children.push(_cur);
} else {
_p.children = [_cur];
}
} else {
_queue.unshift(_cur);
}
}
return tree[0].children;
}
console.log(convert(source));
2. 树转数组
-
给定如下的树结构:
[{
id: 1,
pid: 0,
name: 'body',
children: [{
id: 2,
pid: 1,
name: 'title',
children: [{
id: 3,
pid: 2,
name: 'div'
}],
}, ],
}, {
id: 4,
pid: 0,
name: 'html',
children: [{
id: 5,
pid: 4,
name: 'div',
children: [{
id: 7,
pid: 5,
name: 'img'
}],
}, ],
}]
-
将其转成如下数组:
let source = [{
id: 1,
pid: 0,
name: 'body',
}, {
id: 2,
pid: 1,
name: 'title',
}, {
id: 3,
pid: 2,
name: 'div',
}, {
id: 4,
pid: 0,
name: 'html',
}, {
id: 5,
pid: 4,
name: 'div',
}, {
id: 6,
pid: 5,
name: 'span',
}, {
id: 7,
pid: 5,
name: 'img',
}, ]
分析 策略 实现
-
这个是一个深度优先遍历问题,直接使用递归就可以实现了
-
遍历每一个元素,如果这个元素没有children属性就直接加入到结果数组中去;如果有children属性,就对children递归,将递归结果加入到结果数组中去,然后删除此元素的children属性,并加入到结果数组中去。
function tree2arr (tree) {
const rst = [];
tree.forEach(
item => {
const {children} = item;
if(children){
rst.push(...tree2arr(children));
delete item['children'];
}
rst.push(item);
}
)
return rst;
}
console.log(tree2arr(source2).sort(function(a,b){return a.id-b.id}))
3. 使用js实现冒泡排序
考这个主要是考细节,就是考两层循环的起始位置和结束位置
-
原理:遍历元素,如果前面的比后面的大,就交换二者顺序
-
外层遍历起始位置为0,结束位置为length;内层循环以外层循环因子作为起始值,结束位置也为length
function bubbleSort (source) {
const rst = [...source];
const len = rst.length;
const swap = (i,j) => {
[rst[i], rst[j]] = [rst[j], rst[i]];
}
for (let i = 0; i < len; i++) {
for (let j = i; j < len; j++) {
if(rst[i] > rst[j]){
swap(i,j);
}
}
}
return rst;
}
上面的程序改一个字就可以编程倒序排序,你知道在哪里吗?