前端面试手撕代码

154 阅读7分钟
// 1 深拷贝
// eslint-disable-next-line no-unused-vars
function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
  if (typeof obj !== "object") return obj;
  // 是对象的话就要进行深拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}

// 2 手写apply
Function.prototype.myapply = function apply(ctx, ...args) {
  let context = ctx || window;
  let symbol = Symbol();
  context[symbol] = this;
  let res = context[symbol](args);
  delete context[symbol];
  return res;
};

function fn1(a, b, c) {
  console.log("this", this);
  console.log(a, b, c);
  return "this is fn1";
}
fn1.myapply({ x: 100 }, []);

// 3 手写call
Function.prototype.myBind = function bind(ctx, ...args) {
  let argument = args || [];
  const fn = this;
  return function () {
    return fn.apply(ctx, argument);
  };
};

// 4 手写instanceOf
// eslint-disable-next-line no-unused-vars
function newInstanceOf(left, right) {
  let rightProto = right.prototype;
  // eslint-disable-next-line no-constant-condition
  while (true) {
    if (left === null) {
      return false;
    }
    if (left === rightProto) {
      return true;
    }
    left = left._proto_;
  }
}

// 5 手写emitListener
// eslint-disable-next-line no-unused-vars
class event {
  constructor() {
    this.events = {};
  }
  
  emit(event, ...args){
      if (!this.events[event]) {
          this.events.forEach(v => {
              v.call(...args)
              if(v.isOnce){
                 off(event, v)
              }
          })
      }
  }

  on(event, fn) {
    if (!this.events[event]) {
      this.events[event] = [fn];
    } else {
      this.events[event].push(fn);
    }
  }

  off(event, fn) {
    this.events[event].filter((item) => item !== fn);
  }

  once(event, fn) {
    fn.isOnce = true
    this.on(event, fn);
  }
}

// 6 手写ajax

// eslint-disable-next-line no-unused-vars
function ajax(url, type) {
  let xhr = new XMLHttpRequest();
  xhr.open(type, url, true);
  xhr.send();
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      new Promise.resolve(xhr.response);
    }
  };
}

// 7 手写Promise 
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  // 保存初始化状态
  var self = this;

  // 初始化状态
  this.state = PENDING;

  // 用于保存 resolve 或者 rejected 传入的值
  this.value = null;

  // 用于保存 resolve 的回调函数
  this.resolvedCallbacks = [];

  // 用于保存 reject 的回调函数
  this.rejectedCallbacks = [];

  // 状态转变为 resolved 方法
  function resolve(value) {
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);
    }

    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变,
      if (self.state === PENDING) {
        // 修改状态
        self.state = RESOLVED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.resolvedCallbacks.forEach(callback => {
          callback(value);
        });
      }
    }, 0);
  }

  // 状态转变为 rejected 方法
  function reject(value) {
    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变
      if (self.state === PENDING) {
        // 修改状态
        self.state = REJECTED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.rejectedCallbacks.forEach(callback => {
          callback(value);
        });
      }
    }, 0);
  }

  // 将两个方法传入函数执行
  try {
    fn(resolve, reject);
  } catch (e) {
    // 遇到错误时,捕获错误,执行 reject 函数
    reject(e);
  }
}

MyPromise.prototype.then = function(onResolved, onRejected) {
  // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
  onResolved =
    typeof onResolved === "function"
      ? onResolved
      : function(value) {
          return value;
        };

  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : function(error) {
          throw error;
        };

  // 如果是等待状态,则将函数加入对应列表中
  if (this.state === PENDING) {
    this.resolvedCallbacks.push(onResolved);
    this.rejectedCallbacks.push(onRejected);
  }

  // 如果状态已经凝固,则直接执行对应状态的函数

  if (this.state === RESOLVED) {
    onResolved(this.value);
  }

  if (this.state === REJECTED) {
    onRejected(this.value);
  }
};


