编程题汇总
学习过程中的简单记录,若问题敬请指教!文章持续更新中...
路过的朋友,可以点个赞,关注一下~~~
1. 编程题-链表合并
/*
* 构造两个以整型数字为值的链表,其中的值是单调递增的。
* 将两个链表合并,保持递增。
* 要求空间复杂度O(1)
*/
function ListNode(x) {
this.val = x;
this.next = null;
}
// 要求空间复杂度O(1)
function Merge(pHead1, pHead2) {
// write code here
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
let cur = new ListNode(0);
let headNode = new ListNode(0);
if (pHead1.val <= pHead2.val) headNode = pHead1;
else headNode = pHead2;
while (pHead1 && pHead2) {
if (pHead1.val <= pHead2.val) {
cur.next = pHead1;
pHead1 = pHead1.next;
} else {
cur.next = pHead2;
pHead2 = pHead2.next;
}
cur = cur.next;
}
cur.next = pHead1 ? pHead1 : pHead2;
return headNode;
}
// 递归的空间复杂度为O(n) 循环几次复杂度为几次
function Merge(pHead1, pHead2) {
if (pHead1 === null) {
return pHead2;
}
if (pHead2 === null) {
console.log("pHead1:** ", pHead1);
return pHead1;
}
if (pHead1.val <= pHead2.val) {
pHead1.next = Merge(pHead1.next, pHead2);
return pHead1;
} else {
pHead2.next = Merge(pHead1, pHead2.next);
return pHead2;
}
}
测试
let pHead1 = {
val: 1,
next: {
val: 3,
next: null,
},
};
let pHead2 = {
val: 2,
next: {
val: 4,
next: null,
},
};
console.log(Merge(pHead1, pHead2));
2. 编程题-单链表每隔k个元素做一次反转
/**
* 单链表每隔k个元素做一次反转
*
* Example:
* Inputs: 1->2->3->4->5->6->7->8->NULL and k = 3
* Output: 3->2->1->6->5->4->8->7->NULL.
*
* Inputs: 1->2->3->4->5->6->7->8->NULL and k = 5
* Output: 5->4->3->2->1->8->7->6->NULL.
*
*/
// 反转
function reverse(a, b) {
let pre = null,
cur = a,
nxt = a;
while (cur != b) {
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
// 反转组
function reverseKGroup(head, k) {
let a = head,
b = head;
for (let i = 0; i < k; i++) {
if (b == null) return head; //不足k个剩下的不需要反转
b = b.next; //分组:每一组k个被反转的节点
}
let newHead = reverse(a, b); //调用反转函数
a.next = reverseKGroup(b, k); //将每组被反转的组,连接到一起
return newHead; //返回最后的链表
}
测试
let head = {
val: 1,
next: {
val: 2,
next: {
val: 3,
next: {
val: 4,
next: {
val: 5,
next: {
val: 6,
next: {
val: 7,
next: {
val: 8,
next: {
val: 9,
next: {
val: 10,
next: {
val: 11,
next: null,
},
},
},
},
},
},
},
},
},
},
};
let result = reverseKGroup(head, 3);
console.log("result: ", result);
3.编程题-扁平结构转嵌套结构
// 将扁平化 数组 转换树的方法
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
}
let childrenListMap = {}
let nodeIds = {}
let tree = []
// 获取所有id的一个map 和 parentId的相同的一个数组
for (let d of data) {
let parentId = d[config.parentId]
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = []
}
nodeIds[d[config.id]] = d
childrenListMap[parentId].push(d)
}
// 所有所有根节点
for (let d of data) {
let parentId = d[config.parentId]
if (nodeIds[parentId] == null) {
tree.push(d)
}
}
// 所有的根节点挂载下级
for (let t of tree) {
adaptToChildrenList(t)
}
// 适配子节点列表
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]]
}
// 存在子节点 循环子节点去挂载
if (o[children.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c)
}
}
}
}
使用示例
const data = [
{ name: '数据1', parentId: null, id: 1 },
{ name: '数据2', id: 2, parentId: 1 },
{ name: '数据3', parentId: 2, id: 3 },
{ name: '数据4', parentId: 3, id: 4 }
]
let treeData = handleTree(data)
4. 编程题-JS异步并发调度器
javaScript中控制并发数量
源码
想到了解法其实很简单,我的代码看一遍就能会。
class Scheduler {
constructor(max) {
this.max = max
this.count = 0
this.queue = []
}
add(p) {
this.queue.push(p)
this.start()
}
start() {
if (this.count >= this.max || !this.queue.length) return
this.count++
this.queue.shift()().finally(() => {
this.count--
this.start()
})
}
}
复制代码
示例
// 延迟函数
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
// 同时进行的任务最多2个
const scheduler = new Scheduler(2);
// 添加异步任务
// time: 任务执行的时间
// val: 参数
const addTask = (time, val) => {
scheduler.add(() => {
return sleep(time).then(() => console.log(val));
});
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 2
// 3
// 1
// 4
复制代码
5. 防抖
防抖主要是使用延时器来实现的,正常防抖可以分为两种分别是立即执行版本
和非立即执行版本
。此处主要是结合两种形式的一个组合版本。
- 立即执行版本:一般用在按钮点击上(比如提交表单),点击立即触发,单位时间内一直点击不会触发。当超过单位时间再次点击会再次触发。
- 非立即执行版本:一般用在搜索联想功能中,输入内容的时候不触发,输入完后再请求后端获取数据。
export function debounce(func, delay, immediate = true) {
let timer = null;
return function () {
// 上下文参数
const context = this;
const args = arguments;
// 清除延时器
if (timer) clearTimeout(timer);
if (immediate) {
// 立即执行版本
const canCall = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (canCall) func.canCall(context, ...args);
} else {
// 非立即执行版本
timer = setTimeout(() => {
func.call(context, ...args);
});
}
};
}
6. 设计节流
节流也是使用延时器来实现的
使用场景
常见的使用场景是在一些 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
// 实现节流
export function throttle(func, delay) {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (!timer) {
timer = setTimeout(() => {
timer = null;
func.call(context, ...args);
}, delay);
}
};
}
后记
本文纯仅属于个人的一些简单的见解,比较浅显,若有不妥之处还请不吝赐教!!!(☆_☆)
如果本文对你有所帮助,欢迎点赞!!!
o( ̄▽ ̄)d