打卡LeetCode

2021.11.22

784. 字母大小写全排列

var letterCasePermutation = function(s) {
  const strMap = getStrMap(s);
  const visited = new Array(s.length).fill(false);
  const result = new Set();
  const dfs = (index, currStr) => {
    if (index === s.length) {
      result.add(currStr);
      return;
    }

    for (let i = 0; i < strMap.get(index).length; i++) {
      const str = currStr;
      currStr += strMap.get(index)[i];
      dfs(index + 1, currStr);
      currStr = str;
    }
  }
  dfs(0, '');
  return Array.from(result);
};
// 根据str获取对应大小写key: index value: 大小写值
function getStrMap(s) {
  const map = new Map();
  for (let i = 0; i < s.length; i++) {
    if (s[i].toLocaleUpperCase() === s[i].toLocaleLowerCase()) {
      // 说明不是一个字母
      map.set(i, [s[i]]);
    } else {
      map.set(i, [s[i].toLocaleUpperCase(), s[i].toLocaleLowerCase()]);
    }
  }
  return map;
}
console.log(letterCasePermutation("a1b2")) // ["a1b2", "a1B2", "A1b2", "A1B2"]
console.log(letterCasePermutation("3z4")) // ["3z4", "3Z4"]
console.log(letterCasePermutation("12345")) // ["12345"]
console.log(letterCasePermutation("aa")) // ["aA", "Aa", "aa", "AA"]
console.log(letterCasePermutation("a")) // ["a", "A"]

785. 模糊坐标

// 将数字任意分成两组数,然后随意添加小数点或不加
var ambiguousCoordinates = function(s) {
  const numList = getNumsList(s);
  const result = new Set();
  const dfs = (index1, maxIndex1, arrList) => {
    if (index1 === maxIndex1) {
      return;
    }
    for (const item of arrList[1]) {
      result.add(`(${arrList[0][index1]}, ${item})`)
    }
    dfs(index1 + 1, maxIndex1, arrList);
  }
  for (const item of numList) {
    dfs(0, item[0].length, item);
  }
  return [...result];
};

// 将字符串分成两两数值
function getNumsList(s) {
  const nums = [];
  const numStr = s.substr(1, s.length - 2);
  for (let i = 1; i < numStr.length; i++) {
    const item1 = convertNums(numStr.substr(0, i));
    const item2 = convertNums(numStr.substr(i));
    const item = [item1, item2];
    nums.push(item);
  }
  return nums;
}

// 传入一个数字字符串,输出加小数点的各种值list
function convertNums(numStr) {
  if (numStr.length === 1) {
    return [numStr];
  }
  const result = [];
  if (numStr.length === 1 || !numStr.startsWith('0')) {
    result.push(numStr);
  }
  // 添加小数点
  for (let i = 1; i < numStr.length; i++) {
    const value1 = numStr.substr(0, i);
    const value2 = numStr.substr(i);
    if (isMatch(value1, value2)) {
      result.push(value1 + '.' + value2);
    }
  }
  return result;
}

// 判断该数是否符合要求 整数部分不以0开头,小数部分不以0结尾
function isMatch(floatNum, floatValue) {
  if (floatNum.length > 1 && floatNum.startsWith('0')) {
    return false;
  }
  if (floatValue.endsWith('0')) {
    return false;
  }
  return true;
}
console.log(ambiguousCoordinates("(123)")) //  ["(1, 23)", "(12, 3)", "(1.2, 3)", "(1, 2.3)"]
console.log(ambiguousCoordinates("(00011)")) // ["(0.001, 1)", "(0, 0.011)"]
console.log(ambiguousCoordinates("(0123)")) // ["(0, 123)", "(0, 12.3)", "(0, 1.23)", "(0.1, 23)", "(0.1, 2.3)", "(0.12, 3)"]
console.log(ambiguousCoordinates("(100)")) //  [(10, 0)]

2021.11.23

93. 复原IP地址

var restoreIpAddresses = function(s) {
  if (s.length < 4) {
    return [];
  }
  debugger;
  const result = [];
  // 每个IP数值最多有三个
  const dfs = (start, currStr, dotSize) => {
    if (start > s.length || (dotSize === 3 && start < s.length)) {
      return;
    }
    if (dotSize === 3 && start === s.length) {
      result.push(currStr);
      return;
    }
    for (let sizeInt = 1; sizeInt < 4; sizeInt++) {
      const preStr = currStr;
      const preDotSize = dotSize;
      const subStr = s.substr(start, sizeInt);
      if (isMatchNumNode(subStr)) {
        currStr = currStr + '.' + subStr;
        dfs(start + sizeInt, currStr, dotSize + 1);
        currStr = preStr;
        dotSize = preDotSize;
      }
    }
  }
  for (let i = 1; i < 4; i++) {
    const str = s.substr(0, i);
    if (isMatchNumNode(str)) {
      dfs(i, str, 0);
    }
  }
  return result;
};

// 判断是不是符合IP地址单个数值要求
function isMatchNumNode(strNum) {
  // 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔
  if (!strNum) {
    // 空字符
    return false;
  }
  const num = Number(strNum)
  if (Number.isNaN(num)) {
    // 不是一个数值
    return false;
  }
  if (strNum.length > 1 && strNum.startsWith('0')) {
    return false;
  }
  if (num % 1 !== 0) {
    // 不是一个整数
    return false;
  }
  if (num < 0 || num > 255) {
    // 不在有效范围内
    return false;
  }
  return true;
}
console.log(restoreIpAddresses("25525511135")) // ["255.255.11.135","255.255.111.35"]
console.log(restoreIpAddresses("0000")) // ["0.0.0.0"]
console.log(restoreIpAddresses("1111")) // [""1.1.1.1"]
console.log(restoreIpAddresses("010010")) // ["0.10.0.10","0.100.1.0"]
console.log(restoreIpAddresses("101023")) // ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

967. 连续差相同的数字

var numsSameConsecDiff = function(n, k) {
  const resut = new Set();
  const str = '0123456789';
  // preNum前一个数值 currNum当前数值 currStrNums所有的数值
  const dfs = (preNum, currStrNums) => {
    if (currStrNums.length > n) {
      return;
    }
    if (currStrNums.length === n && isMathNum(currStrNums)) {
      resut.add(currStrNums - 0);
      return;
    }
    for (const item of str) {
      const preNumCopy = preNum;
      const preStrNums = currStrNums;
      if (Math.abs(preNum - 0 - item) === k) {
        currStrNums += item;
        dfs(item, currStrNums);
      }
      currStrNums = preStrNums;
      preNum = preNumCopy;
    }
  }
  for (const item of str) {
    dfs(item, item)
  }
  return [...resut];
};

function isMathNum(strNum) {
  return !(strNum.length > 0 && strNum.startsWith('0'))
}
console.log(numsSameConsecDiff(3, 7)) // [181,292,707,818,929]
console.log(numsSameConsecDiff(2, 1)) // [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98]
console.log(numsSameConsecDiff(2, 0)) // [11,22,33,44,55,66,77,88,99]
console.log(numsSameConsecDiff(2, 2)) // [13,20,24,31,35,42,46,53,57,64,68,75,79,86,97]

2021.11.24

494. 目标和

// 每个数有正负两种情况
var findTargetSumWays = function(nums, target) {
  let result = 0;
  const dfs = (currSum, index) => {
    if (index > nums.length) {
      return;
    }
    if (currSum === target && index === nums.length) {
      result = result + 1;
      return;
    }
    const arr = [nums[index], -nums[index]];
    for (const item of arr) {
      const indexPre = index;
      const preSum = currSum;
      dfs(currSum + item, index + 1);
      index = indexPre;
      currSum = preSum;
    }
  }
  const arr = [nums[0], -nums[0]];
  for (const item of arr) {
    dfs(item, 1);
  }
  return result;
};
console.log(findTargetSumWays([1,1,1,1,1], 3)) // 5
console.log(findTargetSumWays([1], 1))  // 1
console.log(findTargetSumWays([1, 0], 1))  // 2

1219. 黄金矿工

