javascript - 在 JavaScript 中设置矩阵归零性能
问题描述
我在 JavaScript 中构建了 Set Matrix Zeroes 算法。我的代码中有很多循环,所以想知道是否有更好的方法来实现这个算法。
给定 amxn 矩阵,如果一个元素为 0,则将其整行和整列设置为 0。就地执行。
我循环矩阵,当我找到一个 0 时,我将它的整个行和列设置为一个标志('X')。如果位置为 0,我不会设置“X”,因为我将来可能需要检查这个位置。最后,我将所有“X”替换为 0。
var setZeroes = function(matrix) {
if(matrix === null || matrix.length === 0)
return [];
for(let i=0; i<matrix.length; i++){
for(let j=0; j<matrix[i].length; j++){
if(matrix[i][j] === 0){
setFlag(matrix, i, j);
}
}
}
for(i=0; i<matrix.length; i++){
for(j=0; j<matrix[i].length; j++){
if(matrix[i][j] === 'X')
matrix[i][j] = 0;
}
}
};
const setFlag = function(matrix, i, j) {
matrix[i][j] = 'X';
let tempI = i+1;
while(tempI < matrix.length){
if(matrix[tempI][j] !== 0)
matrix[tempI][j] = 'X';
tempI++;
}
tempI = i-1;
while(tempI >= 0){
if(matrix[tempI][j] !== 0)
matrix[tempI][j] = 'X';
tempI--;
}
let tempJ = j+1;
while(tempJ < matrix[i].length){
if(matrix[i][tempJ] !== 0)
matrix[i][tempJ] = 'X';
tempJ++;
}
tempJ = j-1;
while(tempJ >= 0){
if(matrix[i][tempJ] !== 0)
matrix[i][tempJ] = 'X';
tempJ--;
}
}
解决方案
这是一个更具声明性的实现,Jsperf 更喜欢它。
let replaceRowCol = (matrix, trg=0, val=0) => {
// Store width and height of matrix
let w = matrix.length;
let h = matrix[0].length;
// An array to hold all replacements that need to be made
let replaceArr = [];
// Any coord whose value is `trg` results in a replacement at that coord
for (let x = 0; x < w; x++) { for (let y = 0; y < h; y++) {
if (matrix[x][y] === trg) replaceArr.push({ x, y });
}}
// Perform all replacements
for (let { x, y } of replaceArr) {
for (let xx = 0; xx < w; xx++) matrix[xx][y] = val;
for (let yy = 0; yy < h; yy++) matrix[x][yy] = val;
}
return matrix;
};
let matrix = replaceRowCol([
[ 0, 2, 3, 4 ],
[ 1, 2, 3, 4 ],
[ 1, 2, 0, 4 ],
[ 1, 2, 3, 4 ]
]);
for (let row of matrix) console.log(row.join(' '));
这种方法不是将临时占位符值插入到矩阵中,而是执行第一遍以记住所有零的位置,然后在这些记住的位置上执行第二遍以替换行和列。
速度是避免回调和函数调用的结果,除了Array.prototype.push
(请注意,如果我们使用简单的链表可以避免这种情况)。尽管回调被编译并且在任何像样的 javascript VM 下都非常快,但它们仍然不如上面的旧程序代码那么快。
值得一提的是非常厚颜无耻的加速的可能性:如果需要替换多行,您可以将第一行中的每个项目设置为零,但对于之后的每一行,您可以引用第一个归零的行。这将完全跳过迭代后面行的需要。当然,生成的矩阵会有多行都引用同一个数组,这可能会在未来的操作中导致非常意外的结果。
这种厚颜无耻的加速看起来像这样:
let replacedX = null;
for (let { x, y } of replaceArr) {
// We still need to replace the column, as usual
for (let xx = 0; xx < w; xx++) matrix[xx][y] = val;
if (replacedX === null) {
replacedX = matrix[x] = Array(w).fill(val); // This has O(w) runtime
} else {
matrix[x] = replacedX; // This has O(1) runtime
}
}
编辑:链表实现。它包含在 jsperf 中,而且速度更快!
let replaceRowColLinkedList = (matrix, trg=0, val=0) => {
// Store width and height of matrix
let w = matrix.length;
let h = matrix[0].length;
// A linked list
let replacements = null;
// Any coord whose value is `trg` results in a replacement at that coord
for (let x = 0; x < w; x++) { for (let y = 0; y < h; y++) {
if (matrix[x][y] === trg) {
let next = replacements;
replacements = { x, y, next };
}
}}
// Perform all replacements
let llNode = replacements;
while (llNode) {
let { x, y, next } = llNode;
for (let xx = 0; xx < w; xx++) matrix[xx][y] = val;
for (let yy = 0; yy < h; yy++) matrix[x][yy] = val;
llNode = next;
}
return matrix;
};
let matrix = replaceRowColLinkedList([
[ 0, 2, 3, 4 ],
[ 1, 2, 3, 4 ],
[ 1, 2, 0, 4 ],
[ 1, 2, 3, 4 ]
]);
for (let row of matrix) console.log(row.join(' '));
编辑:对于具有许多冲突的大型矩阵(许多零共享同一行或列)可能值得使用Set
以避免重新归零行/列。当然,不利的一面是,无论实现如何,Set.prototype.add
它的运行时都必须比. O(1)
然而,总的来说,Set.prototype.add
对于较小的矩阵,运行时间可以忽略不计,而对于较大的矩阵,避免碰撞可以带来显着的好处。这种方法在jsperf中一直是最快的,也是我整体推荐的方法!
let replaceRowCol = (matrix, trg=0, val=0) => {
// Store width and height of matrix
let w = matrix.length;
let h = matrix[0].length;
// Store columns and rows to replace
let replaceX = new Set();
let replaceY = new Set();
// Any coord whose value is `trg` results in a replacement at that coord
for (let x = 0; x < w; x++) { for (let y = 0; y < h; y++) {
if (matrix[x][y] === trg) {
replaceX.add(x);
replaceY.add(y);
}
}}
// Perform all replacements
for (let x of replaceX) for (let y = 0; y < h; y++) matrix[x][y] = val;
for (let y of replaceY) for (let x = 0; x < w; x++) matrix[x][y] = val;
return matrix;
};
let matrix = replaceRowCol([
[ 0, 2, 3, 4, 5, 6 ],
[ 1, 2, 3, 4, 5, 6 ],
[ 1, 2, 3, 4, 5, 6 ],
[ 1, 2, 3, 0, 5, 6 ],
[ 1, 2, 3, 4, 5, 6 ]
]);
for (let row of matrix) console.log(row.join(' '));
推荐阅读
- python - 如何在 Pandas 的开头和结尾添加零值?
- ruby - 生成的 AWS PresignedPost 不包含正确的 url/字段
- laravel - Laravel Nova 组件中的 FullCalendar
- php - Wordpress 出现在错误的 cPanel 帐户上
- javascript - API 数据打印到节点中的控制台,但不显示在 html 中
- twitter-bootstrap - Bootstrap 4 html 工具提示正在删除内联样式
- flutter - 如何检测 Widget 何时获得或失去焦点 - Flutter
- javascript - 如何使用 SignalR 向特定用户发送数据?
- filepond - Filepond 水平预览图像
- python - Python:区分大小写的键盘记录器“