1.找树左下角的值
题目:给定一个二叉树,在树的最后一行找到最左边的值。
思路:可以用层序遍历的思想,递归遍历节点的时候记录当前层级即可
迭代的代码比较多,就不放了
时间复杂度O(n),空间复杂度O(h)
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var findBottomLeftValue = function(root) {
let preLevel = -1;
let v;
const search = (root, level = 0) => {
if (!root) return;
if (level > preLevel) {
v = root.val;
preLevel = level;
}
search(root.left, level + 1);
search(root.right, level + 1);
};
search(root);
return v;
};
2.在每个树行中找最大值
题目:您需要在二叉树的每一行中找到最大的值。
思路:借鉴层序遍历的思路
时间复杂度O(n),空间复杂度O(h)
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var largestValues = function(root) {
const res = [];
const search = (root, level = 0) => {
if (!root) return;
if (res[level] === undefined) {
res[level] = root.val;
} else if (root.val > res[level]) {
res[level] = root.val;
}
search(root.left, level + 1);
search(root.right, level + 1);
};
search(root);
return res;
};
3.最长回文子序列
题目:给定一个字符串 s
,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s
的最大长度为 1000
。
思路:动态规划问题。可以用dp[i][j]来记录区间[i,j]的最长回文子序列。
那么,状态转移方程是:
如果s[i]=s[j],那么
dp[i][j]=dp[i+1][j-1]+2,
如果不等,那么dp[i][j]=Math.max(dp[i][j-1],dp[i+1][j])
可以发现,字符串依赖前一行及当期行之前的结果,所以从后往前遍历
时间复杂度O(n2),空间复杂度O(n2)
/**
* @param {string} s
* @return {number}
*/
var longestPalindromeSubseq = function(s) {
const l = s.length;
if (!l) return 0;
if (l == 1) return 1;
const dp = new Array(l).fill("").map(() => new Array(l).fill(0));
for (let i = 0; i < l; i++) {
dp[i][i] = 1;
}
let pre = 0;
for (let i = 1; i < l; i++) {
if (s[i] == s[i - 1]) {
dp[pre][i] = i - pre + 1;
} else {
pre = i;
}
}
for (let i = l - 2; i >= 0; i--) {
for (let j = i + 1; j < l; j++) {
if (dp[i][j] === j - i + 1) continue;
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
if (s[i] === s[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2);
}
}
}
return dp[0][l - 1];
};
用一个变量记录当前行之前的最长区间,可以压缩一下空间复杂度
时间复杂度O(n2),空间复杂度O(n)
/**
* @param {string} s
* @return {number}
*/
var longestPalindromeSubseq = function(s) {
const len = s.length;
if (len < 2) {
return len;
}
const dp = new Array(len).fill(1);
let max = 0;
for (let i = 1; i < len; i++) {
max = 0;
for (let j = i - 1; j >= 0; j--) {
const tmp = dp[j];
if (s[i] === s[j]) {
dp[j] = max + 2;
}
max = Math.max(tmp, max);
}
}
return Math.max(...dp)
};
4.零钱兑换
题目:给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
思路:完全背包问题,动态规划即可。dp[i]表示金额为i的硬币组合数,那么对于每一种硬币,有dp[i]=dp[i]+dp[i-item]
时间复杂度O(mn),m是金额,n是硬币种类,空间复杂度(m)
/**
* @param {number} amount
* @param {number[]} coins
* @return {number}
*/
var change = function(amount, coins) {
const dp = new Array(amount + 1).fill(0);
dp[0] = 1;
for (const item of coins) {
for (let i = item; i <= amount; i++) {
dp[i] = dp[i - item] + dp[i];
}
}
return dp[amount];
};
5.随机翻转矩阵
题目:
题中给出一个 n_rows 行 n_cols 列的二维矩阵,且所有值被初始化为 0。要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位置下标 [row_id,col_id];同样编写一个 reset 函数,将所有的值都重新置为 0。尽量最少调用随机函数 Math.random(),并且优化时间和空间复杂度。
注意:
1 <= n_rows, n_cols <= 10000
0 <= row.id < n_rows 并且 0 <= col.id < n_cols
当矩阵中没有值为 0 时,不可以调用 flip 函数
调用 flip 和 reset 函数的次数加起来不会超过 1000 次
思路:用数组记录,试过会内存溢出,所以只能用更节约性能的set或者map
对于m*n的矩阵,随机取点其实可以看成在0-m*n随机取一个整数,假定矩阵的点是有序的,就能算出对应的坐标。所以每次随机取一个下标,然后用set记录,出现过就重新取,reset就clear一下set
/**
* @param {number} n_rows
* @param {number} n_cols
*/
var Solution = function (n_rows, n_cols) {
this.count = n_rows * n_cols;
this.n_cols = n_cols;
this.flipC = new Set();
};
/**
* @return {number[]}
*/
Solution.prototype.flip = function () {
if (this.flipC.size === this.count) return;
let v = ~~(Math.random() * this.count);
while (this.flipC.has(v)) {
v = ~~(Math.random() * this.count);
}
this.flipC.add(v);
return [~~(v / this.n_cols), v % this.n_cols];
};
/**
* @return {void}
*/
Solution.prototype.reset = function () {
this.flipC.clear();
};
/**
* @return {void}
*/