2021.11.22
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"]
// 将数字任意分成两组数,然后随意添加小数点或不加
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
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"]
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
// 每个数有正负两种情况
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
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
// 将字符串分成任意的数值组合
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
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
// 固定一个,对另外一个进行全排列
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
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
// 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
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
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
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')) // []
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
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
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
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
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
// 坐标是否在同一斜线上:
// 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));
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;
};
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
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;
};
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)))));
// 搜索二叉树中序遍历的值为递增,把中序遍历的节点放入一个数组,相当于一个递增的数组中有两个数交换了位置,找到这两个数的位置
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)))));
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;
};
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;
};
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
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;
}
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
// 暴力解法
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))
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
// 找出两条路径,再找出其最后一个交点;(最后两个用例溢出)
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
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;
};
// 前序遍历 :根->左->右, 中序遍历: 左 -> 根 ->右
// 前: [[根节点] [所有左子树节点] [所有右子树节点]] 可以得到第一个节点就是根节点
// 中: [[所有左子树节点] [根节点] [所有右子树节点]] 可以根据根节点得到左子树节点个数和右子树节点个数
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];
}
// 如果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;
}
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
解一:暴力
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
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;
};
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;
};
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
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;
};
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;
}
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;
};
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;
};
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));
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;
};
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;
};
解一:
// 先找出两个链表的长度,如果长度差值为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;
};