最紧凑的表示形式是单个数组中的三角矩阵,除非您真的空间不足,否则我不建议这样做。
function triMatrix(n) { // not really needed
return new Array(n * (n + 1) / 2);
}
function trindex(row, col) {
if (col > row) {
var tmp = row; row = col; col = tmp;
}
return row * (row + 1) / 2 + col;
}
function triStore(tri, indexOfKey, rowKey, colKey, value) {
tri[trindex(indexOfKey[rowKey], indexOfKey[colKey])] = value;
}
function triGet(tri, indexOfKey, rowKey, colKey) {
return tri[trindex(indexOfKey[rowKey], indexOfKey[colKey])];
}
const keyOfIndex = ['Y', 'R', 'W'];
const indexOfKey = {'Y': 0, 'R': 1, 'W': 2}; // can be calculated
const N = keyOfIndex.length;
var tri = triMatrix(N); // could also be var tri = [];
triStore(tri, indexOfKey, 'Y', 'Y', 'Y');
triStore(tri, indexOfKey, 'Y', 'R', 'Y');
triStore(tri, indexOfKey, 'Y', 'W', 'Y');
triStore(tri, indexOfKey, 'R', 'R', 'R');
triStore(tri, indexOfKey, 'R', 'W', 'P');
triStore(tri, indexOfKey, 'W', 'W', 'W');
tri; // => [ "Y", "Y", "R", "Y", "P", "W" ]
triGet(tri, indexOfKey, 'R', 'W'); // => "P"
triGet(tri, indexOfKey, 'W', 'R'); // => "P"
关键是:你的矩阵是对称的,所以你只需要它的上三角矩阵或下三角矩阵(包括对角线)。在您的建议中,您存储上三角矩阵,在我的建议中,我存储下三角矩阵,因为索引计算要简单得多。该数组将包含 1 个元素的第 1 行、2 个中的第 2 行、3 个中的第 3 行等。只要记住 1+2+...+n=n(n+1)/2,您就会明白如何计算数组索引。
M₀₀ M₀₁ M₀₂ M₀₀ T₀
M₁₀ M₁₁ M₁₂ => M₁₀ M₁₁ => T₁ T₂ => T₀ T₁ T₂ T₃ T₄ T₅
M₂₀ M₂₁ M₂₂ M₂₀ M₂₁ M₂₂ T₃ T₄ T₅
矩阵很容易扩展 1 行/列,无需重新索引数组:
M₀₀ M₀₁ M₀₂ M₀₃ M₀₀ T₀
M₁₀ M₁₁ M₁₂ M₁₃ M₁₀ M₁₁ T₁ T₂
M₂₀ M₂₁ M₂₂ M₂₃ => M₂₀ M₂₁ M₂₂ => T₃ T₄ T₅ => T₀ ... T₆ T₇ T₈ T₉
M₃₀ M₃₁ M₃₂ M₃₃ M₃₀ M₃₁ M₃₂ M₃₃ T₆ T₇ T₈ T₉
作为练习,我将以上内容大致翻译为 PHP,这是您的目标语言。尽管我一开始不推荐“单个数组中的三角矩阵”方法,但如果它符合您的需求(如果不符合,也许我可以提供帮助),欢迎您将以下类用作黑盒。
class SymmetricMatrix
{
private $n = 0;
private $triangular = [];
private $key_of_index = [];
private $index_of_key = [];
private function add_key_if_necessary($key) {
if ( !isset($this->index_of_key[$key])) {
$index = $this->n++;
$this->index_of_key[$key] = $index;
$this->key_of_index[$index] = $key;
for ($i = 0; $i < $this->n; $i++) {
$this->triangular[] = false; // avoid "jumping" index & init to "absent"
}
}
}
private static function trindex($row, $col) {
if ($col > $row) {
$tmp = $row; $row = $col; $col = $tmp;
}
return $row * ($row + 1) / 2 + $col;
}
public function put($key1, $key2, $value) {
$this->add_key_if_necessary($key1);
$this->add_key_if_necessary($key2);
$trindex = self::trindex($this->index_of_key[$key1], $this->index_of_key[$key2]);
$this->triangular[$trindex] = $value;
}
public function get($key1, $key2) {
if (!isset($this->index_of_key[$key1]) || !isset($this->index_of_key[$key2])) {
return false;
}
$trindex = self::trindex($this->index_of_key[$key1], $this->index_of_key[$key2]);
return $this->triangular[$trindex];
}
public function find_first($value) { // $value !== false
for ($row = 0; $row < $this->n; $row++) {
for ($col = 0; $col <= $row; $col++) {
$trindex = trindex($row, $col);
if ($this->triangular[$trindex] === $value) {
return [$this->key_of_index[$row], $this->key_of_index[$col]];
}
}
}
return false;
}
public function get_keys() {
return $this->key_of_index;
}
public function dump() {
var_export($this);
echo "\n";
}
}
$m = new SymmetricMatrix();
$m->put('Y', 'Y', 'Y');
$m->put('Y', 'R', 'Y');
$m->put('Y', 'W', 'Y');
$m->put('R', 'R', 'R');
$m->put('R', 'W', 'P');
$m->put('W', 'W', 'W');
$m->dump();
echo "keys: ", implode(', ', $m->get_keys()), "\n";
echo "m[R][W]: ", $m->get('R', 'W'), "\n";
echo "m[W][R]: ", $m->get('W', 'R'), "\n";