var getMaximumGold = function(grid) {
  const lenM = grid.length;
  const lenN = grid[0].length;
  const posions = getPostions();
  let result = 0;
  const visited = (new Array(lenM).fill()).map(() => new Array(lenN).fill(false));
  const dfs = (m, n, currentSum) => {
    if(grid[m][n] === 0) {
      return;
    }
    if (currentSum > result) {
      result = currentSum;
    }
    visited[m][n] = true;
    for (const item of posions) {
      const currM = m + item[0];
      const currN = n + item[1];
      if (isMatchPostion(currM, currN, lenM, lenN) && visited[currM][currN] === false) {
        dfs(currM, currN, currentSum + grid[currM][currN])
      }
    }
    visited[m][n] = false;
  }

  for (let m = 0; m < grid.length; m++) {
    for (let n = 0; n < grid[m].length; n ++) {
      if (grid[m][n] !== 0) {
        dfs(m, n, grid[m][n])
      }
    }
  }

  return result;
};

// 获取上下左右四个方向
function getPostions() {
  return [
    [-1, 0],
    [1, 0],
    [0, -1],
    [0, 1]
  ]
}
// 坐标是否符合要求
function isMatchPostion(m, n, len1, len2) {
  return m < len1 && n < len2 && m >= 0 && n >= 0;
}

