數組的定義1:一個 N 維數組是受 N 組線性關係約束的線性表。
二維數組的邏輯結構可形式地描述爲:
2_ARRAY(D,R)
其中 D={aij} | i=0,1,...,b1-1; j=0,1,...,b2-1;aij∈D0}
R={Row,Col}
Row={<aij,ai,j+1>|0<=i<=b1-1;0<=j<=b2-2;aij,ai,j+1∈D0}
ai,j+1是aij在行關係中的後繼元素。
Col={<aij,ai+1,j>|0<=i<=b1-2;0<=j<=b2-1;aij,ai+1,j∈D0}
ai+1,j是aij在列關係中的後繼元素。
①每個數組元素a[i][j]都受兩個關係Row和Col的約束:
ROW(行關係):ai,j+1 是aij在行關係中的直接後繼。
COL(列關係):ai+1,j是aij在列關係中的後繼元素。
②每一個數組元素屬於同一數據類型。
③每一個數組元素由下標(i,j)惟一肯定其位置。
④每一個下標i由bi限定其範圍,0≤i≤bi-1
n維數組的邏輯結構可描述爲:
n_ARRAY(D,R)
D---數組的元素
R---定義爲數組元素間的關係
R=(R1,R2,...,Rn)
數組的定義2 :一維數組是定長線性表; 二維數組是一個定長線性表,它的每一個元素是一個一維數組;n維數組是線性表,它的每一個元素是n-1維數組。
數組是線性結構,基於兩點:
一、一個 n維數組被定義爲一個線性表,它的元素是一個 n-1維數組。
二、一個 n維數組的數據元素受n個關係的約束,且每一個關係都是線性的。ios
其中: cn =L, ci-1= bi × ci, 1<i ≤ n ; ci 爲常數
上式稱爲n維數組的存儲映象函數算法
數組的基本操做:
一、數組初始化:肯定數組維數、長度,分配存儲空間。
initarray(&A,n,bound[ ]);
bound[ ]= b1,b2......bn
二、撤消數組
destroyarray (&A);
三、求數組元素值
value(A,&e,index[ ]);
index[ ]= i1,i2,......in
四、爲數組元素賦值
assign(&A,e,index[ ]);
數組的順序表示及實現:
用一遍地址連續的存儲單元依次存放數據元素。
一、數據類型描述
#define MAX_ARRAY_DIM 8
typedef struct {
ElemType *base; //數組元素空間
int dim; //數組維數
int *bounds; //數組維長
int *constant; //常數因子
}ARRAY;
矩陣的壓縮存儲:
一、矩陣壓縮存儲的概念
特殊矩陣:值相同元素或0元素在矩陣中分佈有必定規律。
⒈對稱矩陣:矩陣中的元素知足
aij=aji 1≤i,j≤n
⒉三角矩陣:上(下)三角矩陣指矩陣的下(上)三角(不包括對角線)中的元素均爲常數c或0的n階矩陣。
⒊對角矩陣(帶狀矩陣):矩陣中全部非0元素集中在以主對角線爲中心的區域中。
稀疏矩陣:非0元素不多( ≤ 5%)且分佈無規律。
二、矩陣的壓縮存儲
爲多個相同值的元分配一個存儲單元;對零元不分配空間。
對稱矩陣的壓縮存儲
存儲分配策略: 每一對對稱元只分配一個存儲單元,即只存儲下三角(包括對角線)的元, 所需空間數爲:
n×(n+1)/2。
存儲分配方法: 用一維數組sa[n(n+1)/2]做爲存儲結構。
sa[k]與aij之間的對應關係爲:
稀疏矩陣存儲分配策略
只存儲稀疏矩陣的非0元素。
矩陣中的一個元素能夠用行列下標和其值來惟一表示,所以能夠用一個三元組(i,j,aij) 惟一肯定一個非0元素。
邏輯上,用三元組表來表示稀疏矩陣的非0元
廣義表的定義
廣義表又稱爲列表(lists),是n≥0個元素a1,a2,...,an的有限序列,記爲:
A=( a1,a2,...,an)
其中:
A是廣義表的表名,n是廣義表的長度
ai 是單個元素或廣義表,
若ai是單個元素,則稱爲廣義表的單元素(或原子)。
如果廣義表,則稱ai是廣義表的子表。因此廣義表又稱爲列表。
即 ai ∈D0 或 ai ∈lists
廣義表的表頭(Head):非空表A 的第一個元素 a1。
廣義表的頭與a1具備相同的表示形式。
廣義表的表尾(Tail):除其頭以外的其他元素( a2,...,an)組成的表。
廣義表的尾必定是一個廣義表。
特色:廣義表的定義是一個遞歸的定義。編程
2.1.實驗內容
編程實現稀疏矩陣的十字鏈表實現
1.用txt文件錄入稀疏矩陣數組,格式以下:
m n t //表示行號,列號和總數
i j value
..................
2.讀文件創建十字鏈表
3.輸出創建後的鏈表,格式爲;
行號1 列號11 值** 列號12 值*** 。。。。。
行號2 列號21 值** 列號22 值*** 。。。。。
。。。。。。。。。
4.實現矩陣的轉置
5.輸出轉置後的矩陣,格式爲矩陣形式。
2.2.輸入和輸出
輸入:本程序採起文件讀寫形式,文件中數值格式詳見實驗內容
輸出:本程序有兩種輸出形式分別爲按行輸出和按矩陣形式輸出數組
2.3.關鍵數據結構與算法描述
數據結構:創建十字鏈表須要知道行列號i,j和鏈表的right,down指針,以及節點的數值,因而數據結構呼之欲出,又因過程當中讀文件時須要先創建一個緩衝器存儲節點的信息, 則兩個結構具體以下:緩存
/***********如下構建數據結構************/ typedef struct OLink{ int i,j; ElemType value; struct OLink *right,*down; }*LinkList,OLink; /************構建存儲結構*************/ typedef struct record{ int i; int j; int value; }RECORD; /**************構建完畢**************/
算法描述:
創建十字鏈表,數據結構
1.首先要知道鏈表的頭節點,因每行,每列都須要一個循環鏈表,則共須要m+n+1個頭指針,m個行指針,n個列指針,1個總頭指針。框架
2.創建完兩個指針鏈表以後(注意:此處鏈表的每個元素都是附加頭節點),就要對矩陣的元素進行插入,當插入元素時要注意和對應行,對應列都創建聯繫,構成網狀結構,當插入完元素以後,十字鏈表也就創建完畢了。ide
3.餘下的就是輸出元素了,根據頭節點共有兩種輸出方法,一種按行輸出,一種按列輸出。固然也能夠把非零元素放到二維數組中,經過二維數組進行輸出。函數
4.最後,就是矩陣的轉置,只需將a[i][j]與a[j][i]交換便可,再從新創建十字鏈表,修改初始化和i,j指向便可。其中最核心的算法就是頭節點的構建和元素的插入了,具體代碼以下:測試
/*************建立行,列頭節點*****************/
void CreateHead(LinkList *head, int m, int n) { //如下創建列頭結點
LinkList p = *head,q; //構建列頭節點
for(int i=0; i<n; i++) { q = (LinkList)malloc(sizeof(OLink)); q->i = -1; //注意在矩陣的外面,也能夠不賦值
q->j = i; //表明矩陣的列號,從0開始
q->down = q; //構建循環鏈表的標誌
p->right = q; //連接
p = q; //繼續向前推動
} p->right = (*head);//循環鏈表的標誌 //如下創建行頭結點,基本原理同上
p = (*head); for(i=0; i<m; i++) { q = (LinkList)malloc(sizeof(OLink)); q->i = i; q->j = -1; q->right = q; p->down = q; p = q; } p->down = (*head); } /************各個頭節點構建完畢,共m+n+1個************/
/***************插入節點的算法************************/
bool InsertNode(LinkList *head, int i, int j, ElemType e) { LinkList p = *head,q; if(i < 0||j < 0||i >= (*head)->i||j >= (*head)->j) return false; /********構建節點********/ q = (LinkList)malloc(sizeof(OLink)); q->i = i; q->j = j; q->value = e; /*******完畢***********/
for(int k=0; k<=i; k++) { p = p->down;//注意此處等於i截至
} //產生定位指針
LinkList sr = p,s = p->right; /******若不知足s==p,或者插入元素大於後面元素,繼續推動********/
while(s!=p && q->j>s->j) { sr = s; s=s -> right; } /*******推動完畢,有可能有三種狀況*************/ q->right = s; sr->right = q; /**********行連接處理完畢*********************/
/******如下連接列鏈表,方法同上**********************/ p = *head; for( k=0; k<=j; k++) { p = p->right; } sr = p; s = p->down; while(s!=p && q->j>s->j) { sr = s; s = s->down; } q->down = s; sr->down = q; /**********列連接處理完畢*********************/
return true; }
2.4.測試與理論
1.在文件操做中輸入以下文本:
2.程序運行後應產生9*5的矩陣,具體輸出形式應與要求一致,如圖;
3.轉置後變成5*9的矩陣,具體顯示以下
2.五、全部程序
1 #include "stdlib.h"
2 #include "conio.h"
3 #include "iostream"
4 using namespace std; 5
6 typedef int ElemType;//設置元素類型
7
8 /***********如下構建數據結構************/
9 typedef struct OLink{ 10 int i,j; 11 ElemType value; 12 struct OLink *right,*down; 13 }*LinkList,OLink; 14 /************構建存儲結構*************/
15 typedef struct record{ 16 int i; 17 int j; 18 int value; 19 }RECORD; 20 /**************構建完畢**************/
21
22 /**************進行初始化操做********************/
23 void InitArray(LinkList *head, int m, int n) 24 { 25 *head = (LinkList)malloc(sizeof(OLink)); 26 (*head)->i=m; //行長度
27 (*head)->j=n; //列長度
28 } 29 /***************初始化完畢*********************/
30
31 /*************建立行,列頭節點*****************/
32 void CreateHead(LinkList *head, int m, int n) 33 { 34 //如下創建列頭結點
35 LinkList p = *head,q; 36 //構建列頭節點
37 for(int i=0; i<n; i++) 38 { 39 q = (LinkList)malloc(sizeof(OLink)); 40 q->i = -1; //注意在矩陣的外面,也能夠不賦值
41 q->j = i; //表明矩陣的列號,從0開始
42
43 q->down = q; //構建循環鏈表的標誌
44 p->right = q; //連接
45 p = q; //繼續向前推動
46 } 47 p->right = (*head);//循環鏈表的標誌 48
49 //如下創建行頭結點,基本原理同上
50 p = (*head); 51 for(i=0; i<m; i++) 52 { 53 q = (LinkList)malloc(sizeof(OLink)); 54 q->i = i; 55 q->j = -1; 56
57 q->right = q; 58 p->down = q; 59 p = q; 60 } 61 p->down = (*head); 62 } 63 /************各個頭指針構建完畢,共m+n+1個************/
64
65 /***************插入節點的算法************************/
66 bool InsertNode(LinkList *head, int i, int j, ElemType e) 67 { 68 LinkList p = *head,q; 69 if(i < 0||j < 0||i >= (*head)->i||j >= (*head)->j) 70 return false; 71
72 /********構建節點********/
73 q = (LinkList)malloc(sizeof(OLink)); 74 q->i = i; 75 q->j = j; 76 q->value = e; 77 /*******完畢***********/
78
79
80 for(int k=0; k<=i; k++) 81 { 82 p = p->down;//注意此處等於i截至
83 } 84 //產生定位指針
85 LinkList sr = p,s = p->right; 86 /******若不知足s==p,或者插入元素大於後面元素,繼續推動********/
87 while(s!=p && q->j>s->j) 88 { 89 sr = s; 90 s=s -> right; 91 } 92 /*******推動完畢,有可能有三種狀況*************/
93 q->right = s; 94 sr->right = q; 95 /**********行連接處理完畢*********************/
96
97
98 /******如下連接列鏈表,方法同上**********************/
99 p = *head; 100 for( k=0; k<=j; k++) 101 { 102 p = p->right; 103 } 104 sr = p; 105 s = p->down; 106 while(s!=p && q->j>s->j) 107 { 108 sr = s; 109 s = s->down; 110 } 111 q->down = s; 112 sr->down = q; 113 /**********列連接處理完畢*********************/
114
115 return true; 116 } 117
118 /**********關於矩陣轉置的算法,即a[i][j]<->a[j][i]******************/
119 LinkList MatrixTransposition(LinkList head, int m, int n) 120 { 121 LinkList THead = head,p,q,h; 122 int temp; 123 //從新構造十字矩陣,列換行,行換列
124 InitArray(&h,n,m); 125 CreateHead(&h,n,m); 126 for(p = THead->down; p!=THead; p = p->down) 127 { 128
129 for(q = p->right; q!=p; q = q->right) 130 { 131 //j變i,i變j,值不變
132 if(!InsertNode(&h, q->j, q->i, q->value)) 133 return NULL; 134 } 135
136 } 137 return h; 138
139 } 140 /*******銷燬鏈表操做******************/
141 void DestroyMatrix(LinkList *head) 142 { 143 LinkList p,q,r; 144 for(p = (*head)->down; p!=*head; p = p->down) 145 { 146
147 for(q = p->right; q!=p;) 148 { 149 r = q->right; 150 free(q); 151 q=r; 152 } 153
154 } 155 } 156 /*************打印矩陣算法*********************/
157 void PrintMatrix(LinkList head,int m,int n) 158 { 159 LinkList p,q; 160
161 cout<<"此矩陣爲"<<m<<"*"<<n<<"型"<<endl; 162 //向下推動,按行輸出
163 for(p = head->down; p!=head; p = p->down) 164 { 165 cout<<"第"<<p->i<<"行 "; 166 for(q = p->right; q!=p; q = q->right) 167 { 168
169 cout<<" "<<"["<<q->i<<","<<q->j<<"] "<<q->value<<" "; 170 } 171 cout<<endl; 172 } 173 } 174 //以矩陣形式輸出
175 void MatrixPrint(LinkList head,int m,int n) 176 { 177 int a[100][100]; 178 LinkList p,q; 179 memset(a,0,sizeof(a));//置零
180 for(p = head->down; p!=head; p = p->down) 181 { 182 for(q = p->right; q!=p; q = q->right) 183 { 184 a[q->i][q->j]=q->value;//賦值
185 } 186 } 187 for(int i=0;i<m;i++) 188 { 189 for(int j=0;j<n;j++) 190 { 191 cout<<a[i][j]<<" "; 192 } 193 cout<<endl; 194 } 195 } 196
197 void MainMenu() 198 { 199 LinkList head,h; 200 int m,n,total;//total爲矩陣中非零數值個數
201 RECORD RecordMatrix[1000]; 202 FILE *fp; 203
204 if((fp = fopen("F:Matrix.txt","r"))==NULL) 205 { 206 cout<<"cannot open the file"<<endl; 207 exit(-1); 208 } 209 //找到矩陣的基本框架
210 fscanf(fp,"%d%d%d",&m,&n,&total); 211 //緩存器record
212 for(int i=0;i<total;i++) 213 { 214 fscanf(fp,"%d%d%d",&RecordMatrix[i].i,&RecordMatrix[i].j,&RecordMatrix[i].value); 215 } 216 //構建十字鏈表
217 InitArray(&head,m,n); 218 CreateHead(&head,m,n); 219 for(i=0;i<total;i++) 220 { 221 if(!InsertNode(&head,RecordMatrix[i].i,RecordMatrix[i].j,RecordMatrix[i].value)) 222 return ; 223 } 224 cout<<"原矩陣元素爲"<<endl; 225 PrintMatrix(head,m,n);//打印鏈表矩陣
226 MatrixPrint(head,m,n);//以矩陣形式打印 227
228 //測試轉制後的矩陣
229 h = MatrixTransposition(head,m,n); 230 cout<<endl<<"轉置後矩陣元素爲"<<endl; 231 PrintMatrix(h,n,m); 232 MatrixPrint(h,n,m);//以矩陣形式打印
233
234 DestroyMatrix(&head);//銷燬鏈表
235 DestroyMatrix(&h); //銷燬鏈表
236 fclose(fp); //關閉文件
237 } 238
239 int main() 240 { 241 MainMenu();//引入主函數
242 getchar(); 243 return 0; 244 }