// 8 promise.all
// eslint-disable-next-line no-unused-vars
function promiseAll(promises) {
  if (!Array.isArray(promises)) {
    throw new Error("返回值错误");
  }
  let n = 0,
    arr = [];
  promises.forEach((item, index) => {
    Promise.resolve()
      .then((res) => {
        n++;
        arr[index] = res;
        if (n === promises.length) {
          Promise.resolve(arr);
        }
      })
      .catch((e) => {
        Promise.reject(e);
      });
  });
}

// 9 手动控制并发请求
// eslint-disable-next-line no-unused-vars
function multiRequest(urls = [], maxNum) {
  const len = urls.length;
  const result = new Array(len).fill(false);
  let count = 0;
  while (count < maxNum) {
    next();
  }

  function next() {
    let current = count++;
    if (current >= len) {
      !result.includes(false) && Promise.resolve(result);
    }
    const url = urls[current];
    fetch(url)
      .then((res) => {
        result[current] = res;
        if (current < len) {
          next();
        }
      })
      .catch((err) => {
        result[current] = err;
        if (current < len) {
          next();
        }
      });
  }
}

// 10 实现一个发布订阅
// eslint-disable-next-line no-unused-vars
class sub {
  constructor() {
    this.subs = [];
    this.state = 0;
  }

  addSub(sub) {
    let isExsit = this.subs.includes(sub);
    if (isExsit) {
      return console.log("sub existed");
    }
    this.subs.push(sub);
  }

  removeSub(sub) {
    let index = this.subs.indexOf(sub);
    if (index === -1) {
      return console.log("sub not existed");
    }
    this.subs.splice(index, 1);
  }

  notify() {
    this.subs.forEach((sub) => {
      sub.update(this.state);
    });
  }

  doSomeLogic() {
    console.log("doSomeLogic");
    this.state = Math.floor(Math.random() * 10);
    this.notify();
  }
}

// eslint-disable-next-line no-unused-vars
class obeserve {
  constructor(name) {
    this.name = name;
  }
  update(state) {
    console.log(this.name + " Recived state" + state);
  }
}

// 11 防抖
// eslint-disable-next-line no-unused-vars
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

// 12 节流
// eslint-disable-next-line no-unused-vars
function throttle(fn, delay) {
  let timer = null;
  return function (...args) {
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

// 13 函数柯里化
function reduce(...args) {
  return args.reduce((a, b) => a + b);
}
var curry = function(fn) {
    var limit = fn.length; // fn接受的参数个数
    var params = []; // 存储递归过程的所有参数,用于递归出口计算值

    return function _curry(...args) {
        params = params.concat(args); // 收集递归参数
        if (limit <= params.length) {
            // 返回函数执行结果
            return fn.apply(null, params);
        } else {
            // 返回一个柯里化函数
            return _curry;
        }
    };
};

let add = curry(reduce);
console.log(add(1)(2, 3, 4)(5)()); //15
console.log(add(1)(2, 3)(4, 5)()); //15

// 14 解析url为对象
// eslint-disable-next-line no-unused-vars
function parseUrl(url) {
  const paramsStr = /.+\?(.+)$/.exec(url)[1];
  const paramsArr = paramsStr.split("&");
  let params = {};
  paramsArr.forEach((item) => {
    if (/=/.test(item)) {
      let [key, val] = item.split("=");
      params[key] = val;
    }
  });
  return params;
}

// 15 数组扁平化
// eslint-disable-next-line no-unused-vars
function flat(arr) {
  let res = [];
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      res = res.concat(flat(item));
    } else {
      res.push(item);
    }
  });
  return res;
}

// 16 冒泡排序
// eslint-disable-next-line no-unused-vars
function bubbleSort(arr) {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = 0; j < len - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}

// 17 快速排序
// eslint-disable-next-line no-unused-vars
function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  let len = arr.length;
  let midIndex = Math.floor(len / 2);
  let mid = arr.splice(midIndex, 1)[0];
  const left = [],
    right = [];
  arr.forEach((item) => {
    if (item < mid) {
      left.push(item);
    } else {
      right.push(item);
    }
  });
  return quickSort(left).concat(mid).concat(quickSort(right));
}

