1.最小时间差
题目:给定一个 24 小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。
思路:将时间转换为对应的分钟数,遍历一次数组,找到相邻最小的时间差,然后再和第一项和最后一项的差比较即可
时间复杂度O(n),空间复杂度O(1)
/**
* @param {string[]} timePoints
* @return {number}
*/
var findMinDifference = function(timePoints) {
const l = timePoints.length;
const time = timePoints
.map((item) => {
const [h, m] = item.split(":");
return Number(m) + Number(h) * 60;
})
.sort((a, b) => a - b);
let min = 24 * 60;
for (let i = 1; i < l; i++) {
min = Math.min(min, time[i] - time[i - 1]);
}
min = Math.min(
min,
Math.min(time[l - 1] - time[0], time[0] + 24 * 60 - time[l - 1])
);
return min;
};
2.有序数组中的单一元素
题目:给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
思路:有序,所以肯定可以二分法。计算中间位置的成员,有好几种情况,一一处理。
第一种:该成员与左右元素均不相等。所以直接返回该下标的值;
第二种:该成员与左边的值相等,且该成员右边数量是偶数,说明目标在左侧;奇数,说明目标在右侧;
第三种:该成员与右边的值相等,且左边成员数量是偶数,那么在右边;奇数,在左边
时间复杂度O(nlogn),空间复杂度O(1)
/**
* @param {number[]} nums
* @return {number}
*/
var singleNonDuplicate = function(nums) {
const l = nums.length;
if (l < 2) return nums[0];
let left = 0,
right = l - 1;
while (left < right) {
const mid = ~~((left + right) / 2);
if (nums[mid] !== nums[mid - 1] && nums[mid] !== nums[mid + 1])
return nums[mid];
if (nums[mid] === nums[mid + 1] && mid % 2) {
right = mid - 1;
} else if (nums[mid] === nums[mid - 1] && !(mid % 2)) {
right = mid - 2;
} else if (nums[mid] === nums[mid - 1] && mid % 2) {
left = mid + 1;
} else {
left = mid + 2;
}
}
return nums[left];
};
3.01矩阵
题目:
给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
思路:典型的bfs。某一个点到另一个的距离,是横纵坐标之差的绝对值之和。所以先找到所有0的点,然后遍历矩阵,计算每个非0的点到所有0点的距离,取最小值
时间复杂度O(mn),空间复杂度O(mn)
var updateMatrix = function(matrix) {
const h = matrix.length;
if (!h) return [];
const w = matrix[0].length;
const stack = [];
for (let i = 0; i < h; i++) {
for (let j = 0; j < w; j++) {
if (matrix[i][j] === 0) {
stack.push([i, j]);
}
}
}
for (let i = 0; i < h; i++) {
for (let j = 0; j < w; j++) {
if (matrix[i][j] !== 0) {
let min = w + h + 2;
stack.forEach((item) => {
min = Math.min(Math.abs(i - item[0]) + Math.abs(j - item[1]), min);
});
matrix[i][j] = min;
}
}
}
return matrix;
};
换个思路。从左上角王右下角遍历一次,,对于dp[i][j]=min(dp[i-1][j],dp[i][j-1])
从右下角往左上角遍历一次,对于dp[i][j]=min(dp[i+1][j],dp[i][j+1])
即可得到结果。
时间复杂度O(mn),空间复杂度O(1)
/**
* @param {number[][]} matrix
* @return {number[][]}
*/
var updateMatrix = function(matrix) {
let col = matrix.length,
row = matrix[0].length ;
for(let i = 0; i < col; i++){
for(let j = 0; j < row; j++){
if(matrix[i][j] !== 0) {
matrix[i][j] = col + row - 2;
}
}
}
for(let i = 0 ; i < col; i++){
for(let j = 0; j < row; j++){
if(i > 0) matrix[i][j] = Math.min(matrix[i][j], matrix[i - 1][j] + 1);
if(j > 0) matrix[i][j] = Math.min(matrix[i][j], matrix[i][j - 1] + 1);
}
}
for(let i = col - 1; i >= 0; i--){
for(let j = row - 1; j >= 0; j--){
if(i < col - 1) matrix[i][j] = Math.min(matrix[i][j], matrix[i + 1][j] + 1);
if(j < row - 1) matrix[i][j] = Math.min(matrix[i][j], matrix[i][j + 1] + 1);
}
}
return matrix;
};
4.朋友圈
题目:
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
思路:dfs。从第一个成员开始,遍历后续的成员,遇到是朋友,就继续递归遍历,用一个set记录以及遍历过的成员
时间复杂度O(n2),空间复杂度O(n)
/**
* @param {number[][]} M
* @return {number}
*/
var findCircleNum = function(M) {
const n = M.length;
const set = new Set();
let c = 0;
const dfs = (index) => {
if (set.has(index)) return;
set.add(index);
for (let i = 0; i < n; i++) {
if (M[index][i] === 1) {
dfs(i);
}
}
};
for (let i = 0; i < n; i++) {
if (!set.has(i)) {
c++;
dfs(i);
}
}
return c;
};
5.最优除法
题目:
给定一组正整数,相邻的整数之间将会进行浮点除法操作。例如, [2,3,4] -> 2 / 3 / 4 。
但是,你可以在任意位置添加任意数目的括号,来改变算数的优先级。你需要找出怎么添加括号,才能得到最大的结果,并且返回相应的字符串格式的表达式。你的表达式不应该含有冗余的括号。
思路:其实,用最直接的方式思考,一个除法的结果要最大,那么除数尽可能的大,被除数尽可能的小。
第一个数不能修改,所以让被除数尽可能的小即可。最小就是后续都相除
时间复杂度O(n),空间复杂度O(1)
/**
* @param {number[]} nums
* @return {string}
*/
var optimalDivision = function(nums) {
const l = nums.length;
if (l < 3) return nums.join("/");
return `${nums[0]}/(${nums.slice(1).join("/")})`;
};