1. 程式人生 > >矩陣轉置演算法及程式碼實現(三元組順序表)

矩陣轉置演算法及程式碼實現(三元組順序表)

矩陣的轉置實際上就是將資料元素的行標和列標互換,即 T(i,j) = M(j,i) 。例如: 圖1 矩陣的轉置 相應地,三元組錶轉變為: 圖2 三元組表 矩陣的轉置,經歷了三個步驟:

  • 矩陣的行數 n 和列數 m 的值交換;
  • 將三元組中的i和j調換;
  • 轉換之後的表同樣按照行序(置換前的列序)為主序,進行排序;

實現三元組的轉換,重點在第三步,實現演算法有兩種。

普通演算法

普通演算法的實現過程為:
  1. 將矩陣的行數和列數進行調換;
  2. 遍歷表 a 的 j 列(查詢 j 的值,從 1 一直到未轉置之前的矩陣的列數 m ),遍歷的過程,就可以自動儲存為表 b 的形式。
因為在表 a 中 i 列的數值是從小到大的,在根據 j 列由上到下的遍歷時, i 列同樣也是有序的。 實現程式碼:
TSMatrix transposeMatrix(TSMatrix M,TSMatrix T){
    //行和列置換
    T.m=M.n;
    T.n=M.m;
    T.num=M.num;
   
    if (T.num) {
        int q=0;
        //依次遍歷M矩陣的列(從1開始),的遍歷的過程中將行標和列標置換,得到置換後的三元表T
        for (int col=1;col<=M.m; col++) {
            for (int p=0; p<M.num; p++) {
                if (M.data[p].j==col) {
                    T.data[q].i=M.data[p].j;
                    T.data[q].j=M.data[p].i;
                    T.data[q].data=M.data[p].data;
                    q++;
                }
            }
        }
    }
    return T;
}

此演算法的時間複雜度關鍵在於巢狀的兩個 for 迴圈,時間複雜度為O(m*num),和矩陣的列數以及非 0 元素的個數的乘積成正比,如果稀疏矩陣的非 0 元素很多的情況,使用這個演算法,雖然一定程度上節省了空間,但是時間複雜度會很高。

快速轉置演算法

快速轉置演算法在普通演算法的基礎上,對遍歷儲存的過程做了改進。

首先將每一列中非 0 元素的個數對應地儲存在一個數組(陣列名為array)中。在此基礎上,計算出每一列第一個元素存放在三元組表中的位置,儲存在陣列(陣列名為 cpot )中。 第一列第一個非 0 元素肯定存放在第一個位置,第二列第一個非 0 元素的位置 = 第一列存放的起始位置 + 第一列的非 0 元素個數,以此類推。
用圖 2 中置換之前的表舉例:

array 陣列中的資料表示,第一列有一個非 0 元素,第二列中 3 個非0元素。
 

cpot 陣列中的資料表示,第一列中第一個資料儲存的位置預設是 1 ,第二列第一個非 0 元素存放的位置是 2。

計算方法是:cpot[col] = cpot[col-1] + array[col-1],即後邊一列第一個非 0 元素存放的位置為前邊一列第一個非 0 元素存放的位置加上該列非 0 元素的個數的和。 在以上兩個陣列的基礎上,當遍歷表 a 的 j 列時,根據每個元素 j 列的數值,就可以判斷出它在表 b 中的存放位置,整個三元組表只需要遍歷一次,就能實現矩陣的轉置。