console.log(getMaximumGold([[0,6,0],[5,8,7],[0,9,0]]))  // 24
console.log(getMaximumGold([[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]))  // 28

2021.11.25

306. 累加数

// 将字符串分成任意的数值组合
var isAdditiveNumber = function(num) {
 const dfs = (preSum, preValue, currentNums, restStr) => {
    if (restStr.length === 0 && currentNums < 3) {
      return false;
    }
    if (restStr.length === 0 && currentNums >= 3) {
      return true;
    }
    for (let i = 1; i < restStr.length + 1; i++) {
      const currValue = restStr.substr(0, i);
      const restNums = restStr.substr(i);
      if (isMatchStrNum(currValue) && (preSum === currValue - 0 || currentNums === 1)) {
        if (dfs(currValue - 0 + preValue, currValue - 0, currentNums + 1, restNums)) {
          return true;
        }
      }
    }
 }
 for (let i = 1; i < num.length - 1; i++) {
   const currNum = num.substr(0, i);
   const restStr = num.substr(i);
   if (isMatchStrNum(currNum) && dfs(currNum - 0, currNum - 0, 1, restStr)) {
     return true;
   }
 }
 return false;
};
// 是否是一个有效的数值
function isMatchStrNum(strNum) {
  if (strNum.length === 1) {
    return true;
  }
  return !strNum.startsWith('0');
}

console.log(isAdditiveNumber('111122'))  // true
console.log(isAdditiveNumber('112358'))  // true
console.log(isAdditiveNumber('199100199'))  // true
console.log(isAdditiveNumber('0235813'))  // true

1079. 活字印刷

var numTilePossibilities = function(tiles) {
  const visited = new Array(tiles.length).fill(false);
  const result = new Set();
  const dfs = (currStr, size) => {
    if (size === currStr.length) {
      result.add(currStr);
      return;
    }
    for (let i = 0; i < tiles.length; i++) {
      if (visited[i] === false) {
        visited[i] = true;
        const currStrCopy = currStr;
        currStr += tiles[i];
        dfs(currStr, size);
        visited[i] = false;
        currStr = currStrCopy;
      }
    }
  }
  for (let i = 1; i < tiles.length + 1; i ++) {
    dfs('', i);
  }
  return result.size;
};

console.log(numTilePossibilities("AAB"))  // 8 "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA"。
console.log(numTilePossibilities("AA"))  // 2 "A", "AA"
console.log(numTilePossibilities("AAABBC"))  // 188

2021.11.26
面试题17.22 单词转换

var findLadders = function(beginWord, endWord, wordList) {
  const visited = new Array(wordList.length).fill(false);
  let isMatchEnd = false;
  debugger
  for (let i = 0; i < wordList.length; i++) {
    if (wordList[i] === beginWord) {
      visited[i] = true;
    }
    if (wordList[i] === endWord) {
      isMatchEnd = true;
    }
  }
  if (!isMatchEnd) {
    return [];
  }
  const path = [beginWord];
  const dfs = (currWord) => {
    debugger
    if (currWord === endWord) {
      return true;
    };
    for (let i = 0; i < wordList.length; i++) {
      // 当前的单词是否一步转换为wordList中的一个单词
      if (visited[i] === false && changeOneStep(currWord, wordList[i])) {
        visited[i] = true;
        path.push(wordList[i]);
        if (dfs(wordList[i])) {
          return true;
        }
        // 退回来的时候visited不用变回来,因为既然这种情况往下走是到不了终点字符的,退回去之后再次来到这个字符依然是到不了
        path.pop();
      }
    }
    return false;
  };

  if (dfs(beginWord)) {
    return path;
  }
  return [];
};

// 判断word1是否能一步转换到word2
function changeOneStep(word1, word2) {
  if (word1.length !== word2.length) {
    return false;
  }
  let count = 0;
  for (let i = 0; i < word1.length; i++) {
    if (word1[i] !== word2[i]) {
      count++;
    }
    if (count > 1) {
      return false;
    }
  }
  return true;
}

2021.11.28

1947. 最大兼容性评分和

// 固定一个,对另外一个进行全排列
var maxCompatibilitySum = function(students, mentors) {
  const studentsVisited = new Array(students.length).fill(false);
  let maxSum = 0;
  // 老师固定 对学生进行全排列
  const dfs = arr => {
    if (arr.length === students.length) {
      let sum = 0;
      for (let i = 0; i < arr.length; i++) {
        sum += getScore(students[arr[i]], mentors[i]);
      }
      maxSum = Math.max(maxSum, sum);
      return;
    }
    for (let i = 0; i < students.length; i++) {
      if (studentsVisited[i] === false) {
        studentsVisited[i] = true;
        arr.push(i);
        dfs(arr);
        studentsVisited[i] = false;
        arr.pop();
      }
    }
  }
  dfs([])
  return maxSum;
};

function getScore(arr1, arr2) {
  let result = 0;
  let index = 0;
  while (index < arr1.length) {
    if (arr1[index] === arr2[index]) {
      result++;
    }
    index++;
  }
  return result;
}
function getSum(arr) {
  let sum = 0;
  for (const item of arr) {
    sum += item;
  }
  return sum;
}
console.log(maxCompatibilitySum([[1,1,0],[1,0,1],[0,0,1]], [[1,0,0],[0,0,1],[1,1,0]]))  // 8
console.log(maxCompatibilitySum([[0,0],[0,0],[0,0]], [[1,1],[1,1],[1,1]]))  // 0

2021.11.29

473. 火柴拼正方形

var makesquare = function(matchsticks) {
  if (matchsticks.length < 4) {
    return false;
  }
  const edgLen = getOneEdge(matchsticks);
  if (edgLen % 1 !== 0) {
    return false;
  }
  const edgs = new Array(4).fill(0);
  // index表示当前使用的边的索引
  const dfs = (index) => {
    if (index === matchsticks.length) {
      return true;
    }
    for (let i = 0; i < 4; i++) {
      // 正方形四条边,生成当前边
      if (edgs[i] + matchsticks[index] <= edgLen) {
        // 相加小于正方形需要的边长则递归
        edgs[i] += matchsticks[index];
        if (dfs(index + 1)) {
          return true
        }
        edgs[i] -= matchsticks[index];
      }
    }
    return false;
  }
  return dfs(0);
};
// 计算正方形的边长
function getOneEdge(list) {
  let sum = 0;
  for (const item of list) {
    sum += item;
  }
  return sum / 4;
}
console.log(makesquare([1,1,2,2,2]))

2021.11.30
1239. 串联字符串的最大长度

var maxLength = function(arr) {

  const result = [];
  let maxLen = 0;
  const arrRes = deleSame(arr);
  if (arrRes.length === 0) {
    return 0;
  }
  if (arrRes.length === 1) {
    return arrRes[0].length;
  }
  const dfs = (currentStrSet, i) => {
    let set = new Set(currentStrSet)
    if (i === arrRes.length) {
      maxLen = Math.max(maxLen, set.size)
      return;
    }
    dfs(set, i+1);
    for (const item of arrRes[i]) {
      if (set.has(item)) {
        return;
      }
      set.add(item)
    }
    dfs(set, i+1);
  }
  dfs(new Set(), 0)
  return maxLen;
};

function deleSame(arr) {
  // 去除单项有重复的
  const result = [];
  for (const item of arr) {
    if (new Set(item).size === item.length) {
      result.push(item);
    }
  }
  return result;
}
console.log(maxLength(['a', 'b', 'c', 'bcd'])) // 4
console.log(maxLength(["un","iq","ue"])) // 4
console.log(maxLength(["cha","r","act","ers"])) // 6
console.log(maxLength(["abcdefghijklmnopqrstuvwxyz"])) // 26
console.log(maxLength(["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p"])) // 16

2021.12.1

261. 组合总数

// 9个数中选择K个数
var combinationSum3 = function(k, n) {
  const result = [];
  const dfs = (currArr, sum, start) => {
    debugger
    if (start > 10 || sum > n) {
      return;
    }
    if (currArr.length === k) {
      if (sum === n) {
        result.push(currArr.slice());
      }
      return;
    }
    for (let i = start; i < 10; i++) {
      if (sum + i > n || currArr.length + 1 > k) {
        break;
      }
      currArr.push(i);
      dfs(currArr, sum + i, i + 1);
      currArr.pop();
    }
    return;
  }
  dfs([], 0, 1);
  return result;
};
console.log(combinationSum3(3, 7)) //  [[1,2,4]]
console.log(combinationSum3(3, 9)) //  [[1,2,6], [1,3,5], [2,3,4]]
console.log(combinationSum3(8, 36)) //  [1,2,3,4,5,6,7,8]
console.log(combinationSum3(9, 45)) //  [1,2,3,4,5,6,7,8,9]

2021.12.2

1286. 字母组合迭代器

var CombinationIterator = function(characters, combinationLength) {
  this.characters = characters;
  this.combinationLength = combinationLength;
  this.currentStartIndex = 0;
  // 所有组合
  this.allList = [];
  const dfs = (start, currStr) => {
    if (start > characters.length || currStr.length > combinationLength) {
      return;
    }
    if (currStr.length === combinationLength) {
      this.allList.push(currStr);
      return;
    }
    for (let i = start; i < characters.length + 1; i++) {
      dfs(i + 1, currStr + characters[i]);
    }
  }
  dfs(0, '');
};

/**
 * @return {string}
 */
CombinationIterator.prototype.next = function() {
  if (this.currentStartIndex < this.allList.length && this.allList.length > 0) {
    this.currentStartIndex += 1;
    return this.allList[this.currentStartIndex - 1];
  }
  return null;
};

/**
 * @return {boolean}
 */
CombinationIterator.prototype.hasNext = function() {
  return this.allList.length > 0 && this.currentStartIndex < this.allList.length;
};

const com = new CombinationIterator('abc', 2);
console.log(com.next())
console.log(com.hasNext())
console.log(com.next())
console.log(com.hasNext())
console.log(com.next())
console.log(com.hasNext())
console.log(com.next())

1415. 长度为 n 的开心字符串中字典序第 k 小的字符串

var getHappyString = function(n, k) {
  const result = [];
  const strList = ['a', 'b', 'c'];
  const dfs = (currStr, lastStr) => {
    if (result.length === k) {
      return true;
    }
    if (currStr.length === n) {
      result.push(currStr);
      return;
    }
    for (let i = 0; i < strList.length; i++) {
      if (lastStr === strList[i]) {
        continue;
      }
      if (dfs(currStr + strList[i], strList[i])) {
        return true;
      }
    }
  }
  dfs('', '');
  debugger
  if (result[k - 1]) {
    return result[k - 1];
  }
  return '';
};
console.log(getHappyString(1, 3))  // c ["a", "b", "c"]
console.log(getHappyString(3, 9))  //cab  ["aba", "abc", "aca", "acb", "bab", "bac", "bca", "bcb", "cab", "cac", "cba", "cbc"]
console.log(getHappyString(2, 7))  // ''
console.log(getHappyString(10, 100))  // abacbabacb

2021.12.6

2048. 下一个更大的数值平衡数

var nextBeautifulNumber = function(n) {
  const map = new Map();
  while (true) {
    n++;
    if (isBalanceNum(n)) {
      return n;
    }
  }
};
function isBalanceNum(n) {
  const str = `${n}`;
  const map = new Map();
  for (const item of str) {
    if (map.get(item)) {
      map.set(item, map.get(item) + 1);
    } else {
      map.set(item, 1);
    }
  }
  for (const key of map.keys()) {
    if (key !== `${map.get(key)}`) {
      return false;
    }
  }
  return true;
}
console.log(nextBeautifulNumber(0)) // 1
console.log(nextBeautifulNumber(1)) // 22
console.log(nextBeautifulNumber(1000)) // 1333
console.log(nextBeautifulNumber(3000)) // 3133

842. 将数组拆分成斐波那契序列

var splitIntoFibonacci = function(num) {
  let result1 = [];
  const dfs = (start, result) => {
    if (start === num.length && result.length > 2) {
      result1 = [...result];
      return;
    }
    for (let i = 1; i < num.length - start + 1; i++) {
      const currNum = num.substr(start, i);
      if (!isMatchNum(currNum)) {
        continue;
      }
      if (result.length >= 2) {
        const preNum = result[result.length - 1] - 0;
        const prePreNum = result[result.length - 2] - 0;
        if (prePreNum + preNum < currNum) {
          break;
        } else if(prePreNum + preNum > currNum) {
          continue;
        } else {
          result.push(currNum);
          dfs(start + i, result);
          result.pop();
          continue;
        }
      }
      result.push(currNum);
      dfs(start + i, result);
      result.pop();
    }
  }
  dfs(0, [])
  if (result1.length > 2) {
    return result1;
  }
  return [];
};
function isMatchNum(num) {
  if (num.length === 1) {
    return true;
  }
  return !num.startsWith('0') && num - 0 <= Math.pow(2, 31) - 1;
}
// console.log(splitIntoFibonacci('123456579')) // [123,456,579]
// console.log(splitIntoFibonacci('11235813')) // [1,1,2,3,5,8,13]
// console.log(splitIntoFibonacci('112358130')) // []
// console.log(splitIntoFibonacci('0123')) // []
// console.log(splitIntoFibonacci('1101111')) // [11,0,11,11]
// console.log(splitIntoFibonacci('539834657215398346785398346991079669377161950407626991734534318677529701785098211336528511')) // []

131. 分割回文串

var partition = function(s) {
  const result = [];
  const dfs = (start, arr) => {
    if (start >= s.length) {
      result.push([...arr])
      return;
    }
    for (let i = 1; i < s.length - start + 1; i++) {
      let currStr = s.substr(start, i);
      if (isMathStr(currStr)) {
        arr.push(currStr);
        dfs(start + i, arr);
        arr.pop();
      }
    }
  }
  dfs(0, []);
  return result;
};
function isMathStr(str) {
  let start = 0;
  let end = str.length - 1;
  while (start < end) {
    if (str[start] !== str[end]) {
      return false;
    }
    start++;
    end--;
  }
  return true;
}
// console.log(partition('aab')) // [["a","a","b"],["aa","b"]]
// console.log(partition('a')) // [["a"]]
// console.log(partition('abc')) // [["a", "b", "c"]]

2021.12.9

1849. 将字符串拆分成递减的连续值

var splitString = function(s) {
  const arr = [];
  const dfs = (start, preCurrNum) => {
    if (start >= s.length) {
      return true;
    }
    for (let i = 1; i < s.length - start + 1; i++) {
      const currNum = s.substr(start, i);
      if (currNum - 0 > preCurrNum - 0 - 1) {
        break;
      } else if (currNum - 0 < preCurrNum - 0 - 1) {
        continue;
      } else {
        if (dfs(start + i, currNum)) {
          return true
        };
      }
    }
  }
  for (let i = 1; i < s.length; i ++) {
    let preCurrNum = s.substr(0, i);
    if (dfs(i, preCurrNum)) {
      return true;
    };
  }
  return false;
};
console.log(splitString("1")) // false
console.log(splitString("1234")) // false
console.log(splitString("050043")) // true
console.log(splitString("9080701")) // false
console.log(splitString("10009998")) // true

2021.12.14

78. 子集

var subsets = function(nums) {
  const result = [];
  const dfs = (start, size, arr) => {
    if (start.length >= nums.length) {
      return;
    }
    if (arr.length === size) {
      result.push([...arr]);
      return;
    }
    for (let i = start; i < nums.length; i++) {
      if (arr.length < size) {
        dfs(i + 1, size, [...arr, nums[i]]);
      }
    }
  }
  for (let i = 0; i < nums.length + 1; i++) {
    dfs(0, i, []);
  }
  return result;
};

console.log(subsets([1,2,3]));  // [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
console.log(subsets([1,2])); // [[],[1],[2],[1,2]]
console.log(subsets([1])); // [[],[1]]

2021.12.15

90. 子集II

var subsetsWithDup = function(nums) {
 const result = [];
  nums.sort((a, b) => a - b);
 const numSet = new Set();
 const dfs = (start, size, arr) => {
   debugger
   if (start > nums.length) {
     return;
   }
   if (arr.length === size) {
     const str = arr.join('');
     if (numSet.has(str)) {
       return;
     }
     numSet.add(str)
     result.push([...arr]);
     return;
   }
   for (let i = start; i < nums.length; i++) {
     if (arr.length < size) {
       dfs(i + 1, size, [...arr, nums[i]]);
     }
   }
 }
  for (let i = 0; i < nums.length + 1; i++) {
    dfs(0, i, [])
  }
  return result;
};
console.log(subsetsWithDup([1,2,2]))
console.log(subsetsWithDup([0]))
console.log(subsetsWithDup([4,4,4,1,4]))  // [[],[1],[1,4],[1,4,4],[1,4,4,4],[1,4,4,4,4],[4],[4,4],[4,4,4],[4,4,4,4]]

2021.12.22

37. 解数独

var solveSudoku = function(board) {
  const rowVisited = new Array(9).fill().map(() => new Array(10).fill(false)); // 记录行的值
  const columnVisited = new Array(9).fill().map(() => new Array(10).fill(false)); // 记录列的值
  const gridVisited = new Array(9).fill().map(() => new Array(10).fill(false)); // 记录3x3单元格的值
  let emptyNums = []; // 记录空白处的位置
  for (let column = 0; column < board.length; column++) {
    for (let row = 0; row < board[column].length; row++) {
      if (board[column][row] === '.') {
        emptyNums.push([column, row]);
        continue;
      }
      const numIndex = board[column][row];
      rowVisited[column][numIndex] = true;
      columnVisited[row][numIndex] = true;
      const grid = getGrid(column, row);
      gridVisited[grid][numIndex] = true;
    }
  }
  let result = false;
  const dfs = (board, start) => {
    if (start === emptyNums.length) {
      return true;
    }
    const column = emptyNums[start][0];
    const row = emptyNums[start][1];
    const grid = getGrid(column, row);
    for (let i = 1; i < 10; i++) {
      if (!isVisited(rowVisited, columnVisited, gridVisited, column, row, grid, i)) {
        rowVisited[column][i] = true;
        columnVisited[row][i] = true;
        gridVisited[grid][i] = true;
        board[column][row] = `${i}`
        if (dfs(board, start + 1)) {
          return true;
        };
        rowVisited[column][i] = false;
        columnVisited[row][i] = false;
        gridVisited[grid][i] = false;
        board[column][row] = '.';
      }
    }
  }
  dfs(board, 0);
  return board;
};

function isVisited(rowVisited, columnVisited, gridVisited, column, row, grid, i) {
  return rowVisited[column][i] || columnVisited[row][i] || gridVisited[grid][i];
}
function getGrid(column, row) {
  if (column <= 2 && row <= 2) {
    return 0;
  }

  if (column <= 2 && row > 2 && row <= 5) {
    return 1;
  }

  if (column <= 2 && row > 5 && row <= 8) {
    return 2;
  }

  if (column > 2 && column <= 5 && row <= 2) {
    return 3;
  }

  if (column > 2 && column <= 5 && row > 2 && row <= 5) {
    return 4;
  }

  if (column > 2 && column <= 5 && row > 5 && row <= 8) {
    return 5;
  }

  if (column > 5 && column <= 8 && row <= 2) {
    return 6;
  }

  if (column > 5 && column <= 8 && row > 2 && row <= 5) {
    return 7;
  }

  if (column > 5 && column <= 8 && row > 5 && row <= 8) {
    return 8;
  }
}

console.log(solveSudoku( [["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]]))

2021.12.23

51. N皇后

// 坐标是否在同一斜线上:
  // 1. 向右的斜线:同一斜线上的横坐标 - 纵坐标的值是一样的
  // 2. 向左的斜线:同一斜线上的横坐标 + 纵坐标的和是一样的
var solveNQueens = function(n) {
   // const board = new Array(n).fill().map(() => new Array(n).fill('.'));
  const rowVisited = new Array(n).fill(false); // 记录行
  const columnVisited = new Array(n).fill(false); // 记录列
  const lineRightVisited = new Set(); // 记录像右的斜线坐标差的值
  const lineLeftVisted = new Set(); // 记录像左的斜线坐标和值
  const result = [];
  const dfs = (column, board) => {
    debugger;
    if (column >= n) {
      result.push(getResult(board));
    }
    for (let row = 0; row <= n; row++) {
      const value = row - column;
      const sum = row + column;
      if (rowVisited[column] === false && columnVisited[row] === false
        && !lineLeftVisted.has(sum) && !lineRightVisited.has(value)) {
        rowVisited[column] = true;
        columnVisited[row] = true;
        board[column][row] = 'Q';
        lineLeftVisted.add(sum);
        lineRightVisited.add(value);
        dfs(column + 1, board)
        rowVisited[column] = false;
        columnVisited[row] = false;
        board[column][row] = '.';
        lineLeftVisted.delete(sum);
        lineRightVisited.delete(value);
      }
    }
  }
  dfs(0, new Array(n).fill().map(() => new Array(n).fill('.')));
  return result;
};
function getResult(board) {
  const result = [];
  for (const item of board) {
    result.push(item.join(''));
  }
  return result;
}
console.log(solveNQueens(1));
console.log(solveNQueens(2));
console.log(solveNQueens(3));
console.log(solveNQueens(4));

52. N皇后II

var totalNQueens = function(n) {
  const rowVisited = new Array(n).fill(false);
  const columnVisited = new Array(n).fill(false);
  const leftLineVisited = new Set();
  const rightLineVisited = new Set();
  let result = 0;
  const dfs = column => {
    if (column >= n) {
      result += 1;
    }
    for (let row = 0; row < n; row++) {
      if (!rowVisited[row] && !columnVisited[column]
        && !rightLineVisited.has(row - column) && !leftLineVisited.has(column + row)) {
        rowVisited[row] = true;
        columnVisited[column] = true;
        rightLineVisited.add(row - column);
        leftLineVisited.add(row + column);
        dfs(column + 1);
        rowVisited[row] = false;
        columnVisited[column] = false;
        rightLineVisited.delete(row - column);
        leftLineVisited.delete(row + column);
      }
    }
  }
  dfs(0);
  return result;
};
console.log(totalNQueens(1));
console.log(totalNQueens(2));
console.log(totalNQueens(3));
console.log(totalNQueens(4));

2021.12.26

113. 路径总和
(1)解一

var pathSum = function(root, targetSum) {
    const result = [];
    const visited = new Set();
    const dfs = (node, sum, res) => {
        if (!node.left && !node.right && sum === targetSum) {
            result.push([...res]);
            return;
        }
        for (let i = 0; i < 2; i++) {
            const nextNode = getDirection(node, i);
            if (nextNode && !visited.has(nextNode)) {
                visited.add(nextNode);
                res.push(nextNode.val);
                sum += nextNode.val;
                dfs(nextNode, sum, res);
                visited.delete(nextNode);
                res.pop();
                sum -= nextNode.val;
            }
        }
    }
    if (root) {
        visited.add(root);
        dfs(root, root.val, [root.val], false);
    }
    return result;
};

function getDirection(node, val) {
    if (val === 0) {
        return node.left;
    } else {
        return node.right;
    }
}

(2)解二

var pathSum1 = function(root, targetSum) {
    const result = [];
    const visited = new Set();
    const dfs = (node, sum, res) => {
        if (!node.left && !node.right && sum === targetSum) {
            result.push([...res]);
            return;
        }
        if (node.left && !visited.has(node.left)) {
            res.push(node.left.val);
            sum += node.left.val;
            dfs(node.left, sum, res);
            res.pop();
            sum -= node.left.val;
        }
        if (node.right && !visited.has(node.right)) {
            res.push(node.right.val);
            sum += node.right.val;
            dfs(node.right, sum, res);
            res.pop();
            sum -= node.right.val;
        }
    }
    if (root) {
        visited.add(root);
        dfs(root, root.val, [root.val], false);
    }
    return result;
};

剑指 Offer II 110. 所有路径

var allPathsSourceTarget = function(graph) {
    const visited = new Array(graph.length).fill(false);
    const result = [];
    const dfs = (nodeIndex, res) => {
        if (nodeIndex === graph.length - 1) {
            result.push([...res]);
            return;
        }
        for (const nextNodeIndex of graph[nodeIndex]) {
            if (visited[nodeIndex] === false) {
                visited[nodeIndex] = true;
                dfs(nextNodeIndex, [...res, nextNodeIndex]);
                visited[nodeIndex] = false;
            }
        }
    }
    dfs(0, [0]);
    return result;
};
console.log(allPathsSourceTarget([[1,2],[3],[3],[]]));  // [[0,1,3],[0,2,3]]
console.log(allPathsSourceTarget([[4,3,1],[3,2,4],[3],[4],[]]));  // [[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
console.log(allPathsSourceTarget([[1],[]]));  // [[0,1]]
console.log(allPathsSourceTarget([[1,2,3],[2],[3],[]]));  // [[0,1,2,3],[0,2,3],[0,3]]
console.log(allPathsSourceTarget([[1,3],[2],[3],[]]));  // [[0,1,2,3],[0,3]]

2021.12.28

95. 不同的二叉搜索树 II

function TreeNode(val, left, right) {
  this.val = (val === undefined ? 0 : val);
  this.left = (left === undefined ? null : left);
  this.right = (right === undefined ? null : right);
}
/*
如果我们枚举根节点的值为 i,
那么根据二叉搜索树的性质我们可以知道左子树的节点值的集合为[1…i−1],
 右子树的节点值的集合为 [i+1…n]
 */
var generateTrees = function(n) {
  const getAllTress = (start, end) => {
    if (start > end) {
        return [null];
      }
    const allTrees = [];
      // 枚举所有的根节点
    for (let i = start; i <= end; i++) {
      // 获取所有左子树
      const allLeftTrees = getAllTress(start, i - 1);
      // 获取所有右子树
      const allRightTrees = getAllTress(i + 1, end);
      // 从左子树和右子树中选择一颗放到根节点
      for (const left of allLeftTrees) {
        for (const right of allRightTrees) {
          const root = new TreeNode(i);
          root.left = left;
          root.right = right;
          allTrees.push(root);
        }
      }
    }
    return allTrees;
  };
  return getAllTress(1, n);
};

// console.log(generateTrees(1));
// console.log(generateTrees(2));
console.log(generateTrees(3));

2021.12.30

509. 斐波那契数
解一

var fib = function(n) {
  if(n <= 1) {
    return n;
  }
  if (n === 2) {
    return 1;
  }
  return fib(n-1) + fib(n-2);
};

解二

var fib = function(n) {
  if(n <= 1) {
    return n;
  }
  if (n === 2) {
    return 1;
  }
  let f1 = 1;
  let f2 = 1;
  let f3 = f1 + f2;
  for (let i = 4; i <= n; i++) {
    f1 = f2;
    f2 = f3;
    f3 = f1 + f2;
  }
  return f3;
};

98. 验证二叉搜索树

function TreeNode(val, left, right) {
 this.val = (val===undefined ? 0 : val);
 this.left = (left===undefined ? null : left);
 this.right = (right===undefined ? null : right)
}
const root1 = new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6)))
const root2 = new TreeNode(2, new TreeNode(1), new TreeNode(3));
var isValidBST = function(root) {
  // root为根。root值小于minNum值即左子树的值就false,root值大于maxNum值即右子树的值就false
  // 前序遍历
  const dfs = (root, minNum, maxNum) => {
    if (!root) {
      return true;
    }
    if (root.val <= minNum || root.val >= maxNum) {
      return false;
    }
    const leftRes = dfs(root.left, minNum, root.val);
    const RightRes = dfs(root.right, root.val, maxNum);
    return leftRes && RightRes;
  };
  return dfs(root);
};
console.log(isValidBST(root1));
console.log(isValidBST(root2));
console.log(isValidBST(new TreeNode(5,
  new TreeNode(4), new TreeNode(6,
    new TreeNode(3), new TreeNode(7)))));

99. 恢复二叉搜索树

// 搜索二叉树中序遍历的值为递增,把中序遍历的节点放入一个数组,相当于一个递增的数组中有两个数交换了位置,找到这两个数的位置
var recoverTree = function(root) {
  // 中序遍历
  const nodeList = [];
  const dfs = (node) => {
    if (!node) {
      return;
    }
    if(node.left) {
      dfs(node.left);
    }
    nodeList.push(node);
    if (node.right) {
      dfs(node.right)
    }
  };
  dfs(root);
  // 找出nodeList不符合递增的两个节点将两节点值换一下
  const nodes = getNodeList(nodeList);
  if (nodes[0] && nodes[1]) {
    const val = nodes[1].val;
    nodes[1].val = nodes[0].val;
    nodes[0].val = val;
  }
  return root;
};
function getNodeList(nodes) {
  let node1 = null;
  let node2 = null;
  for (let i = 0; i < nodes.length - 1; i++) {
    if (nodes[i].val > nodes[i+1].val) {
      node1 = nodes[i+1];
      if (!node2) {
        node2 = nodes[i];
      }
    }
  }
  return [node1, node2];
}
// [3,1,4,null,null,2]
console.log(recoverTree(new TreeNode(3, new TreeNode(1), new TreeNode(4, new TreeNode(2)))));

94. 二叉树的中序遍历

var inorderTraversal = function(root) {
  const result = [];
  const dfs = (node) => {
    if (!node) {
      return;
    }
    if (node.left) {
      dfs(node.left);
    }
    result.push(node.val);
    if (node.right) {
      dfs(node.right);
    }
  };
  dfs(root);
  return result;
};

144. 二叉树的前序遍历

var preorderTraversal = function(root) {
  const result = [];
  const dfs = (node) => {
    if (!node) {
      return;
    }
    result.push(node.val);
    dfs(node.left);
    dfs(node.right);
  };
  dfs(root);
  return result;
};

145. 二叉树的后序遍历

var postorderTraversal = function(root) {
  const result = [];
  const dfs = node => {
    if (!node) {
      return;
    }
    dfs(node.left);
    dfs(node.right);
    result.push(node.val);
  }
  dfs(root)
  return result;
};

2021.12.31

116. 填充每个节点的下一个右侧节点指针
解一

function Node(val, left, right, next) {
 this.val = val === undefined ? null : val;
 this.left = left === undefined ? null : left;
 this.right = right === undefined ? null : right;
 this.next = next === undefined ? null : next;
};

var connect = function(root) {
  // 一层一层遍历出节点 ,用一个数组,当遍历到一个元素时候将其左右节点都放进去,每次取第一个,直到数组为空
  if (!root) {
    return root;
  }
  const allNodes = [];
  const dfs = nodes => {
    if (nodes.length === 0) {
      return;
    }
    const currentNode = nodes.shift();
    allNodes.push(currentNode);
    if (currentNode.left && currentNode.right) {
      dfs([...nodes, currentNode.left, currentNode.right]);
    } else {
      dfs([...nodes]);
    }
  };
  dfs([root]);
  // 分出将同一层次的节点,完全二叉树一共有n个节点,那么就有看(log2 n) + 1层,第m层就有2^(m-1)个节点
  const nodeSize = allNodes.length;
  const allLevel = Math.log2(nodeSize) + 1;
  let preSum = 0;
  for (let i = 1; i < allLevel + 1; i++) {
    const start = preSum;
    const size = Math.pow(2, i - 1);
    preSum += size;
    // 同一个数组中第i个节点指向第i+1个节点
    convertNextNode(allNodes.slice(start, start + size));
  }
  return root;
};

function convertNextNode(nodeList) {
  for (let i = 0; i < nodeList.length - 1; i++) {
    nodeList[i].next = nodeList[i+1];
  }
}

解二

/*
层次遍历基于广度优先搜索,它与广度优先搜索的不同之处在于,
广度优先搜索每次只会取出一个节点来拓展,而层次遍历会每次将队列中的所有元素都拿出来拓展,
这样能保证每次从队列中拿出来遍历的元素都是属于同一层的
因此我们可以在遍历的过程中修改每个节点的 next 指针,同时拓展下一层的新队列
*/
var connect2 = function(root) {
  if (!root) {
    return root;
  }
  const nodeList = [root];
  while (nodeList.length > 0) {
    // 记录当前队列大小
    const nodeSize = nodeList.length;
    // 遍历这一层的所有节点并处理next
    for (let i = 0; i < nodeSize; i++) {
      // 取出队首的节点
      const node = nodeList.shift();
      // 如果不是最后一个节点,指向当前队列的第一个节点
      if (i < nodeSize - 1) {
        node.next = nodeList[0];
      }
      // 将左右节点加进队列
      if (node.left) {
        nodeList.push(node.left);
      }
      if (node.right) {
        nodeList.push(node.right);
      }
    }
  }
  return root;
};

解三

var connect3 = function(root) {
  if (!root) {
    return root;
  };
  const dfs = (nodeList) => {
    if (nodeList.length === 0) {
      return;
    }
    const size = nodeList.length;
    for (let i = 0; i < size; i++) {
      const node = nodeList.shift();
      if (i < size -1) {
        node.next = nodeList[0];
      }
      if (node.left) {
        nodeList.push(node.left);
      }
      if (node.right) {
        nodeList.push(node.right);
      }
    }
    dfs(nodeList)
  }
  dfs([root]);
  return root;
}

129. 求根节点到叶节点数字之和

function TreeNode(val, left, right) {
  this.val = (val===undefined ? 0 : val);
  this.left = (left===undefined ? null : left);
  this.right = (right===undefined ? null : right)
}

var sumNumbers = function(root) {
  if (!root) {
    return 0;
  }
  let result = 0;
  const visited = new Set();
  const dfs = (currStr, node) => {
    if (!node.left && !node.right) {
      result += (currStr - 0);
      return;
    }
    if (node.left && !visited.has(node.left)) {
      visited.add(node.left);
      dfs(`${currStr}${node.left.val}`, node.left);
      visited.delete(node.left);
    }
    if (node.right && !visited.has(node.right)) {
      visited.add(node.right);
      dfs(`${currStr}${node.right.val}`, node.right);
      visited.delete(node.right);
    }
  };
  dfs(`${root.val}`, root);
  return result;
};

console.log(sumNumbers(new TreeNode(1, new TreeNode(2), new TreeNode(2))));

2022.1.1

560. 和为 K 的子数组
解一

// 暴力解法
var subarraySum = function(nums, k) {
    let result = 0;
    for(let i = 0; i < nums.length; i++) {
        let sum = 0;
        for (let j = i; j < nums.length; j++) {
            sum += nums[j];
            if (sum === k) {
                result++;
            }
        }
    }
    return result;
};

解二

// j...i这个区间的和未K,那么前缀和preNums[i] - preNums[j - 1] = k
// 那么preNums[j - 1] = preNums[i] - k; 其中0 <= j <= i,
// 记录一个map, key为前缀和的值,value为个数;遍历数组,到i的时候就可以判断前面有多少个符合条件的j
var subarraySum = function(nums, k) {
    const sizeMap = new Map();
    sizeMap.set(0, 1);  //  默认第一个元素之前的前缀和为0
    let result = 0;
    let sum = 0;
    for (const item of nums) {
        sum += item;
        if (sizeMap.get(sum - k)) {
            result += sizeMap.get(sum - k);
        }
        if (sizeMap.get(sum)) {
            sizeMap.set(sum, sizeMap.get(sum) + 1);
        } else {
            sizeMap.set(sum, 1);
        }
    }
    return result;
};
console.log(subarraySum([1,1,1], 2))
// console.log(subarraySum([1,2,3], 3))
// console.log(subarraySum([1], 0))
// console.log(subarraySum([1,1, 1, 2, 3], 3))
// console.log(subarraySum([1], 1))
console.log(subarraySum([1,-1,0, 1, 0], 0))

102. 二叉树的层序遍历

 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 {number[][]}
 */
var levelOrder = function(root) {
    const result = [];
    if (!root) {
        return result;
    }
    const dfs = (nodeList, arr) => {
        if (nodeList.length === 0) {
            return;
        }
        const copyNodeList = [];
        for (const node of nodeList) {
            arr.push(node.val);
            if (node.left) {
                copyNodeList.push(node.left);
            }
            if (node.right) {
                copyNodeList.push(node.right);
            }
        }
        if (arr.length > 0) {
            result.push([...arr])
        }
        dfs(copyNodeList, []);
    }
    dfs([root], [])
    return result;
};
console.log(levelOrder(new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3))))

