- 求和为target的组合数
题目:数组中找出和为target的所有组合,每个数可以无限制取
解法:DFS
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum = function(candidates, target) {
const ans = [];
const dfs = (target, combine, idx) => {
if (idx === candidates.length) {
return;
}
if (target === 0) {
ans.push(combine);
return;
}
// 直接跳过
dfs(target, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
dfs(target - candidates[idx], [...combine, candidates[idx]], idx);
}
}
dfs(target, [], 0);
return ans;
};
2.岛的面积
/**
* @param {number[][]} grid
* @return {number}
*/
var maxAreaOfIsland = function(grid) {
let visited = [], stack = [],
rows = grid.length, cols = grid[0].length,
max = 0, cur = 0;
for (let i = 0; i < rows; i++) {
visited.push(new Array(cols));
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (!visited[i][j] && grid[i][j] == 1) {
stack.push([i, j]);
visited[i][j] = true;
cur = 1;
while (stack.length) {
let [x, y] = stack.pop();
if (x > 0 && grid[x - 1][y] == 1 && !visited[x - 1][y]) {
stack.push([x - 1, y]);
visited[x-1][y] = true;
cur += 1;
}
if (x < rows - 1 && grid[x + 1][y] == 1 && !visited[x + 1][y]) {
stack.push([x + 1, y]);
visited[x + 1][y] = true;
cur += 1;
}
if (y > 0 && grid[x][y - 1] == 1 && !visited[x][y - 1]) {
stack.push([x, y - 1]);
visited[x][y - 1] = true;
cur += 1;
}
if (y < cols - 1 && grid[x][y + 1] == 1 && !visited[x][y + 1]) {
stack.push([x, y + 1]);
visited[x][y + 1] = true;
cur += 1;
}
}
max = Math.max(max, cur);
}
}
}
return max;
};
- 求二叉树中距离某个结点距离为k的结点集合
题目:二叉树中所有距离目标结点距离为k的结点
tip: 转化为图的广度优先遍历,距离为3即可
解法:设置parent, 广度优先
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {TreeNode} target
* @param {number} k
* @return {number[]}
*/
//设置指向parent的指针,相当于无向图的广度优先遍历
var distanceK = function(root, target, k) {
let parent = {}, visited = {}, queue = [];
const setParent = (node, par) => {
if (!node) return;
parent[node.val] = par;
setParent(node.left, node);
setParent(node.right, node);
};
queue.push({
node: target,
level: 0
});
visited[target.val] = true;
setParent(root, null);
while (queue.length) {
let item = queue.shift(),
node = item.node,
level = item.level;
if (level == k) {
return [node.val].concat(queue.map(item => item.node.val));
} else {
if (node.left && !visited[node.left.val]) {
visited[node.left.val] = true;
queue.push({
node: node.left,
level: level + 1
});
}
if (node.right && !visited[node.right.val]) {
visited[node.right.val] = true;
queue.push({
node: node.right,
level: level + 1
});
}
if (parent[node.val] && !visited[parent[node.val].val]) {
visited[parent[node.val].val] = true;
queue.push({
node: parent[node.val],
level: level + 1
});
}
}
}
return [];
};
- 腐烂的橘子
BFS
/**
* @param {number[][]} grid
* @return {number}
*/
//广度优先遍历一张图
var orangesRotting = function(grid) {
let queue = [], max = 0, visited = [];
for (let i = 0; i < grid.length; i++) {
visited.push(new Array(grid[0].length));
}
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 2) {
visited[i][j] = true;
queue.push({
orange: [i, j],
level: 0
});
}
}
}
while (queue.length) {
let item = queue.shift(),
[x, y] = item.orange,
level = item.level;
max = Math.max(max, level);
if (x > 0 && grid[x-1][y] == 1 && !visited[x-1][y]) {
visited[x - 1][y] = true;
queue.push({
orange: [x-1, y],
level: level + 1
});
}
if (y < grid[0].length - 1 && grid[x][y + 1] == 1 && !visited[x][y+1]) {
visited[x][y+1] = true;
queue.push({
orange: [x, y + 1],
level: level + 1
});
}
if (x < grid.length - 1 && grid[x+1][y] == 1 && !visited[x+1][y]) {
visited[x+1][y] = true;
queue.push({
orange: [x + 1, y],
level: level + 1
});
}
if (y > 0 && grid[x][y-1] == 1 && !visited[x][y-1]) {
visited[x][y-1] = true;
queue.push({
orange: [x, y - 1],
level: level + 1
});
}
}
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] != 0 && !visited[i][j]) {
return -1;
}
}
}
return max;
};
排序:
- 找出出现频率最高的k个单次
题目要求:O(n)复杂度, 每个数大小[1, n]
tip: 先遍历统计次数,然后设置一个桶数组。桶数组的下标代表出现次数。借助数组下标天然可排序的优势。
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
let map = {}, bucket = new Array(nums.length + 1), max = 0;
nums.forEach(num => {
map[num] = map[num] ? (map[num] + 1) : 1;
});
for (let num in map) {
if (map.hasOwnProperty(num)) {
if(!bucket[map[num]]) {
bucket[map[num]] = [];
}
bucket[map[num]].push(num);
max = Math.max(map[num], max);
}
}
let ans = [];
for (let i = max; i >= 1; i--) {
if (!bucket[i]) continue;
if (ans.length + bucket[i].length <= k) {
ans = ans.concat(bucket[i]);
} else {
let subarr = bucket[i].slice(ans.length + bucket[i].length - k);
ans = ans.concat(subarr);
return ans;
}
}
return ans;
};
最长系列:
- 最长连续子序列
题目:arr中找出最长的,数值上连续的子序列的长度。 比如[100, 1, 200, 4, 3, 2], 最长子序列为[1, 2, 3, 4]。
tip: 要求O(n)的题目可以考虑借助空间。本题借助map. 边遍历边迭代是一个求解一维数组相关题目的思路。
解法:设置map记录每一个数是否出现过。当遍历到nums[i]时,如果不存在nums[i-1](证明它是当前子序列第一个数),那么不断判断map[nums[i+1...n]]是否存在,更新max.
var longestConsecutive = function(nums) {
let map = {}, ans = 0;
nums.forEach(num => {
map[num] = true;
});
for (i = 0; i < nums.length; i++) {
if (!map[nums[i] - 1]) {
let count = 1;
while (map[nums[i] + count]) {
count++;
}
ans = Math.max(count, ans);
}
}
return ans;
};
链表:
- 删除第n个结点
解法:两个指针,一个比另一个向前n步
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let a = head, b = head, i = 0;
while (i < n) {
a = a.next;
i++;
}
if (!a) return head.next;
while(a.next) {
a = a.next;
b = b.next;
}
b.next = b.next.next;
return head;
};
OO:
- LRU 缓存 题目描述: 固定大小c的cache, 实现get和put O(1)时间内实现,如果cache已达最大值,将最久没有被使用的cache移除。
tip: 使用map, 使用双向链表实现优先队列。双向链表要同时记录head和tail. 双向链表的特点是,可以很容易得到一个结点的前驱。 真正的缓存放在双向链表中,map为key到缓存结点的映射。
解法: get: 查找map直接得到有还是没有。如果有,需要移动该节点到head. 时间O(1)——双向链表移动节点是O(1) put: 如果还有空间,直接插入到head前。如果没有空间了, 修改tail部分的结点为新值,并移动到头部(或者直接删除,然后插入一个),也是O(1).
/**
* @param {number} capacity
*/
var LRUCache = function(capacity) {
this.capacity = capacity;
this.size = 0;
this.map = {};
this.head = null;
this.tail = null;
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function(key) {
let node = this.map[key];
if (node) {
if (node.left) {
node.left.right = node.right;
if (node.right) {
node.right.left = node.left;
} else {
this.tail = node.left;
this.tail.right = null;
}
node.left = null;
node.right = this.head;
this.head.left = node;
this.head = node;
}
return node.val;
} else {
return -1;
}
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function(key, value) {
if (!this.capacity) return;
let node = this.map[key];
//已有该值
if (node) {
node.val = value;
//将该结点移动到head
//如果本是第一个结点,直接返回
if (node == this.head) {
return;
} else {
//左边结点left指向右边结点
node.left.right = node.right;
//如果有右边结点,指向左边结点
if (node.right) {
node.right.left = node.left;
} else {
//否则node是尾部结点,调整this.tail;
this.tail = node.left;
}
this.head.left = node;
node.right = this.head;
node.left = null;
this.head = node;
}
//不存在node;
} else {
if (this.size < this.capacity) {
this.size++;
node = {
key: key,
val: value,
left: null,
right: this.head
};
if (!this.head) {
this.head = this.tail = node;
} else {
this.head.left = node;
this.head = node;
}
} else {
delete this.map[this.tail.key];
node = this.tail;
node.key = key;
node.val = value;
if (node != this.head) {
this.tail = node.left;
this.tail.right = null;
node.left = null;
node.right = this.head;
this.head.left = node;
this.head = node;
}
}
this.map[key] = node;
}
};
- TicTacToe
题目:三子棋
解法:记录每个选手每一行 每一列 每一对角线的和
/**
* Initialize your data structure here.
* @param {number} n
*/
var TicTacToe = function(n) {
this.n = n;
this.rowSum = [new Array(n), new Array(n)];
this.colSum = [new Array(n), new Array(n)];
this.diaSum1 = new Array(2);
this.diaSum2 = new Array(2);
};
/**
* Player {player} makes a move at ({row}, {col}).
@param row The row of the board.
@param col The column of the board.
@param player The player, can be either 1 or 2.
@return The current winning condition, can be either:
0: No one wins.
1: Player 1 wins.
2: Player 2 wins.
* @param {number} row
* @param {number} col
* @param {number} player
* @return {number}
*/
TicTacToe.prototype.move = function(row, col, player) {
this.rowSum[player - 1][row] = (this.rowSum[player - 1][row] || 0) + 1;
if(this.rowSum[player - 1][row] == this.n) return player;
this.colSum[player - 1][col] = (this.colSum[player - 1][col] || 0 ) + 1;
if (this.colSum[player - 1][col] == this.n) return player;
if (row == col) {
this.diaSum1[player - 1] = (this.diaSum1[player - 1] || 0) + 1;
if (this.diaSum1[player-1] == this.n) return player;
}
if (row + col == this.n - 1) {
this.diaSum2[player - 1] = (this.diaSum2[player - 1] || 0) + 1;
if (this.diaSum2[player-1] == this.n) return player;
}
return 0;
};
/**
* Your TicTacToe object will be instantiated and called as such:
* var obj = new TicTacToe(n)
* var param_1 = obj.move(row,col,player)
*/
双指针:
- 找出字符串中无重复字符的最长子串
tip: 双指针都从0开始。 循环不变量是双指针l和r之间永远没有重复字符。 通过map记录字符有没有出现过。
解法:一旦字符s[r]出现过,l移动到字符s[r]出现的位置l2并加1, 顺便抹去l到l2之间map的记录。同时如果此时r - l比上次大,更新最大值。
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
if (!s) return 0;
if (s.length == 1) return 1;
//循环不变量是l和r之间永远没有重复的字符。
var l = 0, r = 0, max = 0, map = {};
while(r < s.length) {
if (typeof map[s[r]] != 'undefined') {
if (r - l > max) {
max = r - l;
}
while(l <= map[s[r]]) {
delete map[s[l]];
l++;
}
}
map[s[r]] = r;
r++;
}
if (r - l > max) {
max = r - l;
}
return max;
};
- 3sum
tip: 将数组排序,固定一个数,另外两个数双指针。 冒泡排序partition循环不变量: start + 1到i - 1永远 <= target。j + 1到end永远大于target
/**
* @param {number[]} nums
* @return {number[][]}
*/
const swap = (nums, i, j) => {
let tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
const partition = (nums, start, end) => {
let target = nums[start], i = start, j = end;
while (i < j) {
if (nums[i + 1] > target) {
swap(nums, i + 1, j);
j--;
} else {
i++;
}
}
return i;
}
const quicksort = (nums, start, end) => {
if (start >= end) return;
let pos = partition(nums, start, end);
swap(nums, start, pos);
quicksort(nums, start, pos - 1);
quicksort(nums, pos + 1, end);
}
var threeSum = function(nums) {
let result = [];
if (nums.length < 3) return [];
quicksort(nums, 0, nums.length - 1);
for (let i = 0; i < nums.length - 2; i++) {
let m = i + 1, k = nums.length - 1;
while(m < k) {
let sum = nums[i] + nums[m] + nums[k];
if (sum == 0) {
result.push([nums[i], nums[m], nums[k]]);
m++;
k--;
while(m < k && nums[m] == nums[m-1])m++;
while(m < k && nums[k] == nums[k+1])k--;
} else if (sum > 0) {
k--;
} else {
m++;
}
}
while (nums[i + 1] == nums[i]) i++;
}
return result;
};
- 0 1 2排序
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var swap = function(nums, i, j) {
let tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
var sortColors = function(nums) {
if (nums.length == 1) return;
if (nums.length == 2) {
if (nums[0] > nums[1]) {
swap(nums, 0, 1);
}
return;
}
let left = 0, right = nums.length - 1;
while (nums[left] == 0) left++;
while (nums[right] == 2) right--;
let cur = left;
while (cur <= right) {
if (nums[cur] == 0) {
if (cur >= left) {
swap(nums, cur, left);
while(nums[left] == 0) left++;
} else {
cur++;
}
} else if (nums[cur] == 2) {
swap(nums, cur, right);
right--;
} else {
cur++;
}
}
};
- 最大水容积
题目:给定挡板高度数组heights, 求最大能容纳的水的容积。
解法:从最左和最右开始(宽最大),计算容积。缩进高度比较小的边(毕竟这样才有改善余地)
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let i = 0, j = height.length - 1, max = 0;
while (i < j) {
let v = (j - i) * Math.min(height[i], height[j]);
max = Math.max(v, max);
if (height[i] < height[j]) {
i++;
} else {
j--;
}
}
return max;
};
字母计数:
- 将字符串按照「字谜」分组
解法:字母计数数组 + 哈希表。(计数数组转化为字符串再作为哈希表的key)
var groupAnagrams = function(strs) {
let map = {};
strs.forEach(str => {
let arr = new Array(26).fill(0);
for (let i = 0; i < str.length; i++) {
arr[str[i].charCodeAt(0) - 97]++;
}
let key = arr.map(num => num + '').join('-');
map[key] = map[key] || [];
map[key].push(str);
});
return Object.values(map);
};