代码改变世界

C语言 稀疏矩阵 压缩 实现

2013-11-04 23:35  wid  阅读(9615)  评论(1编辑  收藏  举报

稀疏矩阵压缩存储的C语言实现 (GCC编译)。

 

  1 /**
  2 * @brief C语言 稀疏矩阵 压缩 实现
  3 * @author wid
  4 * @date 2013-11-04
  5 *
  6 * @note 若代码存在 bug 或程序缺陷, 请留言反馈, 谢谢!
  7 */
  8 
  9 #include <stdio.h>
 10 #include <stdlib.h>
 11 #include <assert.h>
 12 #include <string.h>
 13 
 14 #define TRUE 1
 15 #define FALSE 0
 16 #define NPOS -1
 17 
 18 typedef int ElemType;
 19 
 20 typedef struct
 21 {
 22     int m;              ///行下标
 23     int n;              ///列下标
 24     ElemType elm;       ///该下标所保存的元素
 25 }TTuple;    ///三元组结构
 26 
 27 typedef struct
 28 {
 29     TTuple *tup;        ///三元组顺序表
 30     int row;            ///矩阵行数
 31     int col;            ///矩阵列数
 32     int unul;           ///非 0 元素个数
 33 }TMatrix;   ///稀疏矩阵结构
 34 
 35 
 36 ///稀疏矩阵方法声明
 37 TMatrix *CreateEmptyTMatrix( int sizeM, int sizeN );              ///创建一个大小为 sizeM x sizeN 稀疏矩阵
 38 TMatrix *CreateTMatirxFrom2DArray( void *pArr2D, int sizeM, int sizeN );    ///从二维数组中创建稀疏矩阵
 39 void DestroyTMatrix( TMatrix *pMat );       ///销毁稀疏矩阵
 40 int ElemLocate( const TMatrix *const pMat, int m, int n );                  ///定位矩阵下标 m, n 在稀疏矩阵中的存储位置
 41 void DisplayTMatrix( const TMatrix *const pMat );       ///输出稀疏矩阵
 42 int GetTMatrixSize( const TMatrix *const pMat );        ///输出稀疏矩阵 pMat 所占用的空间大小(字节)
 43 int AppendElem( TMatrix *const pMat, ElemType elm, int m, int n );          ///将元素 elm 添加到稀疏矩阵 m, n 位置
 44 int DeleteElem( TMatrix *const pMat, int m, int n );    ///删除稀疏矩阵中 m, n 下标指向的元素
 45 int TMatrixCopy( TMatrix *const pMatDest, TMatrix *const pMatSrc );         ///将稀疏矩阵 pMatSrc 复制到 pMatDest
 46 int Value( const TMatrix *const pMat, int m, int n, ElemType *pElm );       ///从稀疏矩阵中取下标为 m, n 元素的值
 47 void ForEach( const TMatrix *const pMat, void (*func)(ElemType *pElm) );    ///对矩阵中的每个元素依次执行 func 函数
 48 
 49 ///稀疏矩阵方法实现
 50 
 51 /**
 52 * @brief 创建一个大小为 sizeM x sizeN 稀疏矩阵
 53 *
 54 * @return 返回创建的稀疏矩阵的指针
 55 */
 56 TMatrix *CreateEmptyTMatrix( int sizeM, int sizeN )
 57 {
 58     ///不接受大小为0的稀疏矩阵
 59     assert( sizeM > 0 && sizeN > 0 );
 60 
 61     TMatrix *pMat = (TMatrix *)malloc( sizeof(TMatrix) );
 62     pMat->tup = NULL;
 63     pMat->row = sizeM;
 64     pMat->col = sizeN;
 65     pMat->unul = 0;
 66 
 67     return pMat;
 68 }
 69 
 70 /**
 71 * @brief 从二维数组中创建稀疏矩阵
 72 *
 73 * @param pArr2D 一个ElemType型二维数组
 74 * @param sizeM 二维数组的行数
 75 * @param sizeN 二维数组的列数
 76 *
 77 * @return 返回创建的稀疏矩阵的指针
 78 */
 79 TMatrix *CreateTMatirxFrom2DArray( void *pArr2D, int sizeM, int sizeN )
 80 {
 81     ///不接受大小为0的稀疏矩阵
 82     assert( sizeM > 0 && sizeN > 0 );
 83 
 84     TMatrix *pMat = (TMatrix *)malloc( sizeof(TMatrix) );
 85 
 86     ///初始化稀疏矩阵行数、列数
 87     pMat->row = sizeM;
 88     pMat->col = sizeN;
 89 
 90     ///第一趟遍历, 统计非零元素个数
 91     int m = 0, n = 0;
 92     for( m = 0; m < sizeM; ++m )
 93         for( n = 0; n < sizeN; ++n )
 94             if( ((ElemType *)pArr2D)[sizeM * m + n] != 0 )
 95                 ++pMat->unul;
 96 
 97     ///申请合适长度的三元组类型的线性表
 98     pMat->tup = (TTuple *)calloc( pMat->unul, sizeof(TTuple) );
 99 
100     ///第二趟遍历, 存储二维矩阵中的非零元素
101     int nPos = 0;
102     for( m = 0; m < sizeM; ++m )
103         for( n = 0; n < sizeN; ++n )
104             if( ((ElemType *)pArr2D)[sizeM * m + n] != 0 )
105             {
106                 pMat->tup[nPos].m = m;
107                 pMat->tup[nPos].n = n;
108                 pMat->tup[nPos].elm = ((ElemType *)pArr2D)[sizeM * m + n];
109                 ++nPos;
110             }
111 
112     return pMat;
113 }
114 
115 /**
116 * @brief 销毁稀疏矩阵
117 *
118 * @param pMat 指向待销毁的稀疏矩阵
119 */
120 void DestroyTMatrix( TMatrix *pMat )
121 {
122     free( pMat->tup );
123     free( pMat );
124 
125     pMat = NULL;
126 }
127 
128 /**
129 * @brief 定位元素下标 m, n 在稀疏矩阵中出现的位置
130 *
131 * @param pMat 指向待定位元素的稀疏矩阵
132 * @param m 元素行下标
133 * @param n 元素列下标
134 *
135 * @return 若存在, 返回该下标组在稀疏矩阵中出现的位置, 否则返回 NPOS
136 *
137 * @note 元素位置由 0 计起
138 */
139 int ElemLocate( const TMatrix *const pMat, int m, int n )
140 {
141     int i = 0;
142     for( i = 0; i < pMat->unul; ++i )
143     {
144         if( pMat->tup[i].m == m && pMat->tup[i].n == n )
145             return i;
146     }
147 
148     return NPOS;
149 }
150 
151 /**‘
152 * @brief 输出稀疏矩阵
153 *
154 * @param pMat 指向待输出的稀疏矩阵
155 *
156 * @return void
157 */
158 void DisplayTMatrix( const TMatrix *const pMat )
159 {
160     int m = 0, n = 0, pos = 0;
161     for( m = 0; m < pMat->row; ++m )
162     {
163         for( n = 0; n < pMat->col; ++n )
164         {
165             pos = ElemLocate( pMat, m, n );
166             if( pos != NPOS )
167                 printf( "%d ",  pMat->tup[pos].elm );
168             else
169                 printf( "%d ", 0 );
170         }
171         putchar( '\n' );
172     }
173 }
174 
175 /**
176 * @brief 获取稀疏矩阵所占用的空间大小(字节)
177 *
178 * @param pMat 指向待获取占用空间的稀疏矩阵
179 *
180 * @return 返回该矩阵所占用的空间的大小
181 */
182 int GetTMatrixSize( const TMatrix *const pMat )
183 {
184     return pMat->unul * sizeof(TTuple);
185 }
186 
187 /**
188 * @brief 将元素添加到稀疏矩阵的 m, n 位置
189 *
190 * @param pMat 指向待添加元素的稀疏矩阵
191 * @param elm 待添加的元素
192 * @param m 元素所在的行数
193 * @param n 元素所在的列数
194 *
195 * @return 返回添加后稀疏矩阵中非 0 元素的个数
196 */
197 int AppendElem( TMatrix *const pMat, ElemType elm, int m, int n )
198 {
199     ///断言下标合法
200     assert( m >= 0 && m < pMat->row && n >= 0 && n < pMat->col );
201 
202     ///断言元素值合法(不接受元素值为0的元素)
203     assert( elm != 0 );
204 
205     ///测试下标是否存在
206     int i = 0, pos = 0;
207     pos = ElemLocate( pMat, m, n );
208     if( pos != NPOS )
209     {   ///下标已存在, 覆盖原值
210         pMat->tup[pos].elm = elm;
211         return pMat->unul;
212     }
213 
214     ///新添加
215     pMat->tup = (TTuple *)realloc( pMat->tup, sizeof(TTuple) * (pMat->unul + 1) );
216     pMat->tup[pMat->unul].m = m;
217     pMat->tup[pMat->unul].n = n;
218     pMat->tup[pMat->unul].elm = elm;
219 
220     return ++pMat->unul;
221 }
222 
223 /**
224 * @brief 删除稀疏矩阵中下标 m, n 指向的元素
225 *
226 * @param pMat 指向待删除元素的稀疏矩阵
227 * @param m 元素行下标
228 * @param n 元素列下标
229 *
230 * @param 若元素存在, 则返回删除后稀疏矩阵中非 0 元素个数, 否则返回NPOS
231 */
232 int DeleteElem( TMatrix *const pMat, int m, int n )
233 {
234     ///使用断言确保下标合法
235     assert( m >= 0 && m < pMat->row && n >= 0 && n < pMat->col );
236 
237     int pos = ElemLocate( pMat, m, n );
238 
239     ///该元素是否存在
240     if( pos == NPOS )
241         return NPOS;
242 
243     ///删除该位置上的元素以及记录
244     for( pos; pos < pMat->unul - 1; ++pos )
245     {
246         pMat->tup[pos].m = pMat->tup[pos+1].m;
247         pMat->tup[pos].n = pMat->tup[pos+1].n;
248         pMat->tup[pos].elm = pMat->tup[pos+1].elm;
249     }
250 
251     ///缩小内容占用
252     pMat->tup = (TTuple *)realloc( pMat->tup, sizeof(TTuple) * (pMat->unul - 1) );
253 
254     return --pMat->unul;
255 }
256 
257 /**
258 * @brief 将源稀疏矩阵复制到目标稀疏矩阵中
259 *
260 * @param pMatDest 指向目标稀疏矩阵
261 * @param pMatSrc 指向源稀疏矩阵
262 *
263 * @return 返回复制成功后目标稀疏矩阵中的非0元素数量
264 */
265 int TMatrixCopy( TMatrix *const pMatDest, TMatrix *const pMatSrc )
266 {
267     if( pMatDest->tup )
268         free( pMatDest->tup );
269 
270     ///源稀疏矩是否为空
271     if( pMatSrc->tup )
272     {   //不为空, 复制矩阵
273         pMatDest->tup = (TTuple *)calloc( pMatSrc->unul, sizeof(TTuple) * pMatSrc->unul );
274         assert( pMatDest->tup );
275         memcpy( pMatDest->tup, pMatSrc->tup, sizeof(TTuple) * pMatSrc->unul );
276     }
277     else pMatDest->tup = NULL;
278 
279     pMatDest->row = pMatSrc->row;
280     pMatDest->col = pMatSrc->col;
281     pMatDest->unul = pMatSrc->unul;
282 
283     return pMatDest->unul;
284 }
285 
286 /**
287 * @brief 从稀疏矩阵中获取下标为 m, n 元素的值
288 *
289 * @param pMat 指向待获取元素的稀疏矩阵
290 * @param m 元素所在位置的行下标
291 * @param n 元素所在位置的列下标
292 * @param pElm 接收数据元素的指针
293 *
294 * @return 返回该元素在稀疏矩阵中的位置
295 *
296 * @note 位置由 0 计起
297 */
298 int Value( const TMatrix *const pMat, int m, int n, ElemType *pElm )
299 {
300     ///使用断言确保下标合法
301     assert( m >= 0 && m < pMat->row && n >= 0 && n < pMat->col );
302 
303     int pos = ElemLocate( pMat, m, n );
304     if( pos != NPOS )
305     {
306         *pElm = pMat->tup[pos].elm;
307         return pos;
308     }
309     else
310     {
311         *pElm = 0;
312         return NPOS;
313     }
314 }
315 
316 /**
317 * @brief 对稀疏矩阵中的每个元素依次执行 func 函数
318 *
319 * @param pMat 指向待处理的稀疏矩阵
320 * @param func 回调函数
321 *
322 * @return void
323 */
324 void ForEach( const TMatrix *const pMat, void (*func)(ElemType *pElm) )
325 {
326     int m = 0, n = 0, pos = 0, t = 0;
327 
328     for( m = 0; m < pMat->row; ++m )
329         for( n = 0; n < pMat->col; ++n )
330         {
331             pos = ElemLocate( pMat, m, n );
332 
333             if( pos != NPOS )
334                 func( &pMat->tup[pos].elm );
335             else
336                 func( &t );
337         }
338 }
339 
340 ///测试
341 
342 /**
343 * @brief ForEach的回调函数, 若元素为 0 则输出'x', 否则正常输出
344 */
345 void display( ElemType *pElm )
346 {
347     if( *pElm == 0 )
348         putchar( 'x' );
349     else
350         printf( "%d", *pElm );
351 }
352 
353 int main()
354 {
355     ///稀疏因子为 0.098 的二维数组
356     ElemType arrMat[15][15] = {
357         {0, 9, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},
358         {0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
359         {0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0},
360         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
361         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0},
362         {0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0},
363         {0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
364         {0, 0, 8, 0, 0, 0, 0, 0, 5, 0, 0, 0, 8, 0, 0},
365         {0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
366         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
367         {0, 7, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0},
368         {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 1, 0, 0},
369         {0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
370         {0, 0, 0, 0, 8, 0, 0, 0, 0, 7, 0, 0, 0, 1, 0},
371         {0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0}
372     };
373 
374 
375     ///测试 CreateTMatirxFrom2DArray
376     ///从二维数组 arrMat 中创建稀疏矩阵
377     TMatrix *pMat = CreateTMatirxFrom2DArray( arrMat, 15, 15 );
378     printf( "稀疏矩阵占用空间大小: %d (byte)\n", GetTMatrixSize(pMat) );
379 
380     ///测试 CreateEmptyTMatrix
381     ///创建一个 5 x 5 大小的稀疏矩阵
382     TMatrix *pMat2 = CreateEmptyTMatrix( 5, 5 );
383 
384     ///测试 TMatrixCopy
385     ///将 pMat 复制到 pMat2
386     TMatrixCopy( pMat2, pMat );
387 
388     ///测试 DisplayTMatrix
389     printf( "输出稀疏矩阵 pMat2:\n" );
390     DisplayTMatrix( pMat2 );
391 
392     ///测试 AppendElem
393     printf( "将 0, 0 处元素置为 1.\n" );
394     AppendElem( pMat2, 1, 0, 0 );
395 
396     ///测试 DeleteElem
397     printf( "删除 0, 1 处的元素.\n" );
398     DeleteElem( pMat2, 0, 1 );
399 
400     ///输出 pMat2
401     printf( "输出稀疏矩阵 pMat2:\n" );
402     DisplayTMatrix( pMat2 );
403 
404     ///测试 Value
405     int a = -1;
406     Value( pMat2, 10, 8, &a );
407     printf( "位置 10, 8 处的元素为: %d\n", a );
408 
409     ///测试 ForEach
410     printf( "将稀疏矩阵中值为0的元素用x代替并全部输出:\n" );
411     ForEach(pMat2, display );
412 
413     ///销毁稀疏矩阵
414     DestroyTMatrix( pMat );
415     DestroyTMatrix( pMat2 );
416 
417     return 0;
418 }

 

 

运行测试:

 

若代码存在 bug 或程序缺陷, 请留言反馈, 谢谢。