Предполагая, что это возможно в вашем случае, вы можете пойти на классическую стратегию торговли временем для пространства.
(Я предполагаю, что вы используете OO-язык - идея подходит и для не-OO-языков, но потребует больше усилий для обеспечения синхронизации различных представлений одной и той же матрицы).
Вместо представления матрицы в виде массива (или вместе с этим представлением), как насчет сохранения ее в виде набора ненулевых значений?
Итак, что вы представляете как:
|5 6 9 2|
|1 8 4 0|
|5 8 0 0|
|7 0 0 0|
станет (или сохранится также)
1,1=5
1,2=6
1,3=9
...
4,1=7
если это выполнимо, вы также можете разделить этот набор на две части (верхняя и нижняя диагонали)
так в случае вашей первой матрицы:
|5 6 9 2|
|1 8 4 9|
|5 8 1 6|
|7 6 3 2|
будет:
UpperMap -
1,1=5
1,2=6
1,3=9
...
4,1=7
Lower Map-
2,4=9
3,3=1
...
4,4=2
В этом случае ваш тест будет «спрашивать, пуста ли нижняя хэш-карта».
Как уже упоминалось выше, если вам нужно выполнить больше «традиционных» операций над матрицей, вы можете сохранить ее как массив вместе с 2-картами, и в случае, если матрицы не являются неизменяемыми, вам придется предоставить методы для изменить значения "ячейки".
Другой компромисс (кроме пробелов) заключается в том, что создание новой матрицы требует больше времени процессора. Но это может стоить того, если вы создаете и изменяете их нечасто и вам приходится часто проверять нижнюю диагональ.
Более экстремальный подход:
Для каждой матрицы построить растровое представление (ненулевые ячейки равны 1, нулевые ячейки получают 0) и использовать логические операции для проверки «пустоты» секции.