FrankKai / FrankKai.github.io

FE blog

Home Page:https://frankkai.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

一些特别棒的面试题[4]

FrankKai opened this issue · comments

最近面试了一些公司,拿了一些offer,不记录概念题目,仅记录coding类题目。
小伙伴们空闲时间可以做这些题目练练手。​

  • 只出现一次的数字
  • 汇总区间
  • 实现红绿灯效果
  • 数组去重
  • 返回 excel 表格列名
  • 检测空对象
  • 实现a+a+a打印'abc'
  • 实现一个Event模块
  • 大整数相加
  • SuperPerson继承Person
  • 字符串隐藏部分内容
  • 实现一个sum(1,2,3)(4)(5)(6,7)(8)()
  • 实现一个sum(1,2,3)(4)(5)(6,7)(8)()升级版:如何实现加,减,乘,除呢?

只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
答案
/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function (nums) {
  /** 解法1:暴力遍历
   *  性能:704ms 40.5MB
   */
  let numsSet = Array.from(new Set(nums));
  let numsMap = numsSet.map((num) => ({
    num,
    count: 0,
  }));
  nums.forEach((num, i) => {
    numsMap.forEach((numM, j) => {
      if (numM.num === num) {
        numM.count++;
      }
    });
  });
  let filterArr = numsMap.filter((num) => num.count === 1);
  return filterArr[0].num;
  /** 解法2:Set 首次出现add 二次出现delete
   *  性能: 72 ms 38MB
   */
  let numsSet = new Set();
  for (let i = 0; i < nums.length; i++) {
    if (!numsSet.has(nums[i])) {
      numsSet.add(nums[i]);
    } else {
      numsSet.delete(nums[i]);
    }
  }
  return [...numsSet][0];
};

这是一道leetcode 简单难度的题。
题目:leetcode 136 只出现一次的数字
题解:136 只出现一次的数字

汇总区间

给定一个乱序整形数组[0,1,7,13,15,16,2,4,5],找出其中连续出现的数字区间为如下:["0->2", "4->5", "7", "13", "15->16"]
答案
function continuous(arr) {
 arr.sort((a, b) => a - b);
 let stack = [];
 let result = [];
 for (let i = 0; i < arr.length; i++) {
   if (stack.length === 0 || arr[i] - stack[stack.length - 1] === 1) {
     stack.push(arr[i]);
   } else {
     if (stack.length > 1) {
       result.push(`${stack[0]}->${stack[stack.length - 1]}`);
     } else {
       result.push(`${stack[0]}`);
     }

     stack = [];
     stack.push(arr[i]);
   }
   if (i === arr.length - 1) {
     if (stack.length > 1) {
       result.push(`${stack[0]}->${stack[stack.length - 1]}`);
     } else {
       result.push(`${stack[0]}`);
     }
   }
 }
 return result;
}
console.log(continuous([0, 1, 7, 13, 15, 16, 2, 4, 5]));

这是一道leetcode 中等难度的题。
题目:leetcode 228 汇总区间
题解:228汇总区间(Summary Ranges)

实现红绿灯效果

实现红绿灯效果,使用console 输出 “红”、“绿”、“黄”示意,等待时间分别为 3s、2s、1s
答案
function trafficCtrl() {
  // timeline 红0~2 绿3~4 黄5
  const borders = { red: 3, green: 5, yellow: 6 };
  let current = 0;
  setInterval(() => {
    if (current >= 0 && current <= 2) {
      console.log('红', borders.red - current);
    } else if (current >= 3 && current <= 4) {
      console.log('绿', borders.green - current);
    } else {
      console.log('黄', borders.yellow - current);
    }
    current++;
    if (current > 5) {
      current = 0;
    }
  }, 1000);
}

trafficCtrl();

红 3
红 2
红 1
绿 2
绿 1
黄 1
红 3
红 2

数组去重

输入:
['1', '2', '3', 1, '2', undefined, undefined, null, null, 1, 'a','b','b'];
输出:
["1", "2", "3", 1, undefined, null, "a", "b"]
答案
// 解法1:includes
function removeDuplicate(arr) {
    const result = [];
    for(const item of arr){
       if(!result.includes(item)) result.push(item);
    }
    return result;
}
// 解法2:Map
function removeDuplicate(arr) {
     const map = new Map();
    for(const item of arr){
       if(!map.has(item)) map.set(item, true);
      }
     const result = [...map.keys()];
    return result;
}
// 解法3:对撞指针
function removeDuplicate(arr) {
     const map = new Map();
     let i = 0;
     let j = arr.length - 1;
     while(i<=j){
  	      if(!map.has(arr[i])) map.set(arr[i], true);
  	      if(!map.has(arr[j])) map.set(arr[j], true);
  	      i++;
  	      j--;
     }
     const result = [...map.keys()];
          return result;
}
// 解法4:filter
function removeDuplicate(arr) {
    return arr.filter((item, i)=> arr.indexOf(item) === i)
}

