常见的面试算法题 js

110 阅读4分钟

无重复字符的最长子串

滑动窗口解法

递归遍历

篮子算法

滑动窗口

链表

92. 反转链表 II

21.合并两个有序链表

32. 最长有效括号

// 题目描述: 输入字符串,()匹配,如果是有效的输出所有括号里的内容,否则输出error
//输入 ((2+3) + (4+5)) + 8, 输出[(2 + 3), (4 + 5), ((2+3) + (4+5))]
//输入((2+3) + (4+5) + 8, 输出error
// 采用栈的方式,(符号下标入栈,) 符号栈元素出来,如果碰到“)”,此时栈为空,那肯定不是有效字符串,如果遍历结束栈里元素没出完,也不是有效的,其它的时候,根据出栈的值和当前“)”的位置获取字符串

function fn(str) {
    const res = [];
    const arr = [];
    for (let i =0 ; i < str.length; i++) {
        if (str[i] === '(') {
            arr.push(i);
            continue
        }
        if (str[i] === ')') {
            if (!arr.length) return 'error';
            const idx = arr.pop();
            res.push(str.substring(idx, i+1))
        }
    }

    if (arr.length) return 'error';
    return res;
}

排序算法

快排

function quickSort(arr, left, right) {
    var len = arr.length,
        partitionIndex,
        left = typeof left != 'number' ? 0 : left,
        right = typeof right != 'number' ? len - 1 : right;

    if (left < right) {
        // 第一步 找分割点
        partitionIndex = partition(arr, left, right);
        // 第二步 分别递归分割点左右2侧的数据
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

function partition(arr, left ,right) {     // 分区操作
    var pivot = left,                      // 设定基准值(pivot)
        index = pivot + 1;
    // 分割点的原理,主要是index记录所有比pivot小的(大的)有多少个
    for (var i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }        
    }
    swap(arr, pivot, index - 1);
    return index-1;
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

堆排序(www.runoob.com/w3cnote/hea…)

let len;
const heapSort = (nums) => {
    // 首先建立大顶堆(小顶堆),此时第一个数就是最大值(最小值)
    buildMaxHeap(nums);

    /**
        其次进行堆排序,
        主要是将最大值(第一位)和最后一个位置进行交换,
        然后剔除最大值后剩余的进行堆调整
     */
     for (let i = nums.length; i--; i > 0) {
         swap(nums, 0, i);
         len--;
         heapify(nums, 0)
     }

     console.log(nums, '--nums')
     return nums
}

// 建立大顶堆
const buildMaxHeap = (nums) => {
    len = nums.length;
    // 从中间节点开始
    for (let i = Math.floor(len/2); i >= 0; i--) {
        heapify(nums, i)
    }
}

//  堆调整
const heapify = (nums, i) => {
    let largest = i, // 用来标记最大元素
        left = 2 * i + 1, // 左节点
        right = 2 * i + 2;// 右节点
    
    if (left < len && nums[left] > nums[largest]) {
        largest = left
    }



    if (right < len && nums[right] > nums[largest]) {
        largest = right
    }


    if (largest !== i) {
        swap(nums, i, largest);
        heapify(nums, largest)
    }
}

// 元素交换
const swap = (nums, i, j) => {
    const temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

一年中的第几天

var dayOfYear = function(date) {
    var months = [31,28,31,30,31,30,31,31,30,31,30,31]
    var year = +date.slice(0, 4);
    var month = +date.slice(5, 7);
    var day = +date.slice(8);
    var res = 0;
    if (year % 400 === 0 || (year % 4 === 0 && year % 100 !==0)) {
        ++months[1];
    }
    for (var i = 0; i < month - 1; i++) {
        res+= months[i];
    }
    return res + day
};

三数之和

数组/left、right指针

两个数的乘积

LRU

防抖与节流

function debounce(fn, time) {
  let timeId;
  let _this = this
  function debounced() {
    if (timeId) {
      clearSetTimeout(timeId)
    }
    timeId = setTimeout(fn.apply(thisarguments) ,time)
  }
  return debounced
}


function throttle(fn, time) {
  let currentTime = 0;
  function throttled() {
    let now = new Date().getTime();
    if (now- currentTime > time) {
        currentTime = now;
        setTimeout(fn.apply(this, arguments), 0)
   }

  }

  return throttled
}

深度遍历和广度遍历

 深度遍历(递归)
  function deepTransfer(node, nodelist) {
    if (!node) return []
    nodelist.push(node)
    for (let i = 0; i < node.children.length; i++){
      deepTransfer(node.children[i], nodelist)
    }
    return nodelist
 }

 深度遍历(栈)

 function deepTransfer(node) {
    // nodelist 用来保存结果, stack用来实现遍历顺序
    let stack = [], nodelist = [];
    if (!node) return [];
    stack.push(node)
    while(stack.length) {
      let currentNode = stack.pop();
       nodelist.push(currentNode)
      for (let i = currentNode.children.length - 1; i >= 0; i--) {
        stack.push(currentNode.children[i])
      }
    }
    return nodelist
 }
 
 
 广度遍历(队列)
 function widthTransfer(node) {
  let stack = [], nodelist = [];
  if (!node) return [];
   stack.push(node);
   while(stack.length) {
       let currentNode = stack.shift();
       nodelist.push(currentNode)
       for(let i = 0; i < currentNode.children.length; i++) {
         stack.push(currentNode.children[i])
       }
  }
  return nodelist

}

async/await

/** 
function *func() {}
let gen = func();
 gen.next()
*/

function asyncToGenerator(generatorFunc) {
   return function() {
     let gen = generatorFunc.apply(this, arguments);
      return new Promise((resolve, reject) => {
         function step(key, arg){
           let generatorResult;
           try{
            generatorResult = gen[key](arg)
           } catch(err) {reject(err)}
          let {value, done } = generatorResult;
           if (done) {
              return resolve(value)
           } else {
               return Promise.resolve(value).then((val) => {step('next', val), (err) => step('throw', err)})
           }
         }
          step('next')
      })
   }
}

数组扁平化

function func(arr) {
let res = []
if (!arr.length) return []
for (let i = 0; i < arr.length; i++) {
  if (Array.isArray(arr[i])) {
      res = res.concat(func(arr[i]))
  } else {
      res.push(arr[i])
  }
}
return res
}

Promise

/**
 使用var a = new Promise((resolve, reject) => {}).then(resolve, reject)
 状态:pending/fulfilled/rejected
*/

function noop(){}

function resolvePromise(promise, value, resolve, reject) {
  if ((typeof value === 'object' && value !== null ) || typeof value === 'function') {
    if (typeof value.then === 'function') {
      // 如果value是函数的话,执行then继续,
      value.then.call(value, (success) => {
        resolvePromise(promise, success, resolve, reject)
      }, (fail) => {
        reject(fail)
      })
    } else {
      resolve(value)
    }
  } else {
    resolve(value)
  }
}
function Promise(fn) {
  const self = this;
  // promise状态
  self.state = 'pending';
  // 成功的值
  self.value = undefined;
  // 异常的原因
  self.reason = undefined;

   // 存放成功的回调
   this.onResolvedCallbacks = [];
   // 存放失败的回调
   this.onRejectedCallbacks= [];

  function resolve(value) {
    self.state = 'fulfilled';
    self.value = value;
    // 依次将对应的函数执行
    self.onResolvedCallbacks.forEach(fn=>fn());
  }

  function reject(reason) {
    self.state = 'rejected';
    self.reason= reason;
    // 依次将对应的函数执行
    self.onRejectedCallbacks.forEach(fn=>fn());
  }
  try {
    fn(resolve, reject)
  } catch (error) {
    reject(error)
  }
}

Promise.prototype.then = function(resolve, reject) {
  const self = this;
  // 这里的resolve/ reject可能没值,需要包装下
  resolve = typeof resolve === 'function' ? resolve : noop;
  reject = typeof reject === 'function' ? reject : function(err){throw(err)}
  let promise2 = new Promise((_resolve, _reject) => {
    if (self.state === 'fulfilled') {
      // 这里的res可能是then执行后返回的Promise,因此需要循环继续调用
     setTimeout(() => {
      try {
        let res = resolve(self.value);
        resolvePromise(promise2, res, _resolve, _reject)
      } catch (error) {
        reject(error)
      }
     }, 0)
    }
  
    if (self.state === 'rejected') {
      setTimeout(() => {
        try {
          let res = reject(self.reason);
          resolvePromise(promise2, res, _resolve, _reject)
        } catch (error) {
          reject(error)
        }
      }, 0)
    
    }
  
    if (self.state === 'pending') {
      self.onResolvedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let res = resolve(self.value);
            resolvePromise(promise2, res, _resolve, _reject)
          } catch (error) {
            reject(error)
          }
         }, 0)
      })
  
      self.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let res = reject(self.reason);
            resolvePromise(promise2, res, _resolve, _reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      })
    }
  });
  return promise2;
}

ajax

    function ajax(method = 'get', url) {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if (!this.readyState !== 4) return;
            if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { // success } else { // error }

        }

        xhr.open(method,url, 'async');
        xhr.send();
    }

异步scheduler

    class scheduler {
      constructor(limit) {
        this.list = [];
        this.limit= limit;
        this.idx = 0;
      }

      add(cb) {
        this.list.push(cb)
        this.run();
      }

      async run() {
          this.limit -= 1;
          while(this.idx < this.list.length && this.limit) {
              const shift = this.list.shift();
              this.idx += 1;
              await shift()
              this.limit += 1
          }
      }
    }

并发控制

// 主要通过limit的加减控制的,如果有程序运行limit-1, 程序执行结束limit+1, 如果limit为0的时候不执行
function sendRequest(requests, limit) {
  return new Promise((resolve, reject) => {
    const len = requests.length;
    let idx = 0;
    let count = 0;

    const send = async () => {
      while(idx < len && limit) {
        limit--;
        const request = requests[idx++];
        request().then((a) => {
          limit++;
          count++;
          console.log(a, '---aaa')
          if (len === count) {
            resolve()
          } else {
            send();
          }
        })
      }
    }

    send();
  })
}

const list = new Array(50).fill(0).map((a, idx) => {
  return function() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(idx);
      }, 1000);
    });
  }
});

sendRequest(list, 4)

将数组扁平化为树结构


const data1 = [
  {
    id: 1,
    pid: 0,
    name: "body",
  },
  {
    id: 2,
    pid: 1,
    name: "title",
  },
  {
    id: 3,
    pid: 2,
    name: "div",
  },
  {
    id: 4,
    pid: 0,
    name: "div",
  },
  {
    id: 9,
    pid: 4,
    name: "div",
  },
];

//通过map引入的方式
const transformArrayToTree = (arr) => {
    let res = [] // 结果存储
    let mapObj = {} // 通过对象临时记录
    // 首先给每一个添加children
    for (const item of arr) {
        mapObj[item.id] = {
           ...item,
           children: []
        }
    }
    
    // 通过对象引入关联,pid=0的存储到res数组中,其它的是在父节点pid的children下,通过push当前id的节点
    for (const item of arr) {
        // 这里的push采用mapObj保存的临时的,不要采用item原有的
        if (item.pid === 0) {
            res.push(mapObj[item.id]);
            continue;
        }
       mapObj[item.pid].children.push(mapObj[item.id])
        }
        
 return res
}

// 通过递归的方式
const transformArrayToTree = (arr) => {
    let res = [];
    const getChildren = (_res, parentId) => {
        for (const item of arr) {
            if (item.pid === parentId) {
                const newItem = {...item, children: []}
                _res.push(newItem);
                getChildren(newItem.children, newItem.id)
            }
        }
    }
    
    getChildren(res, 0)
    return res;
    
}