// 18 归并排序
// eslint-disable-next-line no-unused-vars
function mergeSort(arr) {
  const len = arr.length;
  if (len < 2) {
    return arr;
  }
  let midIndex = Math.floor(len / 2);
  let left = arr.splice(0, midIndex);
  let right = arr.splice(midIndex);
  return merge(mergeSort(left), mergeSort(right));
}

const merge = function (left, right) {
  const result = [];
  if (left[0] <= right[0]) {
    result.push(left.shift());
  } else {
    result.push(right.shift());
  }
  while (left.length) result.push(left.shift());

  while (right.length) result.push(right.shift());

  return result;
};

// 19 下一个排列 [1, 2, 3] 的下一个排列是[1, 3, 2] [3, 2, 1]的下一个排列是[1, 2, 3]
// eslint-disable-next-line no-unused-vars
var nextPermutation = function (nums) {
  const n = nums.length - 1;
  let i = n - 1,
    j = n;
  while (i >= 0 && nums[i] > nums[i + 1]) i--;
  if (i >= 0) {
    while (j > 0 && nums[j] < nums[i]) j--;
    [nums[i], nums[j]] = [nums[j], nums[i]];
  }
  let l = i + 1,
    r = n;
  while (l < r) {
    [nums[l], nums[r]] = [nums[r], nums[l]];
    l++;
    r--;
  }
};

// 20 合并区间
//输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
//输出: [[1,6],[8,10],[15,18]]
//解释: 区间 [1,3][2,6] 重叠, 将它们合并为 [1,6].
// eslint-disable-next-line no-unused-vars
function mergeRange(arr) {
  if (arr.length < 2) {
    return arr;
  }
  let tmp = arr.sort((a, b) => a[0] - b[0]);
  function unite(list, i) {
    if (i === tmp.length - 1) {
      return list;
    }
    if (arr[i][1] >= arr[i + 1][0]) {
      arr[i] = [arr[i][0], Math.max(arr[i][1], arr[i + 1][1])];
      arr.splice(i + 1, 1);
    } else {
      i + 1;
    }
    return unite(list, i);
  }
  return unite(tmp, 0);
}

// 21 两数之和
// 输入:nums = [2,7,11,15], target = 9 输出:[0,1]
// eslint-disable-next-line no-unused-vars
function twoNums(arr, target) {
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    let another = target - arr[i];
    let index = arr.indexOf(another);
    if (index !== -1 && index !== i) {
      res.push(i, index);
    }
  }
}

// 22 三数之和 leetcode 15
// 输入:nums = [-2,1,-1,0, 2], target = 0 输出:[[-2, 0, 2], [-1, 0 , 1]]
// eslint-disable-next-line no-unused-vars
function treeNum(nums) {
  if (nums.length < 3) {
    return [];
  }
  nums.sort((a, b) => a - b);
  let n = nums.length,
    res = [];
  for (let i = 0; i < n - 2; i++) {
    if (nums[i] > 0) break;
    if (nums[i] === nums[i - 1]) {
      continue;
    }
    let l = i + 1,
      r = n - 1;

    while (l < r) {
      let sum = nums[i] + nums[l] + nums[r];
      if (sum === 0) {
        res.push([nums[i], nums[l], nums[r]]);
        while (l < r && nums[l] === nums[l + 1]) l++;
        while (l < r && nums[r] === nums[r - 1]) r--;
        l++;
        r--;
      }
      if (sum > 0) {
        r--;
      }
      if (sum < 0) {
        l++;
      }
    }
  }
  return res;
}