返回 excel 表格列名

输入:1 输出:A
输入:2 输出:B
输入:26 输出:Z
输入:27 输出:AA
输入:52 输出:AZ
答案
function getExcelColumn(column) {
    const obj = {};
    let i = 0;
    const startCode = "A".charCodeAt();
    while (i < 26) {
        obj[i + 1] = String.fromCharCode(startCode + i);
        i++;
    }
    if (column <= 26) {
        return obj[column]
    }
    const stack = [];
    const left = column % 26;
    const floor = Math.floor(column / 26);

    if (left) {
        stack.unshift(obj[left])
        stack.unshift(obj[floor]);
    } else {
        stack.unshift('Z')
        stack.unshift(obj[floor - 1]);
    }
    const result = stack.join("");
    return result;
}

这是一道leetcode 简单难度的题。
题目:leetcode 168 Excel表列名称
题解:168 Excel表列名称

如何检测一个空对象

如何检测出{}这样的空对象
答案
// 解法1: Object.prototype.toString.call和JSON.stringify
function isObjEmpty(obj){
    return Object.prototype.toString.call(obj)==="[object Object]" && JSON.stringify({}) === "{}";
}
// 解法2: Object.keys() Object.values()
function isObjEmpty(obj){
    return Object.keys(obj).length === 0 || Object.values(obj).length === 0;
}
// 解法3:for...in
function isObjEmpty(obj){
    for(key in obj){
        if(key) return false
    }
    return true;
}

实现a+a+a打印'abc'

console.log(a + a + a); // 打印'abc'
答案
/*
  console.log(a + a + a); // 打印'abc'
*/

/**
 * 解法1: Object.defineProperty() 外部变量
 */
let value = "a";
Object.defineProperty(this, "a", {
  get() {
    let result = value;
    if (value === "a") {
      value = "b";
    } else if (value === "b") {
      value = "c";
    }
    return result;
  },
});
console.log(a + a + a);
/**
 * 解法1(优化版):Object.defineProperty() 内部变量
 */
Object.defineProperty(this, "a", {
  get() {
    this._v = this._v || "a";
    if (this._v === "a") {
      this._v = "b";
      return "a";
    } else if (this._v === "b") {
      this._v = "c";
      return "b";
    } else {
      return this._v;
    }
  },
});
console.log(a + a + a);

/**
 * 解法2: Object.prototpye.valueOf()
 */
let index = 0;
let a = {
  value: "a",
  valueOf() {
    return ["a", "b", "c"][index++];
  },
};
console.log(a + a + a);

/**
 * 解法3:charCodeAt,charFromCode
 */
let code = "a".charCodeAt(0);
let count = 0;
Object.defineProperty(this, "a", {
  get() {
    let char = String.fromCharCode(code + count);
    count++;
    return char;
  },
});
console.log(a + a + a); // 'abc'

/**
 * 解法3(优化版一):内部变量this._count和_code
 */
Object.defineProperty(this, "a", {
  get() {
    let _code = "a".charCodeAt(0);
    this._count = this._count || 0;
    let char = String.fromCharCode(_code + this._count);
    this._count++;
    return char;
  },
});
console.log(a + a + a); // 'abc'

/**
 * 解法3(优化版二):内部变量this._code
 */
Object.defineProperty(this, "a", {
  get() {
    this._code = this._code || "a".charCodeAt(0);
    let char = String.fromCharCode(this._code);
    this._code++;
    return char;
  },
});
console.log(a + a + a); // 'abc'

/*
 题目扩展: 打印`a...z`
 a+a+a; //'abc'
 a+a+a+a; //'abcd'
*/
/**
 * charCodeAt,charFromCode
 */
let code = "a".charCodeAt(0);
let count = 0;
Object.defineProperty(this, "a", {
  get() {
    let char = String.fromCharCode(code + count);
    if (count >= 26) {
      return "";
    }
    count++;
    return char;
  },
});
// 打印‘abc’
console.log(a + a + a); // 'abc'

// 打印‘abcd’
let code = "a".charCodeAt(0);
let count = 0;
// {...定义a...}
console.log(a + a + a); // 'abcd'

