C语言 稀疏矩阵 压缩 实现
2013-11-04 23:35 wid 阅读(9635) 评论(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 或程序缺陷, 请留言反馈, 谢谢。