// 23 leetcode 33. 搜索旋转排序数组
// nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1],
// ..., nums[k - 1]](下标 从 0 开始 计数)。例如,[0, 1, 2, 4, 5, 6, 7] 在下标 3 处经旋转后可能变为[4, 5, 6, 7, 0, 1, 2] 。
// 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
// eslint-disable-next-line no-unused-vars
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
// eslint-disable-next-line no-unused-vars
var search = function (nums, target) {
  const n = nums.length;
  if (n === 1) return nums[0] === target ? 0 : -1;
  if (n < 1) return -1;
  let l = 0,
    r = n - 1;
  while (l <= r) {
    let mid = Math.floor((l + r) / 2);
    if (nums[mid] === target) return mid;
    // 判断左边是否为升序
    if (nums[0] <= nums[mid]) {
      if (nums[0] <= target && nums[mid] > target) {
        r = mid - 1;
      } else {
        l = mid + 1;
      }
    } else {
      if (target > nums[mid] && target <= nums[n - 1]) {
        l = mid + 1;
      } else {
        r = mid - 1;
      }
    }
  }
  return -1;
};

// 24 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数
// eslint-disable-next-line no-unused-vars
var findMedianSortedArrays = function (nums1, nums2) {
  let res = [];
  if (nums1.length < 1) {
    res = nums2;
  }
  if (nums2.length < 1) {
    res = nums1;
  }
  res = nums2.concat(nums1);
  res = res.sort((a, b) => a - b);
  let i1 = Math.ceil(res.length / 2);
  let i2 = Math.floor(res.length / 2);
  if (i1 === i2) {
    return (res[i1 - 1] + res[i1]) / 2;
  } else {
    return res[i2];
  }
};

// 25 全排列 leetcode 46
// eslint-disable-next-line no-unused-vars
var permute = function (nums) {
  var res = [];
  var backtrack = function (path) {
    if (path.length === nums.length) {
      res.push(path);
    }
    nums.forEach((i) => {
      if (path.includes(i)) {
        return;
      }
      backtrack(path.concat(i));
    });
  };
  backtrack([]);
  return res;
};

// 26 字符串中的第一个唯一字符 leetcode 387
// eslint-disable-next-line no-unused-vars
var firstUniqChar = function (s) {
  for (let i in s) {
    if (s.indexOf(s[i]) == s.lastIndexOf(s[i])) {
      return i;
    }
  }
  return -1;
};

// 27 无重复的最大子串 leetcode 3
// eslint-disable-next-line no-unused-vars
var lengthOfLongestSubstring = function (s) {
  if (s.length <= 1) {
    return s.length;
  }
  const n = s.length;
  let res = 1;
  for (let i = -1; i < n - 1; i++) {
    let j = i + 1,
      set = new Map();
    while (j < n && !set.has(s.charAt(j))) {
      set.set(s.charAt(j), 1);
      j++;
    }
    res = Math.max(res, j - i - 1);
  }
  return res;
};

// 28 最长回文子串 leetcode 5
/**
 * @param {string} s
 * @return {string}
 */
// eslint-disable-next-line no-unused-vars
var longestPalindrome = function (s) {
  let max = 0; // 当前最大回文串的长度
  let start = -1; // 当前最大回文串的起始索引
  const len = s.length; // s 的长度
  for (let i = 0; i < len; i++) {
    // 遍历 s
    let now = 1; // 当前回文串的长度
    let l = i - 1; // 左侧开始遍历的指针
    while (s[i + 1] === s[i]) {
      // 如果当前字符后边的字符都一样, 当前长度 + 1,  s遍历指针向后推
      now++;
      i++;
    }
    let r = i + 1; // 获取右侧开始遍历的指针
    while (s[l] === s[r] && s[l] !== undefined) {
      // 从连续字符两端开始像两侧扩展,直到越界或者不一致,一致的直接累积到当前长度中,修改左右指针
      now += 2;
      l--;
      r++;
    }
    if (now > max) {
      // 判断与之前最大的对比,更新当前最大回文串的起始索引
      max = now;
      start = l + 1;
    }
  }
  return s.slice(start, start + max); // 通过最大长度和起始索引,获取需要的字符串
};