// 打印‘abcdefghijklmnopqrstuvwxyz’
let code = "a".charCodeAt(0);
let count = 0;
// {...定义a...}
let str = "";
for (let i = 0; i < 27; i++) {
  str += a;
}
console.log(str); // "abcdefghijklmnopqrstuvwxyz"

/*
 题目扩展(优化版): 打印`a...z`
 a+a+a; //'abc'
 a+a+a+a; //'abcd'
*/

Object.defineProperty(this, "a", {
  get() {
    this._code = this._code || "a".charCodeAt(0);
    let char = String.fromCharCode(this._code);
    if (this._code >= "a".charCodeAt(0) + 26) {
      return "";
    }
    this._code++;
    return char;
  },
});
// 打印‘abc’
console.log(a + a + a); // 'abc'

实现一个Event模块

简单实现一个事件订阅机制,具有on、emit、once、off
on(event, func){ ... }
emit(event, ...args){ ... }
once(event, func){ ... }
off(event, func){ ... }

const event = new EventEmitter();
event.on('someEvent', (...args) => {
     console.log('some_event triggered', ...args);
});
event.emit('someEvent', 'abc', '123');
event.once('someEvent', (...args) => {
     console.log('some_event triggered', ...args);
});
event.off('someEvent', callbackPointer);
答案
class EventEmitter {
  constructor() {
    this.map = new Map()
  }
  on(event, func) {
    const e = { name: event, handler: { type: 'persistent', func } }
    const { handlers } = this.map.get(e.name) || {}
    this.map.set(e.name, this.map.has(e.name) ? { handlers: [...handlers, e.handler] } : { handlers: [e.handler] })
  }
  emit(event, ...args) {
    const e = { name: event }
    const { handlers } = this.map.get(e.name) || {}
    if (!handlers) return
    for (const handler of handlers) {
      handler.func(...args)
    }
    // 过滤
    this.map.set(e.name, { handlers: handlers.filter((handler) => handler.type !== 'once') })
    // console.log('emit', this.map)
  }
  once(event, func) {
    const e = { name: event, handler: { func, type: 'once' } }
    const { handlers } = this.map.get(e.name) || {}
    this.map.set(e.name, this.map.has(e.name) ? { handlers: [...handlers, e.handler] } : { handlers: [e.handler] })
    // console.log('once', this.map)
  }
  off(event, func) {
    const e = { name: event, handler: { func } }
    const { handlers } = this.map.get(e.name) || {}
    if (!handlers) return
    const leftHandlers = handlers.filter((handler) => handler.func !== func)
    this.map.set(e.name, { handlers: leftHandlers })
    // console.log('off', this.map)
  }
}

const event = new EventEmitter()
const callbackPointer = (...args) => {
  console.log('some_event triggered', ...args)
}
const callbackPointer1 = (...args) => {
  console.log('some_event triggered 1', ...args)
}
event.on('someEvent', callbackPointer)
event.on('someEvent', callbackPointer1)

event.emit('someEvent', 'abc', '123')
event.off('someEvent', callbackPointer)
event.emit('someEvent', 'abc', '123')

const callbackPointer2 = (...args) => {
  console.log('some_event triggered 2', ...args)
}
const callbackPointer3 = (...args) => {
  console.log('some_event triggered 3', ...args)
}

event.once('someEvent2', callbackPointer2)
event.once('someEvent2', callbackPointer3)
event.emit('someEvent2', 'abc', '123')
event.emit('someEvent2', 'abc', '123')

const callbackPointer4 = (...args) => {
  console.log('some_event triggered 4', ...args)
}
const callbackPointer5 = (...args) => {
  console.log('some_event triggered 5', ...args)
}

event.on('someEvent3', callbackPointer4)
event.once('someEvent3', callbackPointer5)
event.emit('someEvent3', 'abc', '123')
event.emit('someEvent3', 'abc', '123')

大整数相加

请通过代码实现大整数(可能比Number.MAX_VALUE大)相加运算
var bigint1 = new BigInt('1231230');
var bigint2 = new BigInt('12323123999999999999999999999999999999999999999999999991');
console.log(bigint1.plus(bigint2))
答案
function BigInt(value) {
  this.value = value;
}

BigInt.prototype.plus = function (bigint) {
  let aArr = this.value.split("");
  let bArr = bigint.value.split("");
  let stack = [];
  let count = 0;
  while (aArr.length !== 0 || bArr.length !== 0) {
    let aPop = aArr.pop() || 0;
    let bPop = bArr.pop() || 0;
    let stackBottom = 0;
    if (stack.length > count) {
      stackBottom = stack.shift();
    }
    let sum = parseInt(aPop) + parseInt(bPop) + parseInt(stackBottom);
    if (sum < 10) {
      stack.unshift(sum);
    } else if (sum >= 10) {
      stack.unshift(sum - 10);
      stack.unshift(1);
    }
    count++;
  }
  return stack.join("");
};

