1. 程式人生 > >資料結構 有序陣列表示稀疏矩陣

資料結構 有序陣列表示稀疏矩陣

一般儲存矩陣,自然想到二維資料。但是對於稀疏矩陣(0項很多),這無疑浪費的大量的空間。所以,這裡考慮換一種表示方法。用一個三元組表示矩陣中的非零元素。

//(稀疏)矩陣資料結構, 待表示矩陣如下
// 15     0     0     22     0     -15
// 0     11     3     0     0     0
// 0     0     0     -6     0     0
// 0     0     0     0     0     0
// 91     0     0     0     0     0
// 0     0     28     0     0     0

#include <stdio.h>
#include <stdbool.h>
typedef struct {
     int col; // 列號
     int row; // 行號
     int value; // 值
} sparse_matrix;
void createMatrix(sparse_matrix *matrix);
void printMatrix(sparse_matrix *matrix);
void transposeMatrix(sparse_matrix *matrix, sparse_matrix *transposeMatrix);
#define GET_ARRAY_LEN(array,len) (len=(sizeof(array)/sizeof(array[0])));
int main() {   
     int length;
     sparse_matrix smatrix[9], transMatrix[9];
     createMatrix(smatrix);
     GET_ARRAY_LEN(smatrix,length);
     printMatrix(smatrix);
     printf("Array length is %d\n",length);
     transposeMatrix(smatrix, transMatrix);
     printf("Print transpose matrix: \n");
     printMatrix(transMatrix);
    return 0;
}
// 初始化稀疏矩陣
void createMatrix(sparse_matrix *matrix) {
     matrix[0].row = 6;// 0行元素儲存行數
     matrix[0].col = 6;// 0列元素儲存列數
     matrix[0].value = 8;
     matrix[1].row = 0;
     matrix[1].col = 0;
     matrix[1].value = 15;
     matrix[2].row = 0;
     matrix[2].col = 3;
     matrix[2].value = 22;
     matrix[3].row = 0;
     matrix[3].col = 5;
     matrix[3].value = -15;
     matrix[4].row = 1;
     matrix[4].col = 1;
     matrix[4].value = 11;
     matrix[5].row = 1;
     matrix[5].col = 2;
     matrix[5].value = 3;
     matrix[6].row = 2;
     matrix[6].col = 3;
     matrix[6].value = -6;
     matrix[7].row = 4;
     matrix[7].col = 0;
     matrix[7].value = 91;
     matrix[8].row = 5;
     matrix[8].col = 2;
     matrix[8].value = 28;
}

//列印稀疏矩陣
//最差時間複雜度:O(row * col * valueCount);
void printMatrix(sparse_matrix *matrix) {
     int i,j,k;
     int row = (*matrix).row;
     int col = (*matrix).col;
     int valueCount = (*matrix).value;
     int startIndex = 1;
     for (i = 0; i < row; i++) {
          for (k = 0; k < col; k++) {
               bool print = false;
               for (j = startIndex; j <= valueCount; j++) {
                    int curRow = (*(matrix + j)).row,
                    curCol = (*(matrix + j)).col;
                    if (i == curRow && k == curCol) {
                         printf("%d\t", (*(matrix+j)).value);
                         print = true;
                         startIndex++;
                    }
               }
               if (!print) {
                    printf("%d\t", 0);
               }
          }
          printf("\n");
     }
}
// 求稀疏矩陣的轉置矩陣,即交換行列的位置
// 最差時間複雜度:O(col * valueCount);
void transposeMatrix(sparse_matrix *smatrix, sparse_matrix *transposeMatrix) {
     int i,n, rowNum = (*smatrix).row, colNum = (*smatrix).col, valueNum = (*smatrix).value;
     (*transposeMatrix).row = colNum;
     (*transposeMatrix).col = rowNum;
     (*transposeMatrix).value = valueNum;
     int index = 1;
     for (i = 0; i < colNum; i++) {
          for (n = 1; n <= valueNum; n++) {
               int curRow = (*(smatrix + n)).row,
                    curCol = (*(smatrix + n)).col,
                    curValue = (*(smatrix + n)).value;
               if (i == curCol) {
                    (*(transposeMatrix + index)).row = curCol;
                    (*(transposeMatrix + index)).col = curRow;
                    (*(transposeMatrix + index)).value = curValue;
                    index++;
               }
          }
     }
}

可以看到,相對於傳統的使用二維資料的方式儲存矩陣,該儲存方式對於稀疏矩陣來說,無疑節省了大量空間。  但是對於矩陣的列印和轉置來說,從資料量級看浪費了時間。所以,此種表示方式適用於矩陣中非零元素少的稀疏矩陣。尤其當矩陣中非零元素數量為cols * rows時,轉置的時間複雜性為O(rows * cols * cols)。用時間換空間。

