Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
FrankKai opened this issue Dec 26, 2020 · 13 comments
Open

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

FrankKai opened this issue Dec 26, 2020 · 13 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Dec 26, 2020

最近面试了一些公司,拿了一些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)()升级版:如何实现加,减,乘,除呢?
@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
示例 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 只出现一次的数字

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

汇总区间

给定一个乱序整形数组[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)

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

实现红绿灯效果

实现红绿灯效果,使用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

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

数组去重

输入:
['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)
}

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

返回 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表列名称

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

如何检测一个空对象

如何检测出{}这样的空对象
答案
// 解法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;
}

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

实现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'

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

实现一个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')

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

大整数相加

请通过代码实现大整数(可能比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("");
};

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

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');

@FrankKai
Copy link
Owner Author

FrankKai commented Dec 26, 2020

字符串隐藏部分内容

字符串隐藏部分内容
说明:实现一个方法,接收一个字符串和一个符号,将字符串中间四位按指定符号隐藏
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;
}

@FrankKai
Copy link
Owner Author

FrankKai commented Mar 26, 2021

实现一个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]);
    }
}

@FrankKai
Copy link
Owner Author

FrankKai commented Mar 26, 2021

实现一个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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant