我有一个如下所示的稀疏矩阵,包括数据单元格(1..9)和空单元格(= 0):
[
[ 1, 2, 0, 3 ],
[ 0, 4, 0, 0 ],
[ 5, 6, 7, 8 ],
]
我想把它显示为一个html表,但是应该没有空单元格 - 它们应该被它们的相邻数据单元的行和colspans“覆盖”:
<table border=1 cellpadding=10>
<tr>
<td rowspan=2>1</td>
<td colspan=2>2</td>
<td>3</td>
</tr>
<tr>
<td colspan=3>4</td>
</tr>
<tr>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
</tr>
</table>
(这是一种可能的实现,我们也可以在第二行使用colspan=4
而不使用rowspan。)
生成实际的html不是问题,但我在编写算法来计算数据单元的行和列跨度时遇到了麻烦。
编辑:仍在寻找答案。仅使用colspans并在每个数据单元的左/右连接空单元格似乎是微不足道的。但是,我希望单元格尽可能为方形,所以答案应该包括rowspan逻辑。谢谢!
EDIT2 :到目前为止所有答案总结如下:http://jsfiddle.net/ThQt4/
答案 0 :(得分:3)
您希望它尽可能呈方形吗?我想说,你想优先考虑最大的区域。 现在到了算法。首先,让矩阵变大一点:
这就是我想出的地方:
它正在使用这些简单的步骤:
完成。
var Omatrix = [ //Original matrix
[ 1, 2, 0, 3, 4, 0, 2 ],
[ 0, 4, 0, 0, 7, 0, 4 ],
[ 5, 6, 7, 8, 0, 1, 0 ],
[ 1, 0, 0, 0, 4, 0, 0 ],
[ 0, 4, 0, 0, 7, 2, 4 ],
[ 5, 0, 0, 0, 0, 3, 0 ],
[ 0, 0, 0, 0, 4, 9, 3 ],
];
var matrix = []
for (var i = 0; i < Omatrix.length; i++){
matrix[i] = Omatrix[i].slice(0);
}
//calculating all possible lengths
var a = matrix[0].length;
//maximum rectangle width
var b = matrix.length;
//maximum rectangle height
//calculate the area of each rectangle and save it using an array
var array = [];
for (var i = 1; i <= a; i++) {
for (var j = 1; j <= b; j++) {
array.push({"width":i, "height":j, "area":i*j, "square":i===j});
}
}
//sort first on square, then on area: biggest first
array.sort(function(a, b){
if(a.square === b.square){
x = b.area;
y = a.area;
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}
else{
return a.square? -1:1;
}
});
for(var i = 0; i < array.length; i++){
//working from biggest area to smallest
for(var j = 0; j < matrix.length-array[i].width+1; j++){
//working from left to right
notfound:
for(var k = 0; k < matrix[0].length-array[i].height+1; k++){
//working from top to bottom
var nonzero = false;
var zero = false
//checking if there is exactly one number and atleast one zero
for(var l = 0; l < array[i].width; l++){
//working from left to right
for(var m = 0; m < array[i].height; m++){
//working from top to bottom
if(matrix[j+l][k+m] > 0){
if(!nonzero){
//we have found the first number
//and saved that number for later use
nonzero = matrix[j+l][k+m];
}
else{
//we have found a second number:
//break and go to the next square
continue notfound;
}
}
else if(matrix[j+l][k+m] === 0){
//this is a zero
zero = true;
}
else{
//there is already a square build from this block
continue notfound;
}
}
}
if(!nonzero || !zero){
//there isn't a number here
//or there isn't a zero here
continue notfound;
}
//mark the spots with '-'
for(var l = 0; l < array[i].width; l++){
for(var m = 0; m < array[i].height; m++){
matrix[j+l][k+m] = "-";
}
}
//pack the head of the block with data
matrix[j][k] = array[i].width+"x"+array[i].height+"x"+nonzero;
}
}
}
var tablestring = "";
for(var i = 0; i < Omatrix.length; i++){
tablestring += "<tr>";
for(var j = 0; j < Omatrix[i].length; j++){
tablestring += "<td>"+Omatrix[i][j]+"</td>";
}
tablestring += "</tr>";
}
document.getElementById("table1").innerHTML += tablestring;
var tablestring = "";
for(var i = 0; i < Omatrix.length; i++){
tablestring += "<tr>";
for(var j = 0; j < Omatrix[i].length; j++){
//going trough all the cells
if(matrix[i][j] === "-"){
//a cell with a "-" will not be displayed
continue;
}
else if(typeof(matrix[i][j]) === "string"){
//a cell with a string is the head of a big block of cells.
var add = "";
var data = matrix[i][j].split("x");
if(data[0] !== "1"){
add += " rowspan="+data[0];
}
if(data[1] !== "1"){
add += " colspan="+data[1];
}
tablestring += "<td"+add+">"+data[2]+"</td>";
}
else{
//a normal cell
tablestring += "<td>"+Omatrix[i][j]+"</td>";
}
}
tablestring += "</tr>";
}
document.getElementById("table2").innerHTML += tablestring;
答案 1 :(得分:3)
你想要的东西要考虑几件事。
如果你总是喜欢在最大的形状上使用正方形,那么最终可能会有很多单行的柱子,因为矩形总是以正方形开头。只有当左上角的部分有值时,方形或矩形才能开始。
考虑这个数组:
[1, 2, 3, 4, 5],
[7, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[1, 2, 0, 0, 5]
如果你在最大的形状上选择正方形,你最终会得到一个正方形和三列
[1] [2] [3] [4] [5] instead of [1] [2] [3] [4] [5]
[ 7 ] [ ] [ ] [ ] [ 7 ]
[ ] [ ] [ ] [ ] [ ]
此外,您可能最终得到空单个单元格:
[1, 1, 2, 0], gives [1] [1] [ 2 ]
[3, 0, 0, 0], [ ] [?] <---
[0, 0, 0, 5], [ 3 ] [5]
[0, 0, 0, 1] [ ] [1]
这里最大的正方形是从3开始的3x3大小。如果你首先声明3x3然后是以2开头的行,你最终会得到一个高于5的空单元格[?]
。
最后一件事,如果你在顶行或左栏中有零,你也可能会遇到麻烦:
V
[1, 0, 0, 2], will leave you with [ 1 ] [?] [2]
[0, 0, 3, 4], [ ] [3] [4]
[0, 5, 6, 7] --->[?] [5] [6] [7]
那就是说,也许你的数组并不复杂,也许你不介意空单元格。无论哪种方式,使用您的规格解决这个难题很有趣。我的解决方案遵循以下规则:
1)方块先行。
2)尺寸很重要。
越大越好3)行和列:大小很重要。最大的先行。
4)Colspan超过了rowpan
var maxv = arr.length;
var maxh = arr[0].length;
var rsv = [];
var rsh = [];
pop_arr();
var shape;
try_sq();
try_rect();
try_loose();
//TRY SQUARES FIRST
function try_sq() {
var sv, sh;
shape = [];
for (sv = 0; sv < maxv - 1; sv++) {
for (sh = 0; sh < maxh - 1; sh++) {
if (arr[sv][sh] === 0 || rsv[sv][sh] > -1 || rsh[sv][sh] > -1) {
continue;
}
check_sq(sv, sh);
}
}
if (shape.length > 0) {
occu();
}
}
//TRY RECTANGLES
function try_rect() {
var sv, sh;
var rect = false;
do {
shape = [];
for (sv = 0; sv < maxv; sv++) {
for (sh = 0; sh < maxh; sh++) {
if (arr[sv][sh] === 0 || rsv[sv][sh] > -1 || rsh[sv][sh] > -1) continue;
check_rec(sv, sh);
}
}
if (shape.length > 0) {
occu();
} else {
rect = false;
}
} while (rect === true);
}
//TRY LOOSE
function try_loose() {
var sv, sh;
//SET THE 1x1 with value
for (sv = 0; sv < maxv; sv++) {
for (sh = 0; sh < maxh; sh++) {
if (arr[sv][sh] !== 0 && (rsv[sv][sh] == -1 || rsh[sv][sh] == -1)) {
rsv[sv][sh] = 1;
rsh[sv][sh] = 1;
}
}
}
//SEARCH FOR rectangles wit no value
var rect = true;
do {
shape = [];
for (sv = 0; sv < maxv; sv++) {
for (sh = 0; sh < maxh; sh++) {
if (arr[sv][sh] !== 0 || (rsv[sv][sh] > -1 || rsh[sv][sh] > -1)) {
continue;
}
rect = check_loose(sv, sh);
}
}
if (shape.length > 0) occu();
else {
rect = false;
}
} while (rect === true);
}
//check SINGLES
function check_loose(start_v, start_h) {
var vd, hd, iv, ih, rect;
var hor = ver = 1;
var horb = 0;
var mxv = maxv - 1;
var mxh = maxh - 1;
rect = true;
vd = start_v + ver;
hd = start_h + hor;
//check horizontal
for (sh = start_h + 1; sh <= mxh; sh++) {
if (arr[start_v][sh] !== 0 || rsh[start_v][sh] > -1) {
break;
}
hor++;
}
//check vertical
for (iv = start_v + 1; iv <= mxv; iv++) {
if (arr[iv][start_h] !== 0 || rsh[iv][start_h] > -1) {
break;
}
ver++;
}
if (hor > ver || hor == ver) {
shape.unshift({
0: (hor),
1: [start_v, start_h, 1, hor]
});
return true;
} else if (ver > hor) {
shape.push({
0: (ver),
1: [start_v, start_h, ver, 1]
});
return true;
}
return false;
}
//check SQUARE
function check_sq(start_v, start_h) {
if (arr[start_v + 1][start_h] !== 0) {
return false;
}
if (arr[start_v][start_h + 1] !== 0) {
return false;
}
var vd, hd, sv, sh, square;
var hor = ver = 1;
var mxv = maxv - 1;
var mxh = maxh - 1;
//CHECK DIAGONAL
do {
square = true;
vd = start_v + ver;
hd = start_h + hor;
//diagonal OK
if (arr[vd][hd] !== 0) {
if (hor == 1) {
if (ver == 1) {
return false;
}
square = false;
break;
}
}
//check horizontal
for (sh = start_h; sh <= hd; sh++) {
if (arr[vd][sh] !== 0) {
square = false;
break;
}
}
if (square === false) break;
//check vertical
for (sv = start_v; sv <= vd; sv++) {
if (arr[sv][hd] !== 0) {
square = false;
break;
}
}
if (square === false) break;
hor++;
ver++;
} while (square === true && vd < mxv && hd < mxh);
//SQUARE OK
if (hor > 1 && ver > 1 && hor == ver) {
shape.push({
0: (hor * ver),
1: [start_v, start_h, ver, hor]
});
}
}
//check RECTANGLE
function check_rec(start_v, start_h) {
var vd, hd, iv, ih, rect;
var hor = ver = 1;
var horb = 0;
var mxv = maxv - 1;
var mxh = maxh - 1;
rect = true;
vd = start_v + ver;
hd = start_h + hor;
//check horizontal
if (start_h < maxh) {
for (sh = start_h + 1; sh <= mxh; sh++) {
if (arr[start_v][sh] !== 0 || rsh[start_v][sh] > -1) break;
hor++;
}
}
//check vertical
if (start_v < maxv) {
for (iv = start_v + 1; iv <= mxv; iv++) {
if (arr[iv][start_h] !== 0 || rsh[iv][start_h] > -1) break;
ver++;
}
}
if (hor == 1 && ver == 1) return false;
if (hor > ver || hor == ver) {
shape.unshift({
0: (hor),
1: [start_v, start_h, 1, hor]
});
return true;
} else {
shape.push({
0: (ver),
1: [start_v, start_h, ver, 1]
});
return true;
}
return false;
}
//FIND LARGEST SHAPE
function occu() {
var le = shape.length;
for (var i = 0; i < le; i++) {
var b = Math.max.apply(Math, shape.map(function (v) {
return v[0];
}));
for (var j = 0; j < shape.length; j++) {
if (shape[j][0] == b) break;
}
var c = shape.splice(j, 1);
claim(c[0][1]);
}
}
//CLAIM SHAPE
function claim(sh) {
var iv, ih;
for (iv = sh[0]; iv < sh[0] + sh[2]; iv++) {
for (ih = sh[1]; ih < sh[1] + sh[3]; ih++) {
if (rsv[iv][ih] > -1 || rsh[iv][ih] > -1) return false;
}
}
for (iv = sh[0]; iv < sh[0] + sh[2]; iv++) {
for (ih = sh[1]; ih < sh[1] + sh[3]; ih++) {
rsv[iv][ih] = 0;
rsh[iv][ih] = 0;
}
}
rsv[sh[0]][sh[1]] = sh[2];
rsh[sh[0]][sh[1]] = sh[3];
}
function pop_arr() {
var em = [];
em[0] = arr[0].concat();
for (var i = 0; i < maxh; i++) {
em[0][i] = -1;
}
for (i = 0; i < maxv; i++) {
rsv[i] = em[0].concat();
rsh[i] = em[0].concat();
}
}
答案 2 :(得分:3)
有趣的问题。这适用于大多数浏览器。它使用数组方法reduce,这对于这些类型的问题是完美的。
var matrix = [
[ 1, 2, 0, 3 ],
[ 0, 4, 0, 0 ],
[ 5, 6, 7, 8 ],
];
var table = matrix.reduce(function(rows, row){
rows.push(row.reduce(function(newRow, col){
var lastCol = newRow[newRow.length - 1];
if (!col && lastCol) {
lastCol.colspan = (lastCol.colspan || 1) + 1;
} else if (!col && !lastCol) {
var lastRowCol = rows[rows.length-1][newRow.length];
if (lastRowCol) lastRowCol.rowspan = (lastRowCol.rowspan || 1) + 1;
} else {
newRow.push({value:col});
}
return newRow;
}, []));
return rows;
}, []);
var expected = [
[{value : 1, rowspan: 2}, {value:2, colspan: 2}, {value:3}],
[{value : 4, colspan: 3}],
[{value : 5}, {value:6}, {value:7}, {value:8}]
];
table.should.eql(expected); // Passed OK
答案 3 :(得分:2)
注意
当我写这个答案时,我还没有读过你的评论,你表达了对sqare表示的偏好(即:colspan = 2 rowspan = 2 for 3)。一旦找到时间,我会添加一个更灵活的答案。
简单的循环方法可能足以解决这个问题:
var cells = [
[ 1, 2, 0, 3 ],
[ 0, 4, 0, 0 ],
[ 5, 6, 7, 8 ]
],i, j, c, r,tmpString, tbl = [];
for (i=0;i<cells.length;++i)
{
tbl[i] = [];
for (j=0;j<cells[i].length;++j)
{
if (cells[i][j] !== 0)
{
if (j === 0)
{//This only works for first indexes (if not, cells[0][3] + cells[1][3] yields rowspan, too)
r = 1;
while(i+r < cells.length && cells[i+r][j] === 0)
++r;//while next row == 0, add rowspan count
}
c = 1;
while(c+j < cells[i].length && cells[i][j+c] === 0)
++c;
tmpString = '<td';
if (j === 0 && r > 1)
tmpString += ' rowspan="' + r + '"';
if (c > 1)
tmpString += ' colspan="' + c + '"';
tmpString += '>'+cells[i][j]+'</td>';
tbl[i].push(tmpString);
}
}
tbl[i] = tbl[i].join('');
}
console.log('<table border=1 cellpadding=10><tr>' + tbl.join('</tr><tr>') + '</tr></table>');
varnames是笨拙的(充其量),但名称是i
和j
只是简单的索引/循环计数器变量。 c
计算在任何给定时间都需要的colspans,r
对rowpans执行相同的操作。 tbl
是一个数组数组,用于构建表字符串
tmpString
只是一个用于将每个单元连接在一起的变量。其存在的主要原因是避免可怕的陈述,如:
tmp[i].push('<td' + (j === 0 && r > 1 ? ' rowspan="' + r + '"' : '')
+ (c > 1 ? ' colspan="' + c +'"' : '') + '>' + cells[i][j] + '</td>'
);
无论如何,在添加了一些缩进之后,输出是:
<table border=1 cellpadding=10>
<tr>
<td rowspan="2">1</td>
<td colspan="2">2</td>
<td>3</td>
</tr>
<tr>
<td colspan="3">4</td>
</tr>
<tr>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
</tr>
</table>
我认为,这非常接近你所追求的目标
答案 4 :(得分:1)
从您的示例中,colspan是一个加上空单元格右侧的空单元格数,并且rowspan类似于一个加上空单元格下方的空单元格数。
当事情变得更有趣时,无空单元格在右侧和下方都有空单元格。你想怎么处理这种情况?
答案 5 :(得分:1)
此处在您的Java脚本中,您必须执行以下操作
var str="<table border=1 cellpadding=10>"
var rows=data.lenght();
if(rows>0)
{
for(int r=0;r<rows;r++)
{
var cols=data[r].lenght();
if(cols>0)
{
str+="<tr>";
for(int c=0;c<cols;c++)
{
var colstospan=getnext0(r,c,0); //This function will give you columns to be span... defination given below
str+="<td colspan="+colstospan+">"+data[r][c]+"</td>";
}
str+="</tr>";
}
}
}
str+="</table>";
str will give you table you require...
以下是getnext0
function getnext0(rows,cols,count)
{
if(data[rows][cols+1]==0)
{
return getnext0(rows,cols+1,count+1);
}
return count;
}
我希望这对你有用......
请忽略lenght()
函数中的语法错误...