2022.1.2

236. 二叉树的最近公共祖先
解一

//  找出两条路径,再找出其最后一个交点;(最后两个用例溢出)
function TreeNode(val, left, right) {
    this.val = val;
    this.left = left;
    this.right = right;
 }
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
    if (p === q) {
        return q;
    }
    let resultP = findMinPath(root, p);
    let resultQ = findMinPath(root, q);
    // 找相交点
    let start = 0;
    let result = null;
    while (resultP.length && resultQ.length) {
        const pRes = resultP.shift();
        const qRes = resultQ.shift();
        if (pRes === qRes) {
            result = pRes;
        }
    }
    return result;
};
function findMinPath(root, nodeRes) {
    const visited = new Set();
    let result = [];
    const dfs = (arr, node) => {
        if (node === nodeRes) {
            if (arr.length < result.length || result.length === 0) {
                result = [...arr];
            }
            return;
        }
        if (node.left && !visited.has(node.left)) {
            visited.add(node.left);
            dfs([...arr, node.left], node.left);
            visited.delete(node.left)
        }
        if (node.right && !visited.has(node.right)) {
            visited.add(node.right);
            dfs([...arr, node.right], node.right);
            visited.delete(node.right)
        }
    }
    dfs([root], root);
    return result;
}
const root1 = new TreeNode(3, null, new TreeNode(1, new TreeNode(0), new TreeNode(8)));
const node4 = new TreeNode(4);
const node5 = new TreeNode(5, new TreeNode(6), new TreeNode(2, new TreeNode(7), node4));
root1.left = node5;
// console.log(JSON.stringify(root1))
console.log(lowestCommonAncestor(root1, node5, node4))