SuperPerson继承Person

写一个类Person,拥有属性age和name,拥有方法say(something)
再写一个类Superman,继承Person,拥有自己的属性power,拥有自己的方法fly(height) ES5方式
答案
function Person(age, name){
	this.age = age;
        this.name = name;
}
Person.prototype.say = function(something) {
    // ...
}

function Superman(age, name, power){
        Person.call(this, age, name, power);
	this.power = power;
}
Superman.prototype = Object.create(Person.prototype);
Superman.prototype.constructor = Superman;

Superman.prototype.fly = function(height) {
    // ...
}

let superman = new Superman(25, 'GaoKai', 'strong');

// class方式
class Person {
  constructor(age, name){
    this.age = age;
    this.name = name;
  }
  say(something){
  	// ...
  	console.log("say");
  }
}
class Superman extends Person{
  constructor(age, name, power){
	super(age, name)
	this.power = power;
  }
  fly(height){
    // ...
    console.log("fly");
  }
}

let superman = new Superman(25, 'GaoKai', 'strong');

字符串隐藏部分内容

字符串隐藏部分内容
说明:实现一个方法,接收一个字符串和一个符号,将字符串中间四位按指定符号隐藏
1. 符号无指定时使用星号(*
2. 接收的字符串小于或等于四位时,返回同样长度的符号串,等同于全隐藏,如 123,隐藏后是 ***
3. 字符串长度是大于四位的奇数时,如 123456789,隐藏后是 12****789,奇数多出来的一位在末尾
示例:
mask('blibaba', '#');  // b####ba
mask('05716666');   // 05****66
mask('hello');  // ****o
mask('abc', '?');  // ???
mask('哔里巴巴集团', '?'); // 哔????团
答案
function mask(str, char = "*") {
  if(str.length<=4) return char.repeat(str.length);
  /* 代码实现 */
  let result = "";
  let i = Math.floor(str.length / 2) - 1;
  let j = Math.floor(str.length / 2);
  while(result.length!==str.length){
    if(j - i <= 4){
      	result = char + result;
    	result += char ;
    } else {
    	result = (str[i] || "") + result;
    	result += str[j] ;
    }
    i--;
    j++;
  }
  return result;
}

实现一个sum(1,2,3)(4)(5)(6,7)(8)()

返回结果为这些数字的和:36。

这是一道考察求和+闭包+递归的题目。

答案
function sum(){
    const result = [...arguments].reduce((acc, cur)=>acc+cur)
    return function(){
            if(arguments.length === 0)return result
            return sum(...[...arguments, result]);
    }
}

实现一个sum(1,2,3)(4)(5)(6,7)(8)()升级版:如何实现加,减,乘,除呢?

sum(1,2,3)(4)(5)(6,7)(8)()
minus(1,2,3)(4)(5)(6,7)(8)()
multiple(1,2,3)(4)(5)(6,7)(8)()
divide(1,2,3)(4)(5)(6,7)(8)()

除了考察求和,闭包,递归以外,还考察了柯里化函数

sum和multiple不用关注顺序。
而minus和divide需要注意顺序,因此在return curried(...[result, ...arguments]);中将result前置了。

答案
function curry(callback){
    return function curried (){
        const result = callback(arguments)
        return function(){
                if(arguments.length === 0)return result
                return curried(...[result, ...arguments]);
        }
    }
}


let sum = (args) =>{
    return [...args].reduce((acc, cur)=>acc+cur)
}

let minus = (args) =>{
    return [...args].reduce((acc, cur)=>acc-cur)
}


let multiple = (args) =>{
    return [...args].reduce((acc, cur)=>acc*cur)
}

let divide = (args) =>{
    return [...args].reduce((acc, cur)=>acc / cur)
}


let currySum = curry(sum)
let curryMultiple = curry(multiple)
let curryDivide = curry(divide)
let curryMinus = curry(minus)

console.log(currySum(1,2,3)(4)(5)(6,7)(8)())
console.log(curryMultiple(1,2,3)(4)(5)(6,7)(8)())
console.log(curryDivide(1,2,3)(4)(5)(6,7)(8)())
console.log(curryMinus(1,2,3)(4)(5)(6,7)(8)())

期待和大家交流,共同进步,欢迎大家加入我创建的与前端开发密切相关的技术讨论小组:

微信公众号: 生活在浏览器里的我们 / excellent_developers

努力成为优秀的前端工程师!