// 29 整数反转 leetcode 7
// eslint-disable-next-line no-unused-vars
var reverse = function (x) {
  const symbol = String(x).split("").reverse().join("");
  let result;
  if (x > 0) {
    result = Number(symbol);
  } else {
    result = Number(symbol.slice(-1) + symbol.slice(0, -1));
  }
  if (result < (-2) ** 31 || result > 2 ** 31 - 1) {
    result = 0;
  }
  return result;
};

// 30 退格
// 比较含有退格的字符串,"<-"代表退格键,"<"和"-"均为正常字符
// 输入:"a<-b<-", "c<-d<-",结果:true,解释:都为""
// 输入:"<-<-ab<-", "<-<-<-<-a",结果:true,解释:都为"a"
// 输入:"<-<ab<-c", "<<-<a<-<-c",结果:false,解释:"<ac" !== "c"
// eslint-disable-next-line no-unused-vars
function isEqual(str1, str2) {
  var func = function (s) {
    let res = [],
      n = s.length;
    for (let i = 0; i < n; i++) {
      if (s.charAt[i] === "<" && s.charAt[i + 1] === "-") {
        res.pop();
      } else {
        res.push(s.charAt[i]);
      }
    }
    return s.join("");
  };
  let isSame = func(str1) === func(str2);
  return isSame;
}

// 31 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合,
// 输入:n = 3,输出:["((()))", "(()())", "(())()", "()(())", "()()()"]
// eslint-disable-next-line no-unused-vars
function generateParenthesis(n) {
  const result = [];
  const dfs = (path, count1, count2) => {
    // path为递归的字符串,count1为左括号的数量,count2为右括号的数量
    // 当左括号或右括号大于传入的n,括号生成后的岁数,那这个递归函数就不跑了。
    if (count1 > n || count2 > n) return;
    // 如果右括号的数量大于左括号的数量,也不符合题意,也不跑了。
    if (count2 > count1) return;
    // 左括号和右括号的数量都对了 那就把正确结果推出去
    if (count1 === n && count2 === n) {
      result.push(path);
      return;
    }

    //这边处理第一次传入空字符串的情况
    if (count1 === 0) {
      dfs(path + "(", count1 + 1, count2);
    } else {
      // 只有这两种结果
      dfs(path + "(", count1 + 1, count2);
      dfs(path + ")", count1, count2 + 1);
    }
  };
  dfs("", 0, 0);
  return result;
}

// 32 有效括号
// eslint-disable-next-line no-unused-vars
var isValid = function (s) {
  let tmp = [],
    n = s.length;
  for (let i = 0; i < n; i++) {
    if (s.charAt(i) === ")") {
      if (tmp[tmp.length - 1] === "(") {
        tmp.pop();
      } else {
        tmp.push(s.charAt(i));
      }
    } else if (s.charAt(i) === "]") {
      if (tmp[tmp.length - 1] === "[") {
        tmp.pop();
      } else {
        tmp.push(s.charAt(i));
      }
    } else if (s.charAt(i) === "}") {
      if (tmp[tmp.length - 1] === "{") {
        tmp.pop();
      } else {
        tmp.push(s.charAt(i));
      }
    } else {
      tmp.push(s.charAt(i));
    }
  }
  return tmp.length ? false : true;
};

// 33 leetcode 剑指 Offer 39. 数组中出现次数超过一半的数字
/**
 * @param {number[]} nums
 * @return {number}
 */
// eslint-disable-next-line no-unused-vars
var majorityElement = function (nums) {
  nums.sort((a, b) => a - b);
  let len = Math.floor(nums.length / 2);
  for (let i = 0; i <= len; i++) {
    if (nums[i] === nums[i + len]) {
      return nums[i];
    }
  }
  return -1;
};

