// 算法
// 排列组合 - 称砝码
let weightArr = [1, 2]
let numsArr = [2, 1]
let allWeightArr = []
numsArr.forEach((n, i) => {
allWeightArr = allWeightArr.concat(new Array(n).fill(weightArr[i]))
})
console.log(allWeightArr)
let weightSumsArr = [] // 几种组合
let result = [0] //几种重量
// 遍历每一个, 跟组合好的数组重新组合形成新的数组,放入新数组
allWeightArr.forEach((w) => {
let newArr = []
weightSumsArr.forEach((item) => {
let arr = []
arr = [...item, w]
weightSumsArr.push(arr)
})
weightSumsArr.push([w])
})
allWeightArr.forEach((w) => {
let newArr = []
result.forEach((item) => {
newArr.push(w + item)
})
result = [...new Set([...result, ...newArr])]
})
console.log(result, result.length)
/*
weightSumsArr = allWeightArr.reduce((pre, w) => {
pre.forEach(item => {
let arr2 = [...item, w]
pre.push(arr2)
})
pre.push([w])
return pre
}, [])
console.log(weightSumsArr)
*/
console.log(weightSumsArr)
//----------------------------面试题 08.08. 有重复字符串的排列组合-----------------------
//----------------------------面试题 08.07. 无重复字符串的排列组合-----------------------
// 全排列 str = 'abc' 思路: 取出第一个,剩下的全排列,然后插入全排列的每一项。递归 + 回溯
function func (str) {
let result = []
if (str.length === 1 || str.length === 0) {
result.push(str)
return [...new Set(result)]
}
let one = str[0]
let other = str.slice(1)
// 取出第一个, 剩下的全排列 ['bc', 'cb']
let otherCopmose = func(other)
// 然后插入a, 有 3种方式 'abc' 'bac' 'cba'
for(let i=0; i<otherCopmose.length; i++) {
// 每次插入,都有 每组长度 + 1 中可能
for(let j=0; j< otherCopmose[i].length + 1; j++) {
let arr = otherCopmose[i].split('')
arr.splice(j, 0, one)
let newStr = arr.join('')
result.push(newStr)
}
}
return [...new Set(result)]
}
func('abc')
var func = function (str) {
let result = []
function search(path) {
if (path.length === str.length) {
result.push(path)
}
for (let char of str) {
if (path.indexOf(char) == -1) {
search(`${char}${path}`)
}
}
}
search('')
return result
}
func('abc')
//-----------------------------------剑指 Offer II 017. 含有所有字符的最短字符串 难度:困难---------------------
//输入:s = "ADOBECODEBANC", t = "ABC"
//输出:"BANC"
//"acbbaca"
//"aba"
var minWindow = function(s, t) {
let result = []
if (s.length < t.length) return ''
let tmap = {}
for (let ts of t) {
tmap[ts] = tmap[ts] ? tmap[ts] + 1 : 1
}
for (let i=0;i<s.length;i++) {
for (let j=i+1; j<=s.length; j++) {
let ss = s.slice(i, j)
let ssmap = {}
for (let sss of ss) {
ssmap[sss] = ssmap[sss] ? ssmap[sss] + 1 : 1
}
let flag = true
for (let tss of t) {
if (tmap[tss] > ssmap[tss] || !ss.includes(tss) ) {
flag = false
break
}
}
if (flag && ss.length >= t.length) {
result.push(ss)
}
}
}
// console.log(result)
let sortSubs = result.sort((a, b) => a.length - b.length)
if (sortSubs.length == 0) return ''
return sortSubs[0]
// console.log(sortSubs)
};
//-------------------字符串所有子串 - 递归---------------------------
var strFromSubStr = function (str) {
let result = []
function dfs (s) {
if (s.length === 1) {
result.push(s)
return result
}
let one = s[0]
let subs = dfs(s.slice(1))
subs.forEach(ss => {
result.push(one, one + ss)
})
return result
}
dfs(str)
return [...new Set(result)]
}
//---------------------------剑指 Offer II 015. 字符串中的所有变位词-------------------------------
//输入: s = "cbaebabacd", p = "abc"
//输出: [0,6]
var findAnagrams = function(s, p) {
let res = []
let pmap = {}
for (let c of p) {
pmap[c] = pmap[c] ? pmap[c] + 1 : 1
}
for(let i=0; i<s.length; i++) {
let target = s.slice(i, i+p.length)
// target 和 p 是变位词
if (target.length !== p.length) continue
let tmap = {}
for (let c of target) {
tmap[c] = tmap[c] ? tmap[c] + 1 : 1
}
let flag = true
let keys = Object.keys(tmap)
for (let j=0; j<keys.length; j++) {
if (tmap[keys[j]] !== pmap[keys[j]]) {
flag = false
break
}
}
if (!flag) continue
res.push(i)
}
return res
};
//-----------------------剑指 Offer II 014. 字符串中的变位词: s1 是否是 s2 的变位词---------------
var checkInclusion = function(s1, s2) {
for(let i=0; i<s2.length; i++) {
let ts = s2.slice(i, i+s1.length)
if (ts.length !== s1.length) continue
if (ts.split('').sort().join('') === s1.split('').sort().join('')) {
return true
} else {
continue
}
}
return false
};
// 双指针解法
var checkInclusion = function(s1, s2) {
let need = new Array(26).fill(0)
let len1 = s1.length
let len2 = s2.length
for (let s of s1) {
let i = s.charCodeAt() - 97
need[i] += 1
}
// console.log(need)
let l=0, r=0;
let all = new Array(26).fill(0)
while (r<len2) {
all[s2[r++].charCodeAt() - 97] += 1
if (r-l === len1) {
if (need.toString() === all.toString()) {
console.log(all)
return true
}
all[s2[l++].charCodeAt() - 97] -= 1
}
}
return false
}
//-------------------剑指 Offer II 086. 分割回文子字符串----------------------
// 输入: 'google'
// 输出: [ ["g", "o", "o", "g", "l", "e"],["g", "oo", "g", "l", "e"], ["goog", "l", "e"] ]
var partition = function(s) {
let res = []
let arr = []
function search (index) {
if (index === s.length) {
return res.push(arr)
}
let target = ''
for (let i=index; i<s.length; i++) {
target += s[i]
if (target !== target.split('').reverse().join('')) continue
arr.push(target)
search(i + 1)
arr.pop()
}
}
search(0)
return res
};
// ------------------最长无重复子串----------------------------
var lengthOfLongestSubstring = function(s) {
let max = 0
let left = 0
let right = 1
let l = 0
let r = 0
if (s.length === 0 || s.length === 1) return s.length
while (right < s.length) {
let sub = s.slice(left, right)
if (sub.indexOf(s[right]) > -1) {
left++
continue
} else {
right++
}
if (right - left > max) {
max = right - left
l = left
r = right
}
}
console.log(s.slice(l, r))
return s.slice(l, r).length
};
//---------------------------------------516. 最长回文子序列-------------
var longestPalindromeSubseq = function(s) {
let len = s.length
let dp = Array.from(new Array(len),()=>new Array(len).fill(0))
for(let i = len-1;i>=0;i--){
dp[i][i] = 1
for(let j = i+1;j<len;j++){
if(s[i]==s[j]){
// i到j 满足回文的话,去掉第一个和最后一个字符依然是回文。 i+1 去掉首,j-1 去掉尾。 去掉了两个字符,所以长度是 +2
dp[i][j] = dp[i+1][j-1] +2
}else{
// i到j 不满足的话,取 i+1 ~ j 和 i~j-1 最长的
dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1])
}
}
}
return dp[0][len-1]
};
// -----------------------leetcode 8.字符串转整数 --------------------------
var myAtoi = function(str) {
//利用正则
let result = str.trim().match(/^[-|+]{0,1}[0-9]+/)
if(result != null){
if(result[0] > (Math.pow(2,31) -1)){
return Math.pow(2,31) -1
}
if(result[0] < Math.pow(-2,31)){
return Math.pow(-2,31)
}
return result[0]
}
return 0
};
//------------------------leetcode 205. 同构字符串: 哈希表----------------------------
// 输入:s = "egg", t = "add" 正向:e->a g->d 反向:a->e d->g
// 输出:true
var isIsomorphic = function(s, t) {
function helper (str1, str2) {
let map = {}
let len = str1.length
for (let i=0; i<len; i++) {
let s1 = str1[i]
let t1 = str2[i]
if (map[s1]) {
if (map[s1] !== t1) {
return false
}
} else {
map[s1] = t1
}
}
console.log(map)
return true
}
return helper(s, t) && helper(t, s)
};
//-------------------------leetcode 43 字符串数字相乘, 大数相乘---------------
var multiply = function(num1, num2) {
if (num1 === '0' || num2 === '0') {
return '0'
}
let arr1 = num1.split('').reverse()
let arr2 = num2.split('').reverse()
let L1 = arr1.length
let L2 = arr2.length
let result = new Array(L1+L2).fill(0)
for (let i=0; i<L1; i++) {
for(let j=0; j<L2; j++) {
let carryIndex = i + j + 1 //进位位置
let curIndex = i + j //当前位置
let sum = arr1[i] * arr2[j] + result[curIndex] // 当前的和 = 当前的乘积 + 当前位置的值(上次进位的值)
result[curIndex] = sum % 10 //当前位置的值
result[carryIndex] += Math.floor(sum / 10) // 进位位置的值 = 进位位置原来的值 + sum / 10 取整
}
}
console.log(result)
let res = result.reverse()
if (res[0] === 0) res.shift()
return res.join('')
};
// ---------------------------------------------------动态规划类问题:1. 计数, 2. 最值, 3. 是否存在-------------------------------------------------------
/**
1.确定状态- 最后一步 + 子问题
2.总结方程
3.边界条件
4.计算顺序
*/
案例1:有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多,买一本书需要27元,如何用最少的硬币组合正好付清,不需要对方找钱
// 分析:1. 最后一步 27-ak
let coins = [2, 5, 7]
function coinsChange (coins, sum) {
let f = new Array(sum + 1).fill(0)
f[0] = 0;
// 从 f(1) 开始计算一直到 f(27)
for (let i=1; i<=sum; i++) {
f[i] = Number.POSITIVE_INFINITY
//f[X]=min{f[X-2]+1,f[X-5]+1,f[X-7]+1}
for (let j = 0; j<coins.length; j++) {
if (i >= coins[j] && f[i - coins[j]] !== Number.POSITIVE_INFINITY) {
// 取上一次的最小 + 当前
f[i] = Math.min(f[i], f[i-coins[j]] + 1)
}
}
}
if(f[sum] == Number.POSITIVE_INFINITY) {
return -1
}
return f[sum]
}
coinsChange([2,5,7], 27)
// dp数组优化双重循环带来的重复计算
var coinsChange = (coins, N) => {
let dp = new Array(N+1).fill(Infinity);
dp[0] = 0
if (N < 0) return -1
for(let i=0; i<dp.length; i++) {
for(let j=0; j<coins.length; j++) {
if (i-coins[j] < 0) continue
dp[i] = Math.min(dp[i], 1 + dp[i-coins[j]])
}
}
return dp[N]
}
coinsChange([2,5,7], 27)
// 案例2: 给定m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下或者向右走一步, 有多少种不同的方式走到右下角
function skipGEZI (m, n) {
let f = new Array(m).fill(new Array(n).fill(0))
for (let i=0; i<m; i++) {
for (let j=0; j<n; j++) {
if(j === 0 || i === 0) {
f[i][j] = 1
} else {
f[i][j] = f[i-1][j] + f[i][j-1]
}
}
}
return f[m-1][n-1]
}
// 案例3:给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标
// 1. 最后一步 a[i] + i >= j j代表某个位置
const jumpCan = (arr) => {
let len = arr.length
let f = new Array(len).fill(false)
f[0] = true
for (let i=0; i< len; i++) {
for (let j=0; j<i; j++) {
if (f[j] && arr[j] + j >= i) {
f[i] = true
}
}
}
return f[len -1]
}
jumpCan([2,3,1,1,4]) // true
jumpCan([3,2,1,0,4]) // false
//------------------------------------------leetcode 97.交错字符串------------------------
// 输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
// 输出:true 下面解法不对,应该用动态规划。
var isInterleave = function(s1, s2, s3) {
let arr1 = s1.split('')
let arr2 = s2.split('')
let all = [...arr1, ...arr2]
if (all.length !== s3.length) return false
let allmap = all.reduce((map, cur) => {
if (map[cur]) {
map[cur] += 1
} else {
map[cur] = 1
}
return map
}, {})
let flag = true
let map = {}
for (let ch of s3) {
if (!all.includes(ch)) {
flag = false
break
}
if (map[ch]) {
map[ch] += 1
} else {
map[ch] = 1
}
}
let keys = Object.keys(allmap)
for (let j=0; j<keys.length; j++) {
if (allmap[j] !== map[j]) {
flag = false
break
}
}
function swrapArr (ar1, ar2) {
// return ar1.filter((v) => !ar2.includes(v))
let _ar1 = [...ar1]
for (let c of ar2) {
let index = _ar1.findIndex(v => v===c)
if (index > -1) {
_ar1.splice(index, 1)
}
}
return _ar1
}
let s3arr = s3.split('')
let _arr1 = swrapArr(s3arr, arr2) // 剩余的arr1
let _arr2 = swrapArr(s3arr, arr1) // 剩余的arr2
console.log(s3arr)
console.log(_arr1, arr1)
console.log(_arr2, arr2)
if (arr1.sort().join('') !== _arr1.sort().join('')) {
flag = false
}
if (arr2.sort().join('') !== _arr2.sort().join('')) {
flag = false
}
return flag
};
// 正确解法 - 剑指 Offer II 096. 字符串交织
var isInterleave = function(s1, s2, s3) {
const n = s1.length;
const m = s2.length;
if(n+m!=s3.length) return false;
// dp 路径
let dp = new Array(n+1).fill(0).map(()=>new Array(m+1).fill(false));
// dp[i][j] : 长度为[i+j]的s3前缀 能否由 长度为i的s1前缀 与 长度为j的s2前缀 交织组成
// 先处理一下 i/j 取0 的情况
dp[0][0] = true;
for(let i=1;i<n+1;i++) {
if(s1[i-1]==s3[i-1]) dp[i][0] = true;
else break;
}
for(let j=1;j<m+1;j++) {
if(s2[j-1]==s3[j-1]) dp[0][j] = true;
else break;
}
for(let i=1;i<n+1;i++) {
for(let j=1;j<m+1;j++) {
dp[i][j] = (s1[i-1] == s3[i+j-1] && dp[i-1][j]) || (s2[j-1] == s3[i+j-1] && dp[i][j-1])
}
}
return dp[n][m];
};
//-------------------------97. 交错字符串/ 剑指 Offer II 096. 字符串交织: s3 是否是由 s1 和 s2 交错 组成 -------------------------------
//dp[i][j] 表示 s1.substring(0, i) 和 s2.substring(0, j) 能交错组成 s3.substring(0, i+j)
//dp[0][0] = true
//dp[i][j] = (dp[i-1][j] && s1[i-1] === s3[i+j-1]) || (dp[i][j-1] && s2[j-1] === s3[i+j-1]);
var isInterleave = function(s1, s2, s3) {
const m = s1.length + 1, n = s2.length + 1;
if (s3.length !== m + n - 2) return false;
const dp = [];
for (let i = 0; i < m; ++i) {
const temp = new Array(n);
dp.push(temp);
}
dp[0][0] = true;
for (let i = 1; i < m; ++i) {
dp[i][0] = dp[i-1][0] && s1[i-1] === s3[i-1];
}
for (let j = 1; j < n; ++j) {
dp[0][j] = dp[0][j-1] && s2[j-1] === s3[j-1];
}
for (let i = 1; i < m; ++i) {
for (let j = 1; j < n; ++j) {
dp[i][j] = (dp[i-1][j] && s1[i-1] === s3[i+j-1]) || (dp[i][j-1] && s2[j-1] === s3[i+j-1]);
}
}
return dp[m-1][n-1];
};
// 案例4:斐波那切数列 dp数组迭代法
function fib(n) {
let dp = new Array(n+1)
dp[0] = dp[1] = 1
for (let i=3; i<=n; i++) {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
//---------------爬楼梯 每次1级2级 有多少种方法-------------------
var climbStairs = function(n) {
let dp = new Array(n+1).fill(0)
if (n === 1 || n === 2) {
return n
}
dp[0] = 0
dp[1] = 1
dp[2] = 2
for (let i=3; i<=n; i++) {
dp[i] = dp[i-1] + dp[i-2]
}
console.log(dp)
return dp[n]
};
//----------------------------面试题 05.02. 二进制小数数转字符串----------------------
var printBin = function(num) {
let dist = []
while (num) {
num *=2
let d = num >= 1 ? 1 : 0
dist.push(d)
if (dist.length > 32) return 'ERROR'
num -= d
}
return `0.${dist.join('')}`
};