解二

// 1。 hasLPQ表示当前节点的左子树是否包含p,或q,hasRPQ表示当前节点的又子树是否包含p 或 q,
// 如果当前解点的左子树以及右子树都包含p或q,那么当前节点就是最近的公共祖先节点
// 2。如果当前节点是q或者是q,以及当前的左子树或右子树中有另外一个节点,那么当前节点是最近的公共祖先节点
var lowestCommonAncestor1 = function(root, p, q) {
    // 如果其中一个节点等于root,那么最近的公共祖先节点就是root
    if (p === root || q === root) {
        return root;
    }
    let result;
    const dfs = (currNode) => {
        if (!currNode) {
            return false;
        }
        // 当前节点的左子树是否包含q或p
        const hasLPQ = dfs(currNode.left);
        // 当前节点的右子树是否包含p或q
        const hasRPQ = dfs(currNode.right);
        // 如果左右子树都包含p或q,那么当前节点就是祖先节点
        if (hasLPQ && hasRPQ) {
            result = currNode;
            return true;
        }
        // 当前节点是p或q,且当前节点的左子树或右子树包含另外一个节点,那么当前节点就是祖先节点
        if ((currNode === p || currNode === q) && (hasLPQ || hasRPQ)) {
            result = currNode;
            return true;
        }
        return (hasLPQ || hasRPQ) || (currNode === p || currNode === q);
    }
    dfs(root);
    return result;
};