考慮再用少量空間,換一些時間。實現時間複雜度的為O(cols + elements)的快速轉置:

// 快速轉置演算法,浪費少量空間,換取時間
// 直接計算轉置後元素的存放位置
void fastTranspose(sparse_matrix *smatrix, sparse_matrix *transposeMatrix) {
     int i, rowNum = (*smatrix).row, colNum = (*smatrix).col, valueNum = (*smatrix).value;
     int row_terms[colNum + 1],
          startPos[colNum + 1];
     (*transposeMatrix).row = colNum;
     (*transposeMatrix).col = rowNum;
     (*transposeMatrix).value = valueNum;
     for (i = 0; i< colNum; i++) {
          row_terms[i] = 0; // 陣列中的元素必須初始化
     }
     for (i = 1; i <= valueNum; i++) {
          int     curCol = (*(smatrix + i)).col;
          row_terms[curCol]++;//記錄轉置後,每行的元素個數
     }
     startPos[0] = 1;//初始化起始位置
     for (i = 1; i < colNum; i++) {
          startPos[i] = startPos[i-1] + row_terms[i-1];
     }
      for (i = 1; i <= valueNum; i++) {
           int curRow = (*(smatrix + i)).row,
                curCol = (*(smatrix + i)).col,
                curValue = (*(smatrix + i)).value;
           int j = startPos[curCol]++;// 計算出原矩陣中的元素在轉置矩陣資料中的位置
           (*(transposeMatrix + j)).row = curCol;
           (*(transposeMatrix + j)).col = curRow;
           (*(transposeMatrix + j)).value = curValue;
      }
}

可見快速轉置的主要思想就是直接計算出元素在轉置矩陣中的順序位置,直接儲存。接下來是稀疏矩陣的乘法。

// 稀疏矩陣乘法運算, 以下面矩陣為例
// 0     0     1     1 * 0     0
//                         0     0 = 2     5
//                         0     2
//                         2     3
void matrixMulti(sparse_matrix *amatrix, sparse_matrix *bmatrix, sparse_matrix *resultMatrix)
{ int i, aRowNum = (*amatrix).row, aValueNum = (*amatrix).value; int j, bColNum = (*bmatrix).col, bValueNum = (*bmatrix).value; (*resultMatrix).row = aRowNum;//首先可知結果矩陣的行列數 (*resultMatrix).col = bColNum; sparse_matrix tbMatrix[9];// 為了計算方便先求出被乘矩陣的轉置矩陣,下面計算都利用轉置矩陣 fastTranspose(bmatrix, tbMatrix); int temp_sum = 0, row = (*(amatrix + 1)).row, // 矩陣A的起始行,即為結果的起始行 col, rowBegin = 1, rIndex = 0; // 在迴圈內部控制迴圈的次數和起始 for (i = 1; i <= aValueNum; ) { col = (*(tbMatrix + 1)).row; // 矩陣B的轉置矩陣的行,即對應的列 for (j = 1; j <= bValueNum + 1; ) { if (amatrix[i].row != row) { storeValue(resultMatrix, row, col, temp_sum, &rIndex); i = rowBegin; for (; col == tbMatrix[j].row; j++) ; col = tbMatrix[j].row; temp_sum = 0; } else if (col != tbMatrix[j].row) { storeValue(resultMatrix, row, col, temp_sum, &rIndex); i = rowBegin; col = tbMatrix[j].row; temp_sum = 0; } else { if (amatrix[i].col < tbMatrix[j].col) { i++; } else if (amatrix[i].col == tbMatrix[j].col) { temp_sum += amatrix[i].value * tbMatrix[j].value; i++; j++; } else { j++; } } } for (; row == (*(amatrix + i)).row; i++) ; rowBegin = i; row = (*(amatrix + i)).row; } (*resultMatrix).value = rIndex; } void storeValue(sparse_matrix *resultMatrix, int row, int col, int value, int *index) { if (value) { (*(resultMatrix + ++*index)).row = row; (*(resultMatrix + *index)).col = col; (*(resultMatrix + *index)).value = value; } }

沒有做太多錯誤邊界的判斷。計算結果:

初學乍練,如果有錯誤還望大家指出。這個乘法,OneCoder著實寫了好久。演算法的時間複雜度為:O(bColNum * aValueNum + aRowNum * bValueNum)。相對於傳統的二維陣列表示矩陣的計算來說,實現邏輯複雜好多。傳統方式的時間複雜度為:O(aRowNum * aColNum * bColNum)。