實現程式碼:
TSMatrix fastTransposeMatrix(TSMatrix M,TSMatrix T){
    //行和列置換
    T.m=M.n;
    T.n=M.m;
    T.num=M.num;
    if (T.num) {
        //建立並初始化array陣列
        int array[number];
       
        for (int col=1; col<=M.m; col++) {
            array[col]=0;
        }
        for (int t=0; t<M.num; t++) {
            int j=M.data[t].j;
            array[j]++;
        }
        //建立並初始化cpot陣列
        int cpot[T.m+1];
        cpot[1]=1;//第一列中第一個非0元素的位置預設為1
        for (int col=2; col<=M.m; col++) {
            cpot[col]=cpot[col-1]+array[col-1];
        }
        for (int p=0; p<M.num; p++) {
            //提取當前三元組的列數
            int col=M.data[p].j;
            //根據列數和cpot陣列,找到當前元素需要存放的位置
            int q=cpot[col];
            //轉置矩陣的三元組預設從陣列下標0開始,而得到的q值是單純的位置,所以要減1
            T.data[q-1].i=M.data[p].j;
            T.data[q-1].j=M.data[p].i;
            T.data[q-1].data=M.data[p].data;
            //存放完成後,cpot陣列對應的位置要+1,以便下次該列儲存下一個三元組
            cpot[col]++;
        }
    }
    return T;
}
這個演算法中含有四個並列的單迴圈,時間複雜度為O(m+num)(實際得到的是O(2*m+2*num),當 m 和 num 足夠大時,可以省略常數引數),即使最壞情況下,矩陣中的元素都是非 0 元素,時間負責度為O(m*n)。稱此演算法為快速轉置演算法

兩種演算法的完整程式碼

#include<stdio.h>
#define number 10
typedef struct {
    int i,j;
    int data;
}triple;
typedef struct {
    triple data[number];
    int rpos[number];
    int n,m,num;
}TSMatrix;

TSMatrix transposeMatrix(TSMatrix M,TSMatrix T){
    T.m=M.n;
    T.n=M.m;
    T.num=M.num;
   
    if (T.num) {
        int q=0;
        for (int col=1;col<=M.m; col++) {
            for (int p=0; p<M.num; p++) {
                if (M.data[p].j==col) {
                    T.data[q].i=M.data[p].j;
                    T.data[q].j=M.data[p].i;
                    T.data[q].data=M.data[p].data;
                    q++;
                }
            }
        }
    }
    return T;
}

TSMatrix fastTransposeMatrix(TSMatrix M,TSMatrix T){
    T.m=M.n;
    T.n=M.m;
    T.num=M.num;

    if (T.num) {
        int array[number];
        for (int col=1; col<=M.m; col++) {
            array[col]=0;
        }
        for (int t=0; t<M.num; t++) {
            int j=M.data[t].j;
            array[j]++;
        }
        int cpot[T.m+1];
        cpot[1]=1;
        for (int col=2; col<=M.m; col++) {
            cpot[col]=cpot[col-1]+array[col-1];
        }
        for (int p=0; p<M.num; p++) {
            int col=M.data[p].j;
            int q=cpot[col];
            T.data[q-1].i=M.data[p].j;
            T.data[q-1].j=M.data[p].i;
            T.data[q-1].data=M.data[p].data;
            cpot[col]++;
        }
    }
    return T;
}
int main() {
    TSMatrix M;
    M.m=2;
    M.n=3;
    M.num=4;
   
    M.data[0].i=1;
    M.data[0].j=2;
    M.data[0].data=1;
   
    M.data[1].i=2;
    M.data[1].j=2;
    M.data[1].data=3;
   
    M.data[2].i=3;
    M.data[2].j=1;
    M.data[2].data=6;
   
    M.data[3].i=3;
    M.data[3].j=2;
    M.data[3].data=5;
   
    TSMatrix T;
    T=transposeMatrix(M, T);
    printf("使用普通方法:\n");
    for (int i=0; i<T.num; i++) {
        printf("(%d,%d,%d)",T.data[i].i,T.data[i].j,T.data[i].data);
    }
    printf("\n");
    TSMatrix T1;
    T1=fastTransposeMatrix(M, T1);
    printf("使用改進方法:\n");
    for (int i=0; i<T.num; i++) {
        printf("(%d,%d,%d)",T.data[i].i,T.data[i].j,T.data[i].data);
    }
    return 0;
}
輸出結果 使用普通方法:
(1,3,6)(2,1,1)(2,2,3)(2,3,5)
使用改進方法:
(1,3,6)(2,1,1)(2,2,3)(2,3,5)