146. LRU 缓存
解一

// 使用数组,(最后俩用例超时)

var LRUCache = function(capacity) {
    this.size = capacity;
    this.chacheMap = new Map();
    // 数组前面的不常使用,溢出时将被删除
    this.chacheKey = [];
};

/**
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if(this.chacheMap.get(key) || this.chacheMap.get(key) === 0) {
        // 更新当前位置
        const index = this.chacheKey.indexOf(key);
        this.chacheKey.splice(index, 1);
        this.chacheKey.push(key);
        return this.chacheMap.get(key);
    }
    return -1;
};

/**
 * @param {number} key
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    if(this.chacheMap.size === this.size
        && (this.chacheMap.get(key) === null || this.chacheMap.get(key) === undefined)) {
        // 超限新增,先删除一个
        const delKey = this.chacheKey.shift();
        this.chacheMap.delete(delKey);
    }
    // 更新先删除原来的位置把其放到最后一个去
    if (this.chacheMap.get(key) || this.chacheMap.get(key) === 0) {
        const index = this.chacheKey.indexOf(key);
        this.chacheKey.splice(index, 1);
    }
    this.chacheKey.push(key);
    this.chacheMap.set(key, value);
};

解二

// 使用双向链表 + map
// 1. 创建一个map,该map的key是新增的key,value是链表的节点,该节点包括key , val,以及pre,next
// 2. 创建一个空head和空end,确保链表不为空,能随时获取头和尾部,所有节点都放在他们中间
// 3. 新增一个节点,将节点添加到最后,也就是end的前面,储存该节点进map,判断是否超限,超限则将链表的第一个节点删除,也就是head的next
// 4. 更新一个节点的值,更新缓存,以及将该节点移动到最后
// 5. 获取值,如果没有返回-1, 有的话先讲该节点移动到最后,再将其值返回
class LRUCache {
    constructor(capacity) {
        this.capacity = capacity;
        this.head = new ListNode(); // 双向链表的头  用连个空节点链接,保证这个链表不为空
        this.end = new ListNode(); // 双向链表的尾
        this.head.next = this.end;
        this.end.pre = this.head;
        this.nodeListMap = new Map();  // key为put的key,value为一个节点
    }

    get(key) {
        if (this.nodeListMap.get(key)) {
            // 移动其位置到最后一个去
            this.moveNodeToEnd(this.nodeListMap.get(key));
            return this.nodeListMap.get(key).val;
        }
        return -1;
    }

    put(key, value) {
        if (this.nodeListMap.get(key)) {
            // 更新
            const node = this.nodeListMap.get(key);
            node.val = value;
            // 将该节点移动尾部,也就是end的前面
            this.moveNodeToEnd(node);
        } else {
            // 新增
            const newNode = new ListNode(value, key);
            this.addNodeToEnd(newNode);
            // 加入map缓存
            this.nodeListMap.set(key, newNode);
            // 超过容量,从头删除一个
            if (this.nodeListMap.size > this.capacity) {
                this.deleteFromHead();
            }
        }
    }

    deleteNode(node) {
        // 删除节点
        const preNode = node.pre;
        const nextNode = node.next;
        preNode.next = nextNode;
        nextNode.pre = preNode;
    }

    addNodeToEnd(node) {
        // 将节点添加到链表尾部
        node.pre = this.end.pre;
        this.end.pre.next = node;
        node.next = this.end;
        this.end.pre = node;
    }

    moveNodeToEnd(node) {
        // 从链表中删除
        this.deleteNode(node)
        // 追加到链表末尾(this.end的前面)
        this.addNodeToEnd(node);
    }

    deleteFromHead() {
        // 通过head拿出第一个节点
        const node = this.head.next;
        this.deleteNode(node);
        // 从缓存中删除
        this.nodeListMap.delete(node.key);
    }
}
function ListNode(val, key, pre, next) {
    this.val = val;
    this.key = key;
    this.pre = pre ? pre : null;
    this.next = next ? next : null;
}

2022.1.7

114. 二叉树展开为链表

const flatten = function (root) {
  const nodes = [];
  const dfs = (currNode) => {
    if (!currNode) {
      return null;
    }
    nodes.push(currNode);
    dfs(currNode.left);
    dfs(currNode.right);
  };
  dfs(root);
  for (let i = 0; i < nodes.length - 1; i++) {
    nodes[i].right = nodes[i + 1];
    nodes[i].left = null;
  }
  return root;
};

105. 从前序与中序遍历序列构造二叉树

// 前序遍历 :根->左->右, 中序遍历: 左 -> 根 ->右
// 前: [[根节点] [所有左子树节点] [所有右子树节点]]  可以得到第一个节点就是根节点
// 中: [[所有左子树节点] [根节点] [所有右子树节点]] 可以根据根节点得到左子树节点个数和右子树节点个数
const buildTree = function (preorder, inorder) {
  const dfs = (root, preLeftNodes, preRightNodes, inListLeft, inListRight) => {
    if (preLeftNodes.length === 0 && preRightNodes.length === 0) {
      return;
    }
    if (preLeftNodes.length) {
      const leftNode = new TreeNode(preLeftNodes.shift());
      root.left = leftNode;
      const nodeListLeft = getLeftAndRightNodes(leftNode.val, preLeftNodes, inListLeft);
      dfs(leftNode, nodeListLeft[0], nodeListLeft[1], nodeListLeft[2], nodeListLeft[3]);
    }

    if (preRightNodes.length) {
      const rightNode = new TreeNode(preRightNodes.shift());
      root.right = rightNode;
      const nodeListRight = getLeftAndRightNodes(rightNode.val, preRightNodes, inListRight);
      dfs(rightNode, nodeListRight[0], nodeListRight[1], nodeListRight[2], nodeListRight[3]);
    }
  };
  // 前序遍历的第一个节点为根节点
  const root = new TreeNode(preorder.shift());
  const leftAndRightNodes = getLeftAndRightNodes(root.val, preorder, inorder);
  dfs(root, leftAndRightNodes[0], leftAndRightNodes[1], leftAndRightNodes[2], leftAndRightNodes[3]);
  return root;
};

function getLeftAndRightNodes(rootVal, preorder, internal) {
  // 根据根节点值
  // 输出 左子树的前序遍历 右子树的前序遍历 左子树的中序遍历 右子树的中序遍历
  const leftIn = [];
  let index = null;
  let left = 0;
  for (let i = 0; i < internal.length; i++) {
    if (internal[i] === rootVal) {
      index = i;
      break;
    }
    left++;
    leftIn.push(internal[i]);
  }
  const rightIn = internal.slice(index + 1);
  const preLftNodes = preorder.slice(0, left);
  const preRightNodes = preorder.slice(left);
  return [preLftNodes, preRightNodes, leftIn, rightIn];
}

130. 被围绕的区域

// 如果O存在边界,那么与它相连的O都不能被填为X
// 遍历所有的边界值,如果该值是O就标记该值为A,并递归其上下左右是个方向上的值是否是O,是O就标记为A
var solve = function(board) {
  // 生成四个方向坐标
  const directions = getDirection();
  // 遍历左右边界
  for (let column = 0; column < board.length; column++) {
    dfs(column, 0, board, directions);
    dfs(column, board[0].length - 1, board, directions);
  }
  // 遍历上下边界
  for (let row = 1; row < board[0].length - 1; row++) {
    dfs(0, row, board, directions);
    dfs(board.length - 1, row, board, directions);
  }
  // 遍历整个二维数组,将标记A的标记为O(标记为A的是没有被包围的X),标记为O的标记为X
  for (let column = 0; column < board.length; column++) {
    for (let row = 0; row < board[0].length; row++) {
      if (board[column][row] === 'A') {
        board[column][row] = 'O';
      } else if (board[column][row] === 'O') {
        board[column][row] = 'X';
      }
    }
  }
  return board;
};
function dfs(column, row, board, directions) {
  if (isNotMatch(column, row, board.length -1, board[0].length -1) || board[column][row] !== 'O') {
    return;
  }
  // 标记该值
  board[column][row] = 'A';
  // 递归其4个方向
  for (const item of directions) {
    dfs(column + item[0], row + item[1], board, directions);
  }
}
function getDirection() {
  return [
    [0, 1],
    [0, -1],
    [1, 0],
    [-1, 0]
  ]
}
function isNotMatch(column, row, maxC, maxR) {
  return column < 0 || row < 0 || column > maxC || row > maxR;
}

139. 单词拆分

const wordBreak = function (s, wordDict) {
  const wordsSet = new Set(wordDict);
  const visited = new Array(s.length); // 记录当前已经可以拼接成功的s的位置
  const dfs = start => {
    if (start >= s.length) {
      return true;
    }
    if (visited[start] !== undefined) {
      return visited[start];
    }
    for (let i = start + 1; i < s.length + 1; i++) {
      const str = s.slice(start, i);
      if (wordsSet.has(str) && dfs(i)) {
        visited[start] = true;
        return true;
      }
    }
    visited[start] = false;
    return false;
  };
  return dfs(0);
};

2022.1.10

739. 每日温度

解一:暴力

const dailyTemperatures = function (temperatures) {
  for (let i = 0; i < temperatures.length; i++) {
    let size = 0;
    let isUp = false;
    for (let j = i + 1; j < temperatures.length; j++) {
      size++;
      if (temperatures[j] > temperatures[i]) {
        isUp = true;
        break;
      }
    }
    if (isUp) {
      temperatures[i] = size;
    } else {
      temperatures[i] = 0;
    }
  }
  return temperatures;
};

解二: 单调栈

// 构建一个单调递减的栈,栈内存入索引和值,
// 1.如果当前元素小于栈顶元素,当前元素入栈
// 2. 如果当前元素大于栈顶元素,栈顶元素出栈,并计算天数:索引之差,如果栈内还有元素继续判断当前元素和栈顶元素,如果没有当前元素入栈
// 3. 遍历完,栈内所有元素值设为0

const dailyTemperatures = function (temperatures) {
  const stack = [];
  for (let i = 0; i < temperatures.length; i++) {
    while (stack.length && temperatures[i] > temperatures[stack[stack.length - 1]]) {
      const index = stack.pop();
      temperatures[index] = i - index;
    }
    if (stack.length === 0) {
      stack.push(i);
      continue;
    }
    if (temperatures[i] <= temperatures[stack[stack.length - 1]]) {
      stack.push(i);
    }
  }
  while (stack.length) {
    temperatures[stack.pop()] = 0;
  }
  return temperatures;
};

2022.1.11

96. 不同的二叉搜索树

const numTrees = function (n) {
  if (n === 0 || n === 1) {
    return 1;
  }
  let res = 0;
  for (let i = 0; i < n; i++) {
    // 当根节点为 i 时候 左子树的数量 * 右子树的数量
    res += numTrees(i) * numTrees(n - i - 1);
  }
  return res;
};

538. 把二叉搜索树转换为累加树

function TreeNode(val, left, right) {
  this.val = (val === undefined ? 0 : val);
  this.left = (left === undefined ? null : left);
  this.right = (right === undefined ? null : right);
}

const convertBST = function (root) {
  // 中序遍历 右 根 左 :降序
  const nodes = [];
  const dfs = (node) => {
    if (!node) {
      return;
    }
    dfs(node.right);
    nodes.push(node);
    dfs(node.left);
  };
  dfs(root);
  let sum = 0;
  for (const node of nodes) {
    sum += node.val;
    node.val = sum;
  }
  return root;
};

101. 对称二叉树

const isSymmetric = function (root) {
  if (!root) {
    return true;
  }
  const isNotSymmetric = (leftArr, rightArr) => {
    if (leftArr.length === 0 && rightArr.length === 0) {
      return false;
    }
    if (leftArr.length !== rightArr.length) {
      return true;
    }
    const left = [];
    const right = [];
    while (leftArr.length) {
      const leftNode = leftArr.shift();
      const rightNode = rightArr.shift();
      if (!leftNode && !rightNode) {
        continue;
      }
      if (leftNode && rightNode && leftNode.val !== rightNode.val) {
        return true;
      }
      if ((leftNode && !rightNode) || (!leftNode && rightNode)) {
        return true;
      }
      left.push(leftNode.left);
      left.push(leftNode.right);
      right.push(rightNode.right);
      right.push(rightNode.left);
    }
    if (isNotSymmetric(left, right)) {
      return true;
    }
  };
  return !isNotSymmetric([root.left], [root.right]);
};

2022.1.12

617. 合并二叉树

function TreeNode(val, left, right) {
  this.val = (val === undefined ? 0 : val);
  this.left = (left === undefined ? null : left);
  this.right = (right === undefined ? null : right);
}
const mergeTrees = function (root1, root2) {
  if (!root1) {
    return root2;
  }
  if (!root2) {
    return root1;
  }
  const root = new TreeNode(root1.val + root2.val);
  root.left = mergeTrees(root1.left, root2.left);
  root.right = mergeTrees(root1.right, root2.right);
  return root;
};

543. 二叉树的直径

function TreeNode(val, left, right) {
  this.val = (val === undefined ? 0 : val);
  this.left = (left === undefined ? null : left);
  this.right = (right === undefined ? null : right);
}

const diameterOfBinaryTree = function (root) {
  if (!root) {
    return 0;
  }
  // 如果以当前节点作为根节点,最长直径就是当前节点左子树的最长路径 + 右子树的最长路径
  const maxRoot = getMaxLen(root.left) + getMaxLen(root.right);
  // 再以左节点、右节点分别作为根节点,最大值作为结果
  const maxLeft = diameterOfBinaryTree(root.left);
  const maxRight = diameterOfBinaryTree(root.right);
  return Math.max(maxRoot, maxLeft, maxRight);
};

function getMaxLen(root) {
  let res = 0;
  const dfs = (node, len, visited) => {
    res = Math.max(res, len);
    if (node.left && !visited.has(node.left)) {
      visited.add(node.left);
      dfs(node.left, len + 1, visited);
      visited.delete(node.left);
    }
    if (node.right && !visited.has(node.right)) {
      visited.add(node.right);
      dfs(node.right, len + 1, visited);
      visited.delete(node.right);
    }
  };
  if (root) {
    const visited = new Set();
    visited.add(root);
    dfs(root, 1, visited);
  }
  return res;
}

448. 找到所有数组中消失的数字

const findDisappearedNumbers = function (nums) {
  const numTemp = new Array(nums.length).fill(false);
  for (const num of nums) {
    numTemp[num - 1] = true;
  }
  const res = [];
  for (let i = 0; i < numTemp.length; i++) {
    if (!numTemp[i]) {
      res.push(i + 1);
    }
  }
  return res;
};

283. 移动零

const moveZeroes = function (nums) {
  const len = nums.length;
  for (let i = 0; i < len; i++) {
    const currNum = nums.shift();
    if (currNum !== 0) {
      nums.push(currNum);
    }
  }
  const resLen = len - nums.length;
  for (let i = 0; i < resLen; i++) {
    nums.push(0);
  }
  return nums;
};

234. 回文链表

function ListNode(val, next) {
  this.val = (val === undefined ? 0 : val);
  this.next = (next === undefined ? null : next);
}
// [1,2,2,1]
const isPalindrome = function (head) {
  let node = head;
  const arr = [];
  while (node) {
    arr.push(node.val);
    node = node.next;
  }
  let start = 0;
  let end = arr.length - 1;
  while (start < end) {
    if (arr[start] !== arr[end]) {
      return false;
    }
    start++;
    end--;
  }
  return true;
};
const head1 = new ListNode(1, new ListNode(2, new ListNode(2, new ListNode(1))));
console.log(isPalindrome(head1));

const head2 = new ListNode(1, new ListNode(2));
console.log(isPalindrome(head2));

226. 翻转二叉树

const invertTree = function (root) {
  if (!root) {
    return root;
  }
  // 递归每一个节点,将当前节点的左右节点进行交换
  const { left } = root;
  const { right } = root;
  root.left = right;
  root.right = left;
  invertTree(left);
  invertTree(right);
  return root;
};

206. 反转链表

const reverseList = function (head) {
  const nodes = [];
  let node = head;
  while (node) {
    nodes.push(node);
    node = node.next;
  }
  while (nodes.length > 1) {
    const node1 = nodes.shift();
    const node2 = nodes.pop();
    const val1 = node1.val;
    node1.val = node2.val;
    node2.val = val1;
  }
  return head;
};

160. 相交链表

解一:

// 先找出两个链表的长度,如果长度差值为n,那么长的先走n步,然后再一起走,它们相等的第一个节点就是相交的起点
const getIntersectionNode1 = function (headA, headB) {
  const lenA = getLinkNodeLen(headA);
  const lenB = getLinkNodeLen(headB);
  let nodeA = headA;
  let nodeB = headB;
  const num = Math.abs(lenA - lenB);
  if (lenA > lenB) {
    nodeA = getTargetNode(num, headA);
  } else {
    nodeB = getTargetNode(num, headB);
  }
  // 一起出发
  while (nodeA && nodeB) {
    if (nodeB === nodeA) {
      return nodeB;
    }
    nodeB = nodeB.next;
    nodeA = nodeA.next;
  }
  return null;
};

function getLinkNodeLen(head) {
  let len = 0;
  let node = head;
  while (node) {
    len++;
    node = node.next;
  }
  return len;
}

function getTargetNode(len, head) {
  let node = head;
  for (let i = 0; i < len; i++) {
    node = node.next;
  }
  return node;
}

解二:

// 建立一个Set集合,先遍历一个链表,存入集合,再遍历另一个链表,出现的第一个在Set中的节点就是第一个交点
const getIntersectionNode2 = function (headA, headB) {
  const nodeSet = new Set();
  let nodeA = headA;
  while (nodeA) {
    nodeSet.add(nodeA);
    nodeA = nodeA.next;
  }
  let nodeB = headB;
  while (nodeB) {
    if (nodeSet.has(nodeB)) {
      return nodeB;
    }
    nodeB = nodeB.next;
  }
  return null;
};

解三:

// 两个指针a,b分别指向两链表的头结点
// 两指针同时出发当a走到头,将a指向B的头结点继续走,当b走到头,将b指向A的头
// 该方法和方法一的原理差不多,最终长的那个先走
const getIntersectionNode3 = function (headA, headB) {
  if (!headA || !headB) {
    return null;
  }
  let a = headA;
  let b = headB;
  while (a !== b) {
    a = !a ? headB : a.next;
    b = !b ? headA : b.next;
  }
  return a;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值