// 34 链表 添加  删除
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}
// eslint-disable-next-line no-unused-vars
class SimpleLinkList {
  constructor(data) {
    this.head = new Node(data);
  }
  add(data) {
    let node = new Node(data);
    let current = this.head;
    while (current.next) {
      current = current.next;
    }
    current.next = node;
  }
  addAt(data, index) {
    let node = new Node(data);
    let currentIndex = 1;
    let current = this.head;
    while (currentIndex < index) {
      current = current.next;
      currentIndex++;
    }
    current.next = node;
  }
  removeAt(index) {
    let current = this.head;
    let pre = null;
    let currentIndex = 1;
    while (currentIndex < index) {
      pre = current;
      currentIndex = current.next;
      currentIndex++;
    }
    pre.next = current.next;
  }
  reverse() {
    let pre = this.head;
    let current = pre.next;
    pre.next = null;
    while (current) {
      let next = current.next;
      current.next = pre;
      pre = current;
      current = next;
    }
    this.head = pre;
  }
}

// 35 合并两个有序列表
// eslint-disable-next-line no-unused-vars
var mergeTwoLists = function (l1, l2) {
  if (l1 == null) return l2;
  if (l2 == null) return l1;
  if (l1.val < l2.val) {
    l1.next = mergeTwoLists(l1.next, l2);
    return l1;
  } else {
    l2.next = mergeTwoLists(l1, l2.next);
    return l2;
  }
};

// 36 leetcode 2 两数相加
// 给你两个非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
//请你将两个数相加,并以相同形式返回一个表示和的链表。
//你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}
// eslint-disable-next-line no-unused-vars
var addTwoNumbers = function (l1, l2) {
  let addone = 0;
  let sum = new ListNode();
  this.head = sum;
  while (addone || l1 || l2) {
    let val1 = l1.val ? l1.val : 0;
    let val2 = l2.val ? l2.val : 0;
    let r1 = val1 + val2 + addone;
    addone = r1 > 10 ? 1 : 0;
    sum.next = new ListNode(r1 % 10);
    sum = sum.next;
    if (l1) l1 = l1.next;
    if (l2) l2 = l2.next;
  }
  return this.head.next;
};

// 37 二叉树遍历
// 前序遍历 中序 后序类似
// eslint-disable-next-line no-unused-vars
function treeFrontEach(treeList) {
  if (!treeList || treeList.val === null) return null;
  console.log(treeList.val);
  treeFrontEach(treeList.left);
  treeFrontEach(treeList.right);
}

// 不用递归实现
// eslint-disable-next-line no-unused-vars
const preorderTraversal = function (root) {
  const stack = [],
    res = [];
  root && root.push(root);
  while (stack.length) {
    let cur = stack.pop();
    res.push(cur.val);
    cur.left && stack.push(cur.left);
    cur.right && stack.push(cur.right);
  }
  return res;
};

// 38 leetcode 112 路径总和
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
// eslint-disable-next-line no-unused-vars
var hasPathSum = function (root, targetSum) {
  if (!root) {
    return false;
  }
  if (root.val === targetSum && root.left === null && root.right === null) {
    return true;
  }
  let left = hasPathSum(root.left, targetSum - root.val);
  let right = hasPathSum(root.right, targetSum - root.val);
  return left || right;
};

// 39 leetcode 113 路径总和2
// eslint-disable-next-line no-unused-vars
var pathSum = function (root, targetSum) {
  var res = [];
  var array = [];
  function doFind(root, targetSum) {
    if (root === null) {
      return;
    }
    targetSum = targetSum - root.val;
    array.push(root.val);
    if (root.left === null && root.right === null && targetSum === 0) {
      res.push([...array]);
    }
    doFind(root.left, targetSum);
    doFind(root.right, targetSum);
    array.pop();
  }
  doFind(root, targetSum);
  return res;
};

// 40 leetcode 226 二叉树翻转
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
// eslint-disable-next-line no-unused-vars
var invertTree = function (root) {
  if (root) {
    [root.left, root.right] = [invertTree(root.right), invertTree(root.left)];
  }
  return root;
};

