常见编程题解析
list数据转成tree结构
实现 convert 方法,把原始 list 转换成树形结构,要求尽可能降低时间复杂度
以下数据结构中,id 代表部门编号,name 是部门名称,parentId 是父部门编号,为 0 代表一级部门,现在要求实现一个 convert 方法,把原始 list 转换成树形结构,parentId 为多少就挂载在该 id 的属性 children 数组下
// 原始 list 如下
let list = [
{ id: 1, name: '部门A', parentId: 0 },
{ id: 2, name: '部门B', parentId: 0 },
{ id: 3, name: '部门C', parentId: 1 },
{ id: 4, name: '部门D', parentId: 1 },
{ id: 5, name: '部门E', parentId: 2 },
{ id: 6, name: '部门F', parentId: 3 },
{ id: 7, name: '部门G', parentId: 2 },
{ id: 8, name: '部门H', parentId: 4 },
];
// 转换后的结果如下
let needResult = [
{
id: 1,
name: '部门A',
parentId: 0,
children: [
{
id: 3,
name: '部门C',
parentId: 1,
children: [
{
id: 6,
name: '部门F',
parentId: 3,
},
{
id: 16,
name: '部门L',
parentId: 3,
},
],
},
{
id: 4,
name: '部门D',
parentId: 1,
children: [
{
id: 8,
name: '部门H',
parentId: 4,
},
],
},
],
},
{
// id 2 的
},
// ···
];
最优解,时间复杂度O(n):
function convert(list) {
const res = []
const map = list.reduce((res, v) => (res[v.id] = v, res), {})
for (const item of list) {
if (item.parentId === 0) {
res.push(item)
continue
}
if (item.parentId in map) {
const parent = map[item.parentId]
parent.children = parent.children || []
parent.children.push(item)
}
}
return res
}
递归:
function convert(list) {
let arr = list.filter(obj => obj.parentId === 0);
handleChildren(arr, list);
return arr;
}
function handleChildren(arr, list) {
arr.forEach(obj => {
let childArr = list.filter(childObj => childObj.parentId === obj.id);
if (childArr.length) {
obj.children = childArr;
handleChildren(childArr, list);
}
})
}
实现一个new函数
function new (fn, ...args) {
const obj = {};
obj._proto_ = fn.prototype; // obj = Object.create(fn.prototype)
const res = fn.apply(obj, args);
return (typeof res === 'object' && res !== null) ? res : obj;
}
无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1: 输入: s = “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2: 输入: s = “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3: 输入: s = “pwwkew” 输出: 3 解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,”pwke” 是一个子序列,不是子串。
最优解,时间复杂度O(n):
var lengthOfLongestSubstring = function (str) {
const len = str.length;
if (!len) {
return 0;
}
let left = 0;
let right = 1;
let max = 1;
while (right < len) {
const temp = str.slice(left, right);
if (temp.includes(str.charAt(right))) {
left++;
} else {
max = Math.max(max, right - left + 1);
right++;
}
}
return max;
};
梯度计费
请用尽可能少的代码实现一个函数,用于计算用户一个月共计交费多少港元。(代码请写的尽量清晰简洁,我们希望能够看到你的编码风格和习惯) 用户在的平台上进行交易,需要交平台使用费。平台使用费的梯度收费方案如下: 每月累计订单数 每笔订单(港元) 梯度1:1-5笔 => 30.00 梯度2:6-20笔 => 15.00 梯度3:21-50笔 => 10.00 梯度4:51笔及以上 => 1.00 假设一个用户,一个月交易了6笔订单,则在梯度1交费共计: 30港元*5=150港元,在梯度二交费:15港元,一共交费165港元。
const level = [
{ min: 1, max: 5, fee: 30 },
{ min: 6, max: 20, fee: 15 },
{ min: 21, max: 50, fee: 10 },
{ min: 51, max: 0, fee: 1 },
]
function calcAmount(orderCount) {
let amount = 0;
level.forEach(({ min, max, fee }) => {
if (orderCount >= min) {
if (orderCount <= max || max === 0) {
amount += (orderCount - min + 1) * fee;
} else {
amount += (max - min + 1) * fee;
}
}
})
return amount;
}
实现jsonp
使用方法:
jsonp({
url: 'url',
data: {
key1: 'value1'
},
callback (data) {
// data 是服务端返回的数据
}
})
常规实现
function objectToQuery(obj) {
const arr = [];
for ( var i in obj) {
arr.push(encodeURIComponent(i)+ '=' +encodeURIComponent(obj[i]));
}
return arr.join('&');
}
function jsonp ({url, data, callback}) {
const container = document.getElementsByTagName('head')[0];
const fnName = `jsonp_${new Date().getTime()}`;
const script = document.createElement('script');
script.src = `${url}?${objectToQuery(data)}&callback=${fnName}`;
script.type = 'text/javascript';
container.appendChild(script);
window[fnName] = function (res) {
callback && callback(res);
// 很多候选人漏掉clean这块
container.removeChild(script);
delete window[fnName];
}
script.onerror = function() { // 异常处理,也是很多人漏掉的部分
window[fnName] = function() {
callback && callback(
'something error hanppend!'
)
container.removeChild(script);
delete window[fnName];
}
}
}
Promise实现
function jsonp ({url, data, callback}) {
const container = document.getElementsByTagName('head')[0];
const fnName = `jsonp_${new Date().getTime()}`;
const script = document.createElement('script');
script.src = `${url}?${objectToQuery(data)}&callback=${fnName}`;
script.type = 'text/javascript';
container.appendChild(script);
return new Promise((resolve, reject) => {
window[fnName] = function (res) {
// 很多候选人漏掉clean这块
container.removeChild(script);
delete window[fnName];
resolve(res);
}
script.onerror = function() { // 异常处理,也是很多人漏掉的部分
container.removeChild(script);
delete window[fnName];
reject('something error hanppend!');
}
}
})
}
服务端
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
// 如果jsonp 的请求为GET
if ( ctx.method === 'GET' && ctx.url.split('?')[0] === '/getData.jsonp') {
// 获取jsonp的callback
let callbackName = ctx.query.callback || 'callback'
let returnData = {
success: true,
data: {
text: 'this is a jsonp api',
time: new Date().getTime(),
}
}
// jsonp的script字符串
let jsonpStr = `;${callbackName}(${JSON.stringify(returnData)})`
// 用text/javascript,让请求支持跨域获取
ctx.type = 'text/javascript'
// 输出jsonp字符串
ctx.body = jsonpStr
} else {
ctx.body = 'hello jsonp'
}
})
app.listen(3000, () => {
console.log('[demo] jsonp is starting at port 3000')
})
HardMan
实现HardMan函数:
HardMan('lilei')
//> i'm lilei
HardMan('lilei').rest(10).learn('chinese')
//> i'm lilei
// 等待10秒
//> start learning after 10s
//> learning chinese
HardMan('lilei').restFirst(5).learn('chinese')
// 等待5s
//> start learning after 5s
//> i'm lilei
//> learning chinese
代码:
function HardMan(name) {
const task = []; // 任务容器
// 这边执行next() 相当于调用task数组中的第一个函数,并删除它
function next() {
// 任务执行器
if (task.length) {
task.shift()(); // shift 用于把数组的第一个元素从其中删除,并返回第一个元素的值
}
}
// 延时器
function sleep(time) {
return function () {
setTimeout(function () {
console.log(`start learning after ${time}s`);
next(); // 执行完执行下一个
}, time * 1000);
};
}
// 提供外部方法
const fn = function () {};
fn.prototype.rest = function (time) {
task.push(sleep(time)); // 任务中加入sleep返回的函数
return this; // 返回当前this 用于链式调用
};
fn.prototype.restFirst = function (time) {
task.unshift(sleep(time)); // unshift() 方法可向数组的开头添加一个或更多元素 。 这边是加入到数组前面最先调用 。
return this;
};
fn.prototype.learn = function (sub) {
task.push(function () {
console.log(`learn ${sub}`);
next(); // 执行完执行下一个
});
return this;
};
task.push(function () {
console.log(`i"m ${name}`);
next();
});
// 第一个
setTimeout(next, 0);
return new fn(); // 返回 实例对象 可调用它的内部方法 例:HardMan (name).rest(time)
}
有效的括号
给定一个只包括 ‘(‘,’)’,’{‘,’}’,’[‘,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1、左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。
3、每个右括号都有一个对应的相同类型的左括号。
输入:s = "()"
输出:true
输入:s = "()[]{}"
输出:true
输入:s = "(]"
输出:false
代码:
var isValid = function(s) {
const n = s.length;
if (n % 2 === 1) {
return false;
}
const pairs = new Map([
[')', '('],
[']', '['],
['}', '{']
]);
const stk = [];
for (let ch of s){
if (pairs.has(ch)) {
if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
return false;
}
stk.pop();
}
else {
stk.push(ch);
}
};
return !stk.length;
};
长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的
子数组
[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
代码:
function minSubArrayLen(s, nums) {
if (nums.length === 0) {
return 0;
}
let left = 0;
let right = 0;
let sum = 0;
let minLength = Infinity;
while (right < nums.length) {
sum += nums[right];
while (sum >= s) {
minLength = Math.min(minLength, right - left + 1);
sum -= nums[left];
left++;
}
right++;
}
return minLength === Infinity ? 0 : minLength;
}