矩陣是線性代數中的一個知識,剛開始學習的時候可能感覺不到它有什麼用處,最初的感覺就是對二維資料的操作。其實現實生活中矩陣的用處太大了,設計領域相當的廣泛。在此只討論稀疏矩陣的轉置問題;
可能看到矩陣就會想到二維陣列,比如這樣一個矩陣:
你可能會想到用二維陣列來存放此矩陣中的元素,就像這樣:int text[][5] = {{0,5,6,0,4},{0,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{0,2,0,0,1}};
這樣好像也沒有什麼不好。我們再來看看這個矩陣,五行五列,可以包含二十五個元素,但是此矩陣只有七個元素。但是我們在存放資料的時候分配了二十五塊int單元。這樣是不是有點太浪費了。如果我們只儲存這七個元素我想會節省一部分記憶體空間。但是如果我們只儲存矩陣中的元素還是不行的,因為只有元素我們就無法還原矩陣,我們還需要此元素的行列值。這樣就好辦了。我們宣告一個結構體來表示一個元素。就像這樣:
1 2 3 4 5 6 |
typedef struct juzhen { int row; //行 int col; //列 int value; //元素值 }; |
這樣儲存一個元素就會用到三個儲存單元,七個就是二十一個儲存單元,可能與二十五個沒多大差別,但是如果矩陣的行列是一個很大的值,而且又是稀疏矩陣,這樣做就可以節省很大的空間。這種儲存結構只限於稀疏矩陣。
解決了儲存結構,就開始矩陣的轉置吧!!!
首先我們需要一個矩陣,就按照上圖給的矩陣好了,按照此矩陣做一個二維陣列:
1 |
int text[][5] = {{0,5,6,0,4},{0,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{0,2,0,0,1}}; |
就像這樣;我們需要定義一個陣列來表示稀疏矩陣,並賦值;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#define MAX_TERM 15 struct juzhen a[MAX_TERM]; //存放矩陣中元素數值不為零的元素 int chushi(struct juzhen a[MAX_TERM]) //初始化稀疏矩陣 { int count_value = 0; //統計矩陣中元素數值不為零的元素的總和 int i,j; int count_a = 1; for(i = 0;i < N;i++) { for(j = 0;j < N;j++) { if(text[i][j] != 0) { a[count_a].row = i; a[count_a].col = j; a[count_a].value = text[i][j]; count_a++; } } } a[0].col = 5; //矩陣的總列數 a[0].row = 5; //矩陣的總行數 a[0].value = --count_a; //矩陣中的元素個數 return count_a; } |
在初始化矩陣陣列的時候為了方便轉置矩陣時的操作,我們把陣列的第一個元素設定為矩陣的列數,行數和元素總數;
矩陣有了,存放矩陣元素的陣列也有了。接下來就是轉置矩陣的函式了。
我們在轉置矩陣的時候會需要一個陣列來儲存轉置後的矩陣,定義為:
1 |
struct juzhen b[MAX_TERM];//轉置後的矩陣 |
主要思想,兩層迴圈,第一層迴圈控制矩陣的行,第二層迴圈控制陣列a的行。由於轉置矩陣即把矩陣中元素的列行對換一下,並且按照行排序;所以我們在第二層迴圈中做一個判斷,if(a[j].col == i) 【i控制第一層迴圈,j控制第二層迴圈】 如果為真值則執行:
1 2 3 |
b[count_b].row = a[j].col; b[count_b].col = a[j].row; b[count_b].value = a[j].value; |
整個函式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void zhuanzhi_1(struct juzhen a[MAX_TERM],struct juzhen b[MAX_TERM]) //轉置矩陣方法一 { int i,j; int count_b = 1; //b的當前元素下標 b[0].row = a[0].col; b[0].col = a[0].row; b[0].value = a[0].value; for(i = 0;i < a[0].col;i++) { for(j = 1;j <= a[0].value;j++) { if(a[j].col == i) //有種排序效果 { b[count_b].row = a[j].col; b[count_b].col = a[j].row; b[count_b].value = a[j].value; count_b++; } } } } |
用此方法可以有效的轉置矩陣,我們來看一下此函式的時間複雜度:O(cols * elements)——矩陣的列*矩陣的元素總和;
如果元素很多就會浪費很多的時間。有沒有辦法讓兩層迴圈變成一層迴圈呢?付出空間上的代價,換取時間效率;
我們只用一層迴圈來遍歷陣列a中所有元素,並把該元素放到指定的位置。這樣我們就需要一個陣列star來存放第i個元素所在位置。在定義這個陣列之前,我們還需要一個陣列term來實現統計矩陣第i行元素的數量。這樣我們才能更方便的知道第i個元素應該存放的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int term[N],star[N]; //儲存轉置矩陣第i行元素的數量 儲存第i行開始位置 int n = a[0].value; int i,j,k; int b_star; for(i = 0;i < N;i++) term[i] = 0; for(j = 0;j <= n;j++) term[a[j].col]++; star[0] = 1; for(k = 1;k < N;k++) star[k] = star[k - 1] + term[k - 1]; |
第一個迴圈初始化term,每個元素都為零。第二個迴圈是為了統計第i行元素的數量。第三個迴圈是設定第i個元素所在的位置。因為陣列a的第一個元素是存放行列和元素的總數,因此第三個迴圈要從k = 1開始。此時兩個陣列的元素為:
下一步就是遍歷a中的所有元素,然後根據a[i].col的值來把a[i].value放到指定的位置。
1 2 3 4 5 6 7 8 9 10 |
b[0].col = a[0].col; b[0].row = a[0].row; b[0].value = a[0].value; for(i = 1;i <= n;i++) { b_star = star[a[i].col]++; b[b_star].col = a[i].row; b[b_star].row = a[i].col; b[b_star].value = a[i].value; } |
需要注意的是b的第一個元素與a中的第一個元素是同樣的。b_star = star[a[i].col]++;因為當term[1] = 2;而star[1] = 3;就是a[i].col = 1時有兩個元素,第一個元素的位置是star[a[i].col];而第二個元素的位置就是star[a[i].col] + 1所以在此用star[a[i].col]++。為下一個元素設定相應的位置;
完整函式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void zhuanhuan_2(struct juzhen a[MAX_TERM],struct juzhen b[MAX_TERM]) { int term[N],star[N]; //儲存轉置矩陣第i行元素的數量 儲存第i行開始位置 int n = a[0].value; int i,j,k; int b_star; for(i = 0;i < N;i++) term[i] = 0; for(j = 1;j <= n;j++) term[a[j].col]++; star[0] = 1; for(k = 1;k < N;k++) star[k] = star[k - 1] + term[k - 1]; b[0].col = a[0].col; b[0].row = a[0].row; b[0].value = a[0].value; for(i = 1;i <= n;i++) { b_star = star[a[i].col]++; b[b_star].col = a[i].row; b[b_star].row = a[i].col; b[b_star].value = a[i].value; } } |
此函式每個迴圈體的執行次數分別為cols cols elements elements 時間複雜度為O(cols + elements)和O(cols * elements)相差好多,尤其是clos 和 elements很大的時候;
完整的測試程式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
#include<stdio.h> #define N 5 #define MAX_TERM 15 typedef struct juzhen { int row; //行 int col; //列 int value; //元素值 }; int text[][5] = {{0,5,6,0,4},{0,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{0,2,0,0,1}}; struct juzhen a[MAX_TERM]; //存放矩陣中元素數值不為零的元素 struct juzhen b[MAX_TERM]; //轉置後的矩陣 int chushi(struct juzhen a[MAX_TERM]) //初始化稀疏矩陣 { int count_value = 0; //統計矩陣中元素數值不為零的元素的總和 int i,j; int count_a = 1; for(i = 0;i < N;i++) { for(j = 0;j < N;j++) { if(text[i][j] != 0) { a[count_a].row = i; a[count_a].col = j; a[count_a].value = text[i][j]; count_a++; } } } a[0].col = 5; //矩陣的總列數 a[0].row = 5; //矩陣的總行數 a[0].value = --count_a; //矩陣中的元素個數 return count_a; } void showjuzhen(struct juzhen a[MAX_TERM],int count_a) //顯示稀疏矩陣 { int i,j; int text = 1; for(i = 0;i < N;i++) { for(j = 0;j < N;j++) { if(a[text].row == i && a[text].col == j) { printf(" %d ",a[text].value); text++; } else printf(" 0 "); } printf("\n"); } } void showjuzhen_2(struct juzhen a[MAX_TERM],int count_a) //顯示稀疏矩陣方法二 { int i; printf(" i row col val\n"); for(i = 0;i < count_a + 1;i++) { printf(" %d| %d %d %d\n",i,a[i].row,a[i].col,a[i].value); } } void zhuanzhi_1(struct juzhen a[MAX_TERM],struct juzhen b[MAX_TERM]) //轉置矩陣方法一 { int i,j; int count_b = 1; //b的當前元素下標 b[0].row = a[0].col; b[0].col = a[0].row; b[0].value = a[0].value; for(i = 0;i < a[0].col;i++) { for(j = 1;j <= a[0].value;j++) { if(a[j].col == i) { b[count_b].row = a[j].col; b[count_b].col = a[j].row; b[count_b].value = a[j].value; count_b++; } } } } void zhuanhuan_2(struct juzhen a[MAX_TERM],struct juzhen b[MAX_TERM]) { int term[N],star[N]; int n = a[0].value; int i,j,k; int b_star; for(i = 0;i < N;i++) term[i] = 0; for(j = 0;j <= a[0].value;j++) term[a[j].col]++; star[0] = 1; for(k = 1;k < N;k++) star[k] = star[k - 1] + term[k - 1]; b[0].col = a[0].col; b[0].row = a[0].row; b[0].value = a[0].value; for(i = 1;i <= n;i++) { b_star = star[a[i].col]++; b[b_star].col = a[i].row; b[b_star].row = a[i].col; b[b_star].value = a[i].value; } for(i = 0;i < a[0].value + 1;i++) printf(" %d| %d %d %d\n",i,b[i].row,b[i].col,b[i].value); } int main(void) { int count_a; count_a = chushi(a); showjuzhen(a,count_a); showjuzhen_2(a,count_a); printf("\n"); zhuanhuan_2(a,b); //zhuanzhi_1(a,b); //showjuzhen(b,count_a); //showjuzhen_2(b,count_a); //return 0; } |