给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
你可以假设 k 的值永远是有效的,1 ≤ k ≤ n2 。
/**
* @param {number[][]} matrix
* @param {number} k
* @return {number}
*/
var kthSmallest = function (matrix, k) {
let sorted = []
for (let i = 0; i < matrix.length; i++) {
sorted = [...sorted,...matrix[i]]
}
sorted.sort((a, b) => a - b)
return sorted[k - 1]
}
题目中每行和每列元素均按升序排序这个应该可以作为优化的点
1 | 2 | 3 | 4 |
---|---|---|---|
11 | 12 | 13 | 14 |
21 | 22 | 23 | 24 |
31 | 32 | 33 | 34 |
随便找一个符合规则的matrix,找下规则(row表示行,i表示行索引,column,表示列j表示列索引)
想要单次遍历逐个递增的来统计第k小的数,会发现下一个比他大的数的区值范围在一个梯形范围内很难具体定位,
换个思路,既然指定一个数,我可以定位到大于他的范围,那假设我已经知道了第k小的元素是m那么,直接统计小于他的数是不是k-1个就可以验证m的真实性了。
var kthSmallest = function (matrix, k) {
let n = matrix.length;
let left = matrix[0][0];
let right = matrix[n - 1][n - 1];
while (left < right) {
let mid = left + parseInt((right - left) / 2, 10);
if (check(matrix, mid, k, n)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
function check(matrix, mid, k, n) {
let i = n - 1;
let j = 0;
let num = 0;
while (i >= 0 && j < n) {
if (matrix[i][j] <= mid) {
num += i + 1;
j++;
} else {
i--;
}
}
return num >= k;
}
}
reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值
var kthSmallest = function(matrix, k) {
if(matrix.length < 1) return 0
let arr = matrix.reduce((a, b) => merge(a, b))
return arr[k - 1]
};
function merge(left, right){
let llen = left.length
let rlen = right.length
let i = 0
let j = 0
let res = []
// 后入数组先按大小入目标数组
while(i < llen && j < rlen){
if (left[i] < right[j]) {
res.push(left[i++])
} else {
res.push(right[j++])
}
}
// 排序逻辑中未入目标数组的子集依次进入
while(i < llen) res.push(left[i++])
while(j < rlen) res.push(right[j++])
return res
}