// 41 leetcode 617 合并二叉树
// eslint-disable-next-line no-unused-vars
var mergeTrees = function (root1, root2) {
  if (!root1) return root2;
  if (!root2) return root1;
  root1.val = root1.val + root2.val;
  root1.left = mergeTrees(root1.left, root2.left);
  root1.right = mergeTrees(root1.right, root2.right);
  return root1;
};

// 42 dom树的深度遍历
// eslint-disable-next-line no-unused-vars
function deepTravalSal(node) {
  const nodes = [];
  const stack = [];
  if (node) {
    stack.push(node);
    while (stack.length) {
      const item = stack.pop();
      const len = item.children.length;
      nodes.push(item);
      for (let i = len - 1; i >= 0; i--) {
        stack.push(item.children[i]);
      }
    }
  }
  return nodes;
}
// eslint-disable-next-line no-unused-vars
function dfs(dom) {
  let nodesList = [];
  nodesList.push(dom);
  if (dom.children && dom.children.length) {
    for (let i = 0; i < dom.children.length; i++) {
      nodesList = nodesList.concat(dfs(dom.children[i]));
    }
  }
  return nodesList;
}

// 43 dom树的广度遍历
// eslint-disable-next-line no-unused-vars
function breathTravalSal(node) {
  const nodes = [];
  const queue = [];
  if (node) {
    queue.push(node);
    while (queue.length) {
      const item = queue.shift();
      nodes.push(item);
      for (const v of item.children) {
        queue.push(v);
      }
    }
  }
  return nodes;
}
// 递归
// eslint-disable-next-line no-unused-vars
function bfs(dom) {
  if (!(dom instanceof Array)) {
    dom = [dom];
  }
  let nodeList = [];
  let childrenArr = [];
  for (let i = 0; i < dom.length; i++) {
    nodeList.push(dom[i]);
    if (dom[i].children && dom[i].children.length) {
      childrenArr = childrenArr.concat(dom[i].children);
    }
  }
  if (childrenArr.length > 0) {
    nodeList = nodeList.concat(bfs(childrenArr));
  }
  return nodeList;
}

// 44 动态规划
// 爬楼梯:假设你现在正在爬楼梯,楼梯有 n 级。每次你只能爬 1 级或者 2 级,那么你有多少种方法爬到楼梯的顶部
// eslint-disable-next-line no-unused-vars
var climbStairs = function (n) {
  if (n === 1 || n === 2) {
    return n;
  }
  //前一个值
  let pre = 2;
  //前一个的前一个的值
  let beforePre = 1;
  //中间变量
  let temp = null;
  for (let index = 3; index <= n; index++) {
    temp = pre;
    pre = pre + beforePre;
    beforePre = temp;
  }
  return pre;
};

// 45 leetcode 121 买卖股票的最佳时机
// 暴利解
// eslint-disable-next-line no-unused-vars
var maxProfit = function (prices) {
  var maxProfit = 0;
  for (var i = 0; i < prices.length; i++) {
    for (var j = i + 1; j < prices.length; j++) {
      if (prices[j] > prices[i] && prices[j] - prices[i] > maxProfit) {
        maxProfit = prices[j] - prices[i];
      }
    }
  }
  return maxProfit;
};

// 动态规划
// eslint-disable-next-line no-unused-vars
var maxProfit1 = function (prices) {
  if (prices.length === 0 || prices.length === 1) {
    return 0;
  }
  // dp1数组存储第`i`天,持有股票的最大利润
  const dp1 = [];
  dp1[0] = -prices[0];
  // dp2数组存储第`i`天,不持有股票的最大利润
  const dp2 = [];
  dp2[0] = 0;

  for (let i = 1; i < prices.length; i++) {
    dp1[i] = Math.max(dp1[i - 1], -prices[i]);
    dp2[i] = Math.max(dp2[i - 1], prices[i] + dp1[i - 1]);
  }

  return dp2[dp2.length - 1];
};