天天看点

数据结构 压缩矩阵笔记

/*
数组的压缩储存:
    在一些高阶矩阵中,非零元素非常少,此时如果使用二维数组将造成
    储存空间的浪费,这时可只储存部分元素,从而提高储存空间的利用
    率,通常的做法是为多个相同值的元素只分配一个储存单元,对值为
    零的元素不分配储存单元。我们把非零元素个数远小于二维数组总元
    素个数,或元素分布呈一定规律的(对称矩阵,三角矩阵,对角矩阵)
    特殊矩阵压缩储存。
    
    对称矩阵:
        矩阵元素的值与下标的关系: a(i,j) = a(j,i)
        我们只需为每一对称元素分配一个储存单元,这样可以将 n*n 个
        元素储存在一个 n(n+1)/2 的储存空间里。
        假设用以为数组储存上三角或下三角元素。则一维数组的下标 k
        与 n 阶对称阵的元素 a(i,j) 的下标对应关系为:
            如果 i >= j 时,以下三角储存, k = i*(i+1)/2 + j
            如果 i < j  时,以上三角储存, k = j*(j+1)/2 + i
            
    三角矩阵:
        分为上三角矩阵和下三角矩阵,其中在上三角矩阵中,下三角元素
        全部为一个常数c,下三角矩阵中,上三角元素全部为一个常数c。
        以上三角矩阵为例,上三角矩阵的压缩规则是只储存上三角元素,
        不储存下三角元素,或只用一个储存单元储存下三角非零元素
        用一维数组储存上三角矩阵,采取使用一个储存单元储存下三角元
        素的方法,一维数组的长度为 n*(n+1)/2 + 1 一维数组的下标 k与
         n 阶上三角矩阵的元素 a(i,j) 的下标对应关系为:
            如果 i <= j, k = i*(2n-i+1)/2 + j -i
            如果 i > j , k = n*(n+1)/2 
        下三角矩阵使用一维数组储存后相应的对应关系为:
            如果 i >= j, k = i*(i+1)/2 + j
            如果 i <  j, k = n*(n+1)/2 
        
        其中第 k = n*(n+1)/2 的位置存放的是常数 c 
        
        以上三角矩阵为例:
        # include <stdio.h>
        # define N 5
        
        int main(void)
        {
            int arry[N][N];
            int arr[N*(N+1)/2 + 1] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
            int i, j, k;
            for (i = 0; i < N; ++i)
            {
                for (j = 0; j < N; ++j)
                {
                    if (i <= j)
                    {
                        k = i*(2*N-i+1)/2 + j -i;
                        arry[i][j] = arr[k];
                    }
                    else
                    {
                        k = N*(N+1)/2;
                        arry[i][j] = arr[k];
                    }
                }
            }
            for (i = 0; i < N; ++i)
            {
                for (j = 0; j < N; ++j)
                    printf("%-5d", arry[i][j]);
                
                printf("\n");
            }
            
            return 0;
        }
        
        输出结果:
        1    2    3    4    5
        0    6    7    8    9
        0    0    10   11   12
        0    0    0    13   14
        0    0    0    0    15 
         
        
    对角矩阵:(数学上的问题 <-0-0->) 
        也称为带状矩阵,就是所有的非零元素都集中在以对角线为中心的带状 
        区域内(对角线的个数位奇数),即除了主对角线和主对角线上下若干
        条对角线的元素外,其它元素均为常数 c . 
*/ 

/*
假设已有一个 n*n 的 上三角矩阵 A ,且上三角矩阵 A 的元素已按行为主序连续压缩 
存放在数组 B 中,设计一个算法,将 B 中的元素案列为主序连续压缩存放在数组 C中
         1 2  3  4  5 
         0 6  7  8  9
A(5*5) = 0 0 10 11 12 
         0 0  0 13 14
         0 0  0  0 15   
其中 B = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0},
     C = { 1, 2, 6, 3, 7, 10, 4, 8, 11, 13, 5, 9, 12, 14, 15, 0}.
     
思路:将一维数组B还原成二维数组A,再将二维数组储存在C中
*/ 

# include <stdio.h>
# define N 5

void Trans_To_Original(int arry[N][N], int * arr);
void Trans_To_Compress(int arry[N][N], int * arr);
void Show(int arry[N][N], int * arr1, int * arr2); 

int main(void)
{
    // 创建一个一维数组储存已知的以行为主序压缩后所得元素 
    int B[(N+1)*N/2 + 1] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    int A[N][N] = {0};
    Trans_To_Original(A, B);
    
    // 创建一个一维数组储存以列为主序压缩所得的数据
    int C[(N+1)*N/2 + 1];
    Trans_To_Compress(A, C);
    
    // 将三个数组打印输出 
    Show(A, B, C);
    
    return 0;
}

void Trans_To_Original(int arry[N][N], int * arr)
{
    int i, j, k;
    for (i = 0; i < N; ++i)
    {
        for (j = 0; j < N; ++j)
        {
            if (i <= j)
            {
                k = i*(2*N-i+1)/2 + j -i;
                arry[i][j] = arr[k];
            }
            else
            {
                k = N*(N+1)/2;
                arry[i][j] = arr[k];
            }
        }
    }
    
    return;
}

void Trans_To_Compress(int arry[N][N], int * arr)
{
    int i, j, k = 0;
    // 按列为主序压缩 
    for (i = 0; i < N; ++i)
    {
        for (j = 0; j <= i; ++j)
        {
            arr[k++] = arry[j][i];
        }
    }
    arr[(N+1)*N/2] = 0;
    
    return;
}

void Show(int arry[N][N], int * arr1, int * arr2)
{
    int i, j, k;
    printf("原二维数组为:\n");
    for (i = 0; i < N; ++i)
    {
        printf("              ");
        for (j = 0; j < N; ++j)
        {
            printf("%-5d", arry[i][j]);
        }
        printf("\n");
    }
    printf("以行为主序压缩所得结果为:\n");
    for (k = 0; k < (N+1)*N/2; ++k)
    {
        printf("%-3d", arr1[k]);
    }
    printf("\n以列为主序压缩所得结果为:\n");
    for (k = 0; k < (N+1)*N/2; ++k)
    {
        printf("%-3d", arr2[k]);
    }
    printf("\n");
    
    return;
}