注意:文中彩色代码均在Visual Studio 2022编译器中编写,本文为C语言数据结构手抄版,文中有部分改动,非原创。
目录
第四章 多维数组和广义表
学习目标
- 熟悉数组在按行优先顺序的存储结构中的地址计算方法。
- 熟悉特殊矩阵在压缩存储时的下标变换方法。
- 理解稀疏矩阵的三元组表存储表示方法及有关算法。
- 熟悉广义表的有关概念,理解广义表的括号表示和图形表示之间的转换。
- 理解广义表的求表头和表尾的运算。
4.1.多维数组和运算
数组是我们比较熟悉的一种数据类型。由于数组中各元素具有相同的数据类型,并且数组元素的下标一般具有固定的上界和下界,因此,数组的处理比其他复杂的结构较为简单。当数组维数为1时,数组是一种元素个数固定的线性表,而维数大于1时,称为多维数组,它可以看成是线性表的推广。这里仅讨论多维数组。
由于对数组一般不做插入和删除操作,因此数组一旦建立,结构中的元素个数和元素间的关系就不再发生变化。所以,一般都是采用顺序存储的方法来表示数组。由于在本书中的算法都是用C语言函数来描述的,而C语言的数组下标是从0开始的,因此二维数组A可用如图4.1所示的矩阵形式表示。
由于计算机的内存结构是一维的,所以多维数组的数据存储结构就必须按某种次序将数组元素排成一个线性序列。因此,二维数组又可以以行向量形式表示为:
Amxn=[[a00, a01, .., a0n-1], [a10, a11, .., a1 n-1], ..., [am-10, am-11, .., am-1n-1]]
或者以列向量形式表示为:
Amxn=[[a00, a10, .., am-10], [a01, a11, .., am-11], ..., [a0n-1, a1n-1, .., am-1n-1]]
多维数组是一种复杂的数据结构。以二维数组为例,数组元素之间的关系除了边界元素外,每个元素aij都恰好有两个直接前趋和两个直接后继:行向量上的直接前趋是aij-1和aij+1,列向量上的直接前趋是ai-1j和直接后继ai+1j。并且二维数组也只有一个开始结点a00,它没有前趋;仅有一个终端结点am-1n-1,它没有后继。此外,边界上的结点(开始结点和终端结点除外)只有一个直接前趋或者只有一个直接后继。
4.1.1.数组的顺序存储
数组在各种高级语言中通常也有两种不同的顺序存储方式,其中最典型的Pascal和C语言是按行优先顺序存储的,而Fortran语言则是按列优先顺序存储的。
1.按行优先顺序存储,即将数组元素按行向量排列,第i+1个行向量紧接在第i个行向量后面。A的m x n个元素按行优先顺序存储的线性序列为:
a00, a01, ..., a0n-1, a10, a11, .., a1n-1, ..., am-10, am-11, ..., am-1n-1
2.按列优先顺序存储,即将数组元素按列向量排列,第j+1个列向量紧接在第j个列向量之后, A的m x n个元素按列优先顺序存储的线性序列为:
a00, a10, ..., am-10, a01, a11, .., am-11, ..., a0n-1, a1n-1, ..., am-1n-1
如果按上述两种方式顺序存储数组,只要知道开始结点的存储地址(即基地址),维数和每维的上、下界,以及每个元素所占用的单元数,就可以将每个数组元素的存储地址表示为其下标的线性函数。例如,二维数组Am x n按行优先顺序存储在内存中,假设每个元素占d个存储单元,数组元素aij (i=0, 1, .., m-1; j=0, 1, .., n-1)位于第i行、第j列,前面i行共有i x n个元素,第i行上aij;前面又有j个元素,因此它的前面一共有i x n + j个元素,所以在C语言中的数组元素aij的地址计算函数为:
LOC(aij)=LOC(a00)+(i x n + j)×d
例如,有数组A4x5, d=2, LOC (a00)=100,计算a23的存储地址。
因为i=2, j=3, n=5,根据地址计算函数得:
LOC (a23) =100+ (2×5+3) ×2=126
同理,三维数组Am×n×p按行优先顺序在内存中,计算数组元素aijk的地址计算函数为:
LOC (aijk)=LOC (a000) + (i × n × p+j × p+k) × d
疑问:这些计算公式是怎么来的?
答:它是按行优先顺序存储在内存中,所以你要计算地址值需要计算出前面所有被填满的空位。二维平面数组,三维空间数据。
4.1.2.数组运算举例
【例4.1】设计一个算法,实现矩阵Amm的转置矩阵Bnm。
分析:对于一个m × n的矩阵A,其转置矩阵是一个n × m的矩阵B,而且B[ij] =A[j][i],0≦i≦n-1, 0≦j≦m-1。假设m=5, n-8,其实现算法如下:
void trsmat() { int i,j; for(j=0;j<m;j++) for(i=0;i<n;i++) b[i][j]=a[j][i] } |
【例4.2】如果矩阵A中存在这样的一个元素A[i][j],满足: A[i][j]是第i行元素中最小值,且又是第j列元素中最大值,则称此元素为该矩阵的一个马鞍点。假设以二维数组存储矩阵Am x n,试编写求出矩阵中所有马鞍点的算法。
分析:按照题意,先求出每行中的最小值元素,存入数组Min[m]之中再求出每列的最大值元素,存入数组Max[n]之中。若某元素既在Min[i]中又在Max[j]中,则该元素A[i][j]就是马鞍点,找出所有这样元素。因此,实现该题要求的算法如下:
void MaxMin(int A[4][5],int m, int n) { int i,j; int Max[5], Min[4]; for(i=0;i<m;i++){ //计算每行的最小值元素,存入Min数组中 Min[i]=A[i][0]; //先假设第i行第一个元素最小,然后在与后面的元素比较 for(j=1;j<n;j++) if(A[i][j]<Min[i]) Min[i]=A[i][j]; } for(j=0;j<n;j++){ //计算每列的最大值元素,存入Max数组中 Max[j]=A[0][j]; //假设第j列第一个元素最大,然后再与后面的元素比较 for(i=1;i<M;i++) if(A[i][j]>Max[j]) Max[j]=A[i][j]; } for(i=0;i<m;i++) //判断是否为马鞍点 for(j=0;j<n;j++) if(Min[i]==Max[j]) printf(“(%d,%d)”,i,j); //显示马鞍点 } |
4.2.矩阵的压缩存储
在许多科学计算和工程应用中,经常要用到矩阵的概念。由于矩阵具有元素数目固定以及元素按下标关系有序排列等特点,所以在使用高级语言编程时,一般都是使用二维数组来存储矩阵,这种表示可以对其元素随机存取,方便进行矩阵的各种运算。在第4.1节的例子中已经用过数组存储矩阵进行有关的运算,但是在有些情况下,矩阵中含有许多值相同或者值为零的元素,如果还按前面的方法来存储这种矩阵,就会产生大量的空间浪费。为了节省存储空间,可以对这类矩阵采用压缩存储。
4.2.1.✮✮✮☛特殊矩阵
所谓特殊矩阵,指的是相同值的元素或者零元素在矩阵中的分布有一定规律的矩阵。下面分别讨论几种特殊矩阵的压缩存储。
1.对称矩阵
若n阶方阵A中的元素满足下述性质:
aij=aji(0≦i,j≦n-1)
则称A为n阶的对称矩阵。对称矩阵中的元素是关于主对角线对称的,所以只需要存储矩阵上三角或下三角的元素即可,让两个对称的元素共享一个存储空间。这样,就能够节省近一半的存储空间。按C语言的“按行优先”存储主对角线(包括主对角线)以下的元素,如图4.2所示。
在以上的下三角矩阵中,第i行(0≦i,j≦n-1)恰好有i+1个元素,所以元素总数为
现假设以一维数组sa[n (n+1) /2]作为n阶对称矩阵A的存储结构,那么,矩阵中元素aij和数组元素sa[k]之间存在着一一对应关系。这种对应关系分析如下:
若i≥j时,则aij在下三角矩阵中。aij之前i行(从0行到第i-1行)一共有1+2+3...+ i=i × (i+1) /2个元素,在第i行上,aij之前恰有j个元素。因此有:
k=i × (i+1)/2+j (0≤k≤n(n+1)/2)
若i<j时,则aij在上三角矩阵中。因为有aij=aji,所以只要交换i和j即可得到:
k=j × (j+1)/2+i (0≤k≤n(n+1)/2)
因此,有:
由此,aij的存储地址可用下面的公式计算:
LOC (aij)=LOC (sa[k]) =LOC (sa[0]) +k × d
有了上述的计算公式,就能够立即找到矩阵元素a在其压缩存储表示sa中的对应位置k。例如,a32和a23都存储在sa[8]中,这是因为
k=i × (i+1)/2+j=3 × (3+1)/2+2=8
对称矩阵压缩存储可以使用一维数组作为对称矩阵的存储数据的容器,创建头文件symmetricMatrix.h定义对称矩阵插入与获取数据功能。
#pragma once #ifndef SYMMETRIC_MATRIX_H #define SYMMETRIC_MATRIX_H // sɪˈmetrɪk ˈmeɪtrɪks struct SymmetricMatrix { struct MatrixArray* matrixArray; // æbˈsɪsə ˈɔːdɪnət int (*insert)(int row, int column, void* data, struct SymmetricMatrix* matrix); void* (*get)(int row, int column, struct SymmetricMatrix* matrix); int (*capacity)(struct SymmetricMatrix* matrix); void (*foreach)(void (*printSymmetricMatrix)(void* data), struct SymmetricMatrix* matrix); void* (*removeByIndex)(int row, int column, struct SymmetricMatrix* matrix); void* (*removeByData)(void* data, int (*compare)(void* firstData, void* secondData), struct SymmetricMatrix* matrix); void** (*clear)(struct SymmetricMatrix* matrix); void** (*destroy)(struct SymmetricMatrix* matrix); }; struct SymmetricMatrix* initSymmetricMatrix(int matrixSize); #endif |
创建symmetricMatrix.c文件symmetricMatrix.h实现堆成矩阵操作。
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "symmetricMatrix.h" struct MatrixArray* initMatrixArray(int matrixSize); int insertSymmetricMatrix(int row, int column, void* data, struct SymmetricMatrix* matrix); void* getSymmetricMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix); int capacitySymmetricMatrix(struct SymmetricMatrix* matrix); void foreachSymmetricMatrix(void (*printSymmetricMatrix)(void* data), struct SymmetricMatrix* matrix); void* removeMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix); void* removeMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct SymmetricMatrix* matrix); void** clearSymmetricMatrix(struct SymmetricMatrix* matrix); void** destroySymmetricMatrix(struct SymmetricMatrix* matrix); struct SymmetricMatrix* initSymmetricMatrix(int matrixSize) { struct SymmetricMatrix* symmetricMatrix = malloc(sizeof(struct SymmetricMatrix)); struct MatrixArray* matrix = initMatrixArray(matrixSize); if (symmetricMatrix == NULL || matrix == NULL) { printf("创建对称矩阵,分配内存空间失败!\n"); return NULL; } symmetricMatrix->matrixArray = matrix; symmetricMatrix->insert = insertSymmetricMatrix; symmetricMatrix->get = getSymmetricMatrixByIndex; symmetricMatrix->capacity = capacitySymmetricMatrix; symmetricMatrix->foreach = foreachSymmetricMatrix; symmetricMatrix->removeByIndex = removeMatrixByIndex; symmetricMatrix->removeByData = removeMatrixByData; symmetricMatrix->clear = clearSymmetricMatrix; symmetricMatrix->destroy = destroySymmetricMatrix; return symmetricMatrix; } struct MatrixArray { void** array; int capacity; }; int getMatrixArrayIndex(int row, int column) { if (row >= column) { return row * (row + 1) / 2 + column; } else { return column * (column + 1) / 2 + row; } } struct MatrixArray* initMatrixArray(int matrixSize) { struct MatrixArray* matrix = malloc(sizeof(struct MatrixArray)); if (matrix == NULL) { return NULL; } matrix->capacity = getMatrixArrayIndex(matrixSize - 1, matrixSize - 1) + 1; matrix->array = calloc(matrix->capacity, sizeof(void*)); if (matrix == NULL) { return NULL; } return matrix; } int insertSymmetricMatrix(int row, int column, void* data, struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return 0; } if (row < 0 || column < 0) { printf("插入对称矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } if (data == NULL) { printf("插入对称矩阵数据不存在!"); return 0; } int index = getMatrixArrayIndex(row, column); if (index >= matrixArray->capacity) { printf("对称矩阵行列越界异常!"); return 0; } matrixArray->array[index] = data; return 1; } void* getSymmetricMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } if (row < 0 || column < 0) { printf("获取对称矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } int index = getMatrixArrayIndex(row, column); if (index >= matrixArray->capacity) { printf("对称矩阵行列越界异常!"); return 0; } void* result = matrixArray->array[index]; return matrixArray->array[index]; } int capacitySymmetricMatrix(struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return -1; } return matrixArray->capacity; } void foreachSymmetricMatrix(void (*printSymmetricMatrix)(void* data), struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return; } int weight = 1; int pedometer = weight; printf("方阵对称矩阵:\n"); for (int i = 0; i < matrixArray->capacity; i++) { printSymmetricMatrix(matrixArray->array[i]); printf("\t"); if ((i + 1) % pedometer == 0) { printf("\n"); pedometer = pedometer + ++weight; } } } void* removeMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } int index = getMatrixArrayIndex(row, column); if (index >= matrixArray->capacity) { printf("对称矩阵行列越界异常!"); return 0; } void* result = matrixArray->array[index]; matrixArray->array[index] = NULL; return result; } void* removeMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } for (int i = 0; i < matrixArray->capacity; i++) { if (compare(data, matrixArray->array[i])) { void* result = matrixArray->array[i]; matrixArray->array[i] = NULL; return result; } } return NULL; } void** clearSymmetricMatrix(struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } void** result = calloc(matrixArray->capacity, sizeof(void*)); memcpy(result, matrixArray->array, sizeof(void*) * matrixArray->capacity); memset(matrixArray->array, NULL, sizeof(void*) * matrixArray->capacity); return result; } void** destroySymmetricMatrix(struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } void** result = clearSymmetricMatrix(matrix); free(matrixArray); matrixArray = NULL; free(matrix); matrix = NULL; return result; } |
测试对称矩阵:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include "symmetricMatrix.h" void print(void* data) { if (data == NULL) { printf("NULL"); return; } int* num = data; printf("%d ", num); } int compare(void* firstData, void* secondData) { return firstData == secondData; } int main() { struct SymmetricMatrix* matrixA = initSymmetricMatrix(3); matrixA->insert(0, 0, 2, matrixA); matrixA->insert(0, 1, 5, matrixA); matrixA->insert(0, 2, 1, matrixA); matrixA->insert(1, 1, 4, matrixA); matrixA->insert(1, 2, 2, matrixA); matrixA->insert(2, 2, 7, matrixA); printf("容量:%d\n", matrixA->capacity(matrixA)); matrixA->foreach(print, matrixA); matrixA->removeByData(7, compare, matrixA); printf("删除一个值:\n"); matrixA->foreach(print, matrixA); matrixA->destroy(matrixA); return 0; } |
测试结果:
容量:6 方阵对称矩阵: 2 5 4 1 2 7 删除一个值: 方阵对称矩阵: 2 5 4 1 2 NULL |
从测试结果可以看到矩阵元素a45与a54存储的数值相同。
【例4.3】 已知A和B是两个n × n阶的对称矩阵,因为是对称矩阵,所以仅需要输入下三角元素值存入一维数组。试写一算法,求对称矩阵A和B的乘积。
分析:如果是两个完整的矩阵相乘,其算法是比较简单的,但由于是对称矩阵,所以要清楚对称矩阵的第i行和第j列的元素数据在一维数组中的位置,其位置计算公式为:
k=i × (i+1) /2+j 当i≥j时(Aij,Bij,处在下三角中) ;
k=j × (j+1) /2+i 当i<j时(Aij,Bij,处在上三角中) ;
其中, k代表AIJ或BIJ,在其对称矩阵中的位置,而且0≤k<n (n+1)/2。因此,实现本题功能的算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include "symmetricMatrix.h" void print(void* data) { if (data == NULL) { printf("%d NULL", data); return; } int* num = data; printf("%d ", num); } void multiplication(struct SymmetricMatrix* matrixA, struct SymmetricMatrix* matrixB) { int num = 3; int result[3][3] = { 0 }; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { int add = 0; for (int k = 0; k < num; k++) { int a = matrixA->get(i, k, matrixA); int b = matrixB->get(k, j, matrixB); add += a * b; } result[i][j] = add; } } int weight = 3; int pedometer = weight; printf("矩阵乘积:\n"); for (int i = 0; i < 9; i++) { printf("%d ", result[0][i]); if ((i + 1) % pedometer == 0) { printf("\n"); pedometer = pedometer + weight; } } } int main() { struct SymmetricMatrix* matrixA = initSymmetricMatrix(3); matrixA->insert(0, 0, 2, matrixA); matrixA->insert(0, 1, 5, matrixA); matrixA->insert(0, 2, 1, matrixA); matrixA->insert(1, 1, 4, matrixA); matrixA->insert(1, 2, 2, matrixA); matrixA->insert(2, 2, 7, matrixA); printf("容量:%d\n", matrixA->capacity(matrixA)); matrixA->foreach(print, matrixA); struct SymmetricMatrix* matrixB = initSymmetricMatrix(3); matrixB->insert(0, 0, 2, matrixB); matrixB->insert(0, 1, 5, matrixB); matrixB->insert(0, 2, 3, matrixB); matrixB->insert(1, 1, 6, matrixB); matrixB->insert(1, 2, 8, matrixB); matrixB->insert(2, 2, 1, matrixB); printf("容量:%d\n", matrixB->capacity(matrixB)); matrixB->foreach(print, matrixB); multiplication(matrixA, matrixB); return 0; } |
运行结果:
容量:6 方阵对称矩阵: 2 5 4 1 2 7 容量:6 方阵对称矩阵: 2 5 6 3 8 1 矩阵乘积: 32 48 47 36 65 49 33 73 26 |
2.三角矩阵
以主对角线划分,三角矩阵有上三角和下三角两种。下三角矩阵正好相反,它的主对角线上方均为常数c或零,如图4.3 (a)所示;上三角矩阵是指矩阵的下三角(不包括对角线)中的元素均为常数c或是零的n阶方阵,如图4.3 (b)所示。一般情况下,三角矩阵的常数c均为零。
三角矩阵(n × n阶的方阵)中的重复元素c可共享一个存储空间,其余的元素正好有n (n+1) /2个,因此,三角矩阵可压缩存储在一维数组sa[n (n+1)/2+1]中,其中c存放在数组的最后一个元素中。
在上三角矩阵中,主对角线上第m(0<m<n)行上恰好有n-m个元素,按行优先顺序存储上三角矩阵中元素aij时,aij之前有i行(0~i-1),一共有元素个数:
而在第i行,aij之前有j-i个元素,因此sa[k]和aij存储位置的对应关系为:
创建头文件upperTriangleMatrix.h:
#pragma once #ifndef UPPER_TRIANGLE_MATRIX_H #define UPPER_TRIANGLE_MATRIX_H struct UpperTriangleMatrix { struct UpperMatrixArray* upperMatrix; // æbˈsɪsə ˈɔːdɪnət int (*insert)(int row, int column, void* data, struct UpperTriangleMatrix* matrix); void* (*get)(int row, int column, struct UpperTriangleMatrix* matrix); int (*capacity)(struct UpperTriangleMatrix* matrix); void (*foreach)(void (*printUpperTriangleMatrix)(void* data), struct UpperTriangleMatrix* matrix); void* (*removeByIndex)(int row, int column, struct UpperTriangleMatrix* matrix); void* (*removeByData)(void* data, int (*compare)(void* firstData, void* secondData), struct UpperTriangleMatrix* matrix); void** (*clear)(struct UpperTriangleMatrix* matrix); void** (*destroy)(struct UpperTriangleMatrix* matrix); }; struct UpperTriangleMatrix* initUpperTriangleMatrix(int matrixSize); #endif |
实现头文件逻辑upperTriangleMatrix.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "upperTriangleMatrix.h" struct UpperMatrixArray* initUpperMatrixArray(int matrixSize); int insertUpperTriangleMatrix(int row, int column, void* data, struct UpperTriangleMatrix* matrix); void* getUpperTriangleMatrixByIndex(int row, int column, struct UpperTriangleMatrix* matrix); int capacityUpperTriangleMatrix(struct UpperTriangleMatrix* matrix); void foreachUpperTriangleMatrix(void (*printUpperTriangleMatrix)(void* data), struct UpperTriangleMatrix* matrix); void* removeUpperMatrixByIndex(int row, int column, struct UpperTriangleMatrix* matrix); void* removeUpperMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct UpperTriangleMatrix* matrix); void** clearUpperTriangleMatrix(struct UpperTriangleMatrix* matrix); void** destroyUpperTriangleMatrix(struct UpperTriangleMatrix* matrix); struct UpperTriangleMatrix* initUpperTriangleMatrix(int matrixSize) { struct UpperTriangleMatrix* upperTriangleMatrix = malloc(sizeof(struct UpperTriangleMatrix)); struct UpperMatrixArray* matrix = initUpperMatrixArray(matrixSize); if (upperTriangleMatrix == NULL || matrix == NULL) { printf("创建上三角矩阵,分配内存空间失败!\n"); return NULL; } upperTriangleMatrix->upperMatrix = matrix; upperTriangleMatrix->insert = insertUpperTriangleMatrix; upperTriangleMatrix->get = getUpperTriangleMatrixByIndex; upperTriangleMatrix->capacity = capacityUpperTriangleMatrix; upperTriangleMatrix->foreach = foreachUpperTriangleMatrix; upperTriangleMatrix->removeByIndex = removeUpperMatrixByIndex; upperTriangleMatrix->removeByData = removeUpperMatrixByData; upperTriangleMatrix->clear = clearUpperTriangleMatrix; upperTriangleMatrix->destroy = destroyUpperTriangleMatrix; return upperTriangleMatrix; } struct UpperMatrixArray { void** array; int capacity; int matrixSize; }; int getUpperMatrixArrayIndex(int row, int column, struct UpperTriangleMatrix* matrix) { int matrixSize = matrix->upperMatrix->matrixSize; if (row > column) { return matrixSize * (matrixSize + 1) / 2; } else { return row * (2 * matrixSize - row + 1) / 2 + column - row; } } struct UpperMatrixArray* initUpperMatrixArray(int matrixSize) { struct UpperMatrixArray* matrix = malloc(sizeof(struct UpperMatrixArray)); if (matrix == NULL) { return NULL; } matrix->matrixSize = matrixSize; matrix->capacity = matrixSize * (matrixSize + 1) / 2 + 1; matrix->array = calloc(matrix->capacity, sizeof(void*)); matrix->array[matrix->capacity - 1] = NULL; if (matrix == NULL) { return NULL; } return matrix; } int insertUpperTriangleMatrix(int row, int column, void* data, struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return 0; } if (row < 0 || column < 0) { printf("插入上三角矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } if (data == NULL) { printf("插入上三角矩阵数据不存在!"); return 0; } int index = getUpperMatrixArrayIndex(row, column, matrix); if (index >= upperMatrix->capacity) { printf("上三角矩阵行列越界异常!"); return 0; } upperMatrix->array[index] = data; return 1; } void* getUpperTriangleMatrixByIndex(int row, int column, struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return NULL; } if (row < 0 || column < 0) { printf("获取上三角矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } int index = getUpperMatrixArrayIndex(row, column, matrix); if (index >= upperMatrix->capacity) { printf("上三角矩阵行列越界异常!"); return 0; } void* result = upperMatrix->array[index]; return upperMatrix->array[index]; } int capacityUpperTriangleMatrix(struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return -1; } return upperMatrix->capacity; } void foreachUpperTriangleMatrix(void (*printUpperTriangleMatrix)(void* data), struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return; } int weight = 1; int pedometer = matrix->upperMatrix->matrixSize + 1; printf("方阵上三角矩阵:\n"); for (int i = 0; i < upperMatrix->capacity - 1; i++) { if ((i + 1) % pedometer == 0) { printf("\n"); for (int j = 0; j < weight; j++) { printUpperTriangleMatrix(upperMatrix->array[upperMatrix->capacity - 1]); printf("\t"); } pedometer = pedometer + matrix->upperMatrix->matrixSize - weight++; } printUpperTriangleMatrix(upperMatrix->array[i]); printf("\t"); } printf("\n"); } void* removeUpperMatrixByIndex(int row, int column, struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return NULL; } int index = getUpperMatrixArrayIndex(row, column, matrix); if (index >= upperMatrix->capacity) { printf("上三角矩阵行列越界异常!"); return 0; } void* result = upperMatrix->array[index]; upperMatrix->array[index] = NULL; return result; } void* removeUpperMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return NULL; } for (int i = 0; i < upperMatrix->capacity; i++) { if (compare(data, upperMatrix->array[i])) { void* result = upperMatrix->array[i]; upperMatrix->array[i] = NULL; return result; } } return NULL; } void** clearUpperTriangleMatrix(struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return NULL; } void** result = calloc(upperMatrix->capacity, sizeof(void*)); memcpy(result, upperMatrix->array, sizeof(void*) * upperMatrix->capacity); memset(upperMatrix->array, NULL, sizeof(void*) * upperMatrix->capacity); return result; } void** destroyUpperTriangleMatrix(struct UpperTriangleMatrix* matrix) { struct UpperMatrixArray* upperMatrix = matrix->upperMatrix; if (upperMatrix == NULL) { printf("上三角矩阵内存空间不存在!"); return NULL; } void** result = clearUpperTriangleMatrix(matrix); free(upperMatrix); upperMatrix = NULL; free(matrix); matrix = NULL; return result; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include "upperTriangleMatrix.h" void print(void* data) { if (data == NULL) { printf("null"); return; } int* num = data; printf("%d ", num); } int compare(void* firstData, void* secondData) { return firstData == secondData; } int main() { struct UpperTriangleMatrix* matrixA = initUpperTriangleMatrix(4); matrixA->insert(0, 0, 2, matrixA); matrixA->insert(0, 1, 5, matrixA); matrixA->insert(0, 2, 1, matrixA); matrixA->insert(0, 3, 9, matrixA); matrixA->insert(1, 1, 4, matrixA); matrixA->insert(1, 2, 2, matrixA); matrixA->insert(1, 3, 8, matrixA); matrixA->insert(2, 2, 7, matrixA); matrixA->insert(2, 3, 6, matrixA); matrixA->insert(3, 3, 4, matrixA); printf("容量:%d\n", matrixA->capacity(matrixA)); matrixA->foreach(print, matrixA); printf("删除:%d\n", matrixA->removeByIndex(1,2, matrixA)); printf("删除:%d\n", matrixA->removeByData(9, compare, matrixA)); matrixA->foreach(print, matrixA); matrixA->destroy(matrixA); return 0; } |
运行结果:
容量:11 方阵上三角矩阵: 2 5 1 9 null 4 2 8 null null 7 6 null null null 4 删除:2 删除:9 方阵上三角矩阵: 2 5 1 null null 4 null 8 null null 7 6 null null null 4 |
而在下三角矩阵中, sa[K]和aij存储位置对应关系与前面介绍的对称矩阵压缩存储类似,只是在当i<j时,下三角矩阵中仅有一个存储位置。因此,该对应关系为:
对于这种特殊矩阵,按上述的对应关系就可以对其元素进行随机存取。
创建头文件downTriangleMatrix.h:
#pragma once #ifndef DOWN_TRIANGLE_MATRIX_H #define DOWN_TRIANGLE_MATRIX_H struct DownTriangleMatrix { struct DownMatrixArray* downMatrix; // æbˈsɪsə ˈɔːdɪnət int (*insert)(int row, int column, void* data, struct DownTriangleMatrix* matrix); void* (*get)(int row, int column, struct DownTriangleMatrix* matrix); int (*capacity)(struct DownTriangleMatrix* matrix); void (*foreach)(void (*printDownTriangleMatrix)(void* data), struct DownTriangleMatrix* matrix); void* (*removeByIndex)(int row, int column, struct DownTriangleMatrix* matrix); void* (*removeByData)(void* data, int (*compare)(void* firstData, void* secondData), struct DownTriangleMatrix* matrix); void** (*clear)(struct DownTriangleMatrix* matrix); void** (*destroy)(struct DownTriangleMatrix* matrix); }; struct DownTriangleMatrix* initDownTriangleMatrix(int matrixSize); #endif |
实现头文件逻辑downTriangleMatrix.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "downTriangleMatrix.h" struct DownMatrixArray* initDownMatrixArray(int matrixSize); int insertDownTriangleMatrix(int row, int column, void* data, struct DownTriangleMatrix* matrix); void* getDownTriangleMatrixByIndex(int row, int column, struct DownTriangleMatrix* matrix); int capacityDownTriangleMatrix(struct DownTriangleMatrix* matrix); void foreachDownTriangleMatrix(void (*printDownTriangleMatrix)(void* data), struct DownTriangleMatrix* matrix); void* removeDownMatrixByIndex(int row, int column, struct DownTriangleMatrix* matrix); void* removeDownMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct DownTriangleMatrix* matrix); void** clearDownTriangleMatrix(struct DownTriangleMatrix* matrix); void** destroyDownTriangleMatrix(struct DownTriangleMatrix* matrix); struct DownTriangleMatrix* initDownTriangleMatrix(int matrixSize) { struct DownTriangleMatrix* downTriangleMatrix = malloc(sizeof(struct DownTriangleMatrix)); struct DownMatrixArray* matrix = initDownMatrixArray(matrixSize); if (downTriangleMatrix == NULL || matrix == NULL) { printf("创建下三角矩阵,分配内存空间失败!\n"); return NULL; } downTriangleMatrix->downMatrix = matrix; downTriangleMatrix->insert = insertDownTriangleMatrix; downTriangleMatrix->get = getDownTriangleMatrixByIndex; downTriangleMatrix->capacity = capacityDownTriangleMatrix; downTriangleMatrix->foreach = foreachDownTriangleMatrix; downTriangleMatrix->removeByIndex = removeDownMatrixByIndex; downTriangleMatrix->removeByData = removeDownMatrixByData; downTriangleMatrix->clear = clearDownTriangleMatrix; downTriangleMatrix->destroy = destroyDownTriangleMatrix; return downTriangleMatrix; } struct DownMatrixArray { void** array; int capacity; int matrixSize; }; int getDownMatrixArrayIndex(int row, int column, struct DownTriangleMatrix* matrix) { int matrixSize = matrix->downMatrix->matrixSize; if (row < column) { return matrixSize * (matrixSize + 1) / 2; } else { return row * (row + 1) / 2 + column; } } struct DownMatrixArray* initDownMatrixArray(int matrixSize) { struct DownMatrixArray* matrix = malloc(sizeof(struct DownMatrixArray)); if (matrix == NULL) { return NULL; } matrix->matrixSize = matrixSize; matrix->capacity = matrixSize * (matrixSize + 1) / 2 + 1; matrix->array = calloc(matrix->capacity, sizeof(void*)); matrix->array[matrix->capacity - 1] = NULL; if (matrix == NULL) { return NULL; } return matrix; } int insertDownTriangleMatrix(int row, int column, void* data, struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return 0; } if (row < 0 || column < 0) { printf("插入下三角矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } if (data == NULL) { printf("插入下三角矩阵数据不存在!"); return 0; } int index = getDownMatrixArrayIndex(row, column, matrix); if (index >= downMatrix->capacity) { printf("下三角矩阵行列越界异常!"); return 0; } downMatrix->array[index] = data; return 1; } void* getDownTriangleMatrixByIndex(int row, int column, struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return NULL; } if (row < 0 || column < 0) { printf("获取下三角矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } int index = getDownMatrixArrayIndex(row, column, matrix); if (index >= downMatrix->capacity) { printf("下三角矩阵行列越界异常!"); return 0; } void* result = downMatrix->array[index]; return downMatrix->array[index]; } int capacityDownTriangleMatrix(struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return -1; } return downMatrix->capacity; } void foreachDownTriangleMatrix(void (*printDownTriangleMatrix)(void* data), struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return; } int weight = 1; int pedometer = weight; printf("方阵下三角矩阵:\n"); for (int i = 0; i < downMatrix->capacity - 1; i++) { printDownTriangleMatrix(downMatrix->array[i]); printf("\t"); if ((i + 1) % pedometer == 0) { for (int j = 0; j < downMatrix->matrixSize - weight; j++) { printDownTriangleMatrix(downMatrix->array[downMatrix->capacity - 1]); printf("\t"); } printf("\n"); pedometer = pedometer + ++weight; } } } void* removeDownMatrixByIndex(int row, int column, struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return NULL; } int index = getDownMatrixArrayIndex(row, column, matrix); if (index >= downMatrix->capacity) { printf("下三角矩阵行列越界异常!"); return 0; } void* result = downMatrix->array[index]; downMatrix->array[index] = NULL; return result; } void* removeDownMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return NULL; } for (int i = 0; i < downMatrix->capacity; i++) { if (compare(data, downMatrix->array[i])) { void* result = downMatrix->array[i]; downMatrix->array[i] = NULL; return result; } } return NULL; } void** clearDownTriangleMatrix(struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return NULL; } void** result = calloc(downMatrix->capacity, sizeof(void*)); memcpy(result, downMatrix->array, sizeof(void*) * downMatrix->capacity); memset(downMatrix->array, NULL, sizeof(void*) * downMatrix->capacity); return result; } void** destroyDownTriangleMatrix(struct DownTriangleMatrix* matrix) { struct DownMatrixArray* downMatrix = matrix->downMatrix; if (downMatrix == NULL) { printf("下三角矩阵内存空间不存在!"); return NULL; } void** result = clearDownTriangleMatrix(matrix); free(downMatrix); downMatrix = NULL; free(matrix); matrix = NULL; return result; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include "downTriangleMatrix.h" void print(void* data) { if (data == NULL) { printf("null"); return; } int* num = data; printf("%d ", num); } int compare(void* firstData, void* secondData) { return firstData == secondData; } int main() { struct DownTriangleMatrix* matrixA = initDownTriangleMatrix(4); matrixA->insert(0, 0, 2, matrixA); matrixA->insert(1, 0, 5, matrixA); matrixA->insert(1, 1, 1, matrixA); matrixA->insert(2, 0, 9, matrixA); matrixA->insert(2, 1, 4, matrixA); matrixA->insert(2, 2, 4, matrixA); matrixA->insert(3, 0, 2, matrixA); matrixA->insert(3, 1, 8, matrixA); matrixA->insert(3, 2, 7, matrixA); matrixA->insert(3, 3, 6, matrixA); printf("容量:%d\n", matrixA->capacity(matrixA)); matrixA->foreach(print, matrixA); printf("删除:%d\n", matrixA->removeByIndex(0, 0, matrixA)); printf("删除:%d\n", matrixA->removeByData(9, compare, matrixA)); matrixA->foreach(print, matrixA); matrixA->destroy(matrixA); return 0; } |
运行结果:
容量:11 方阵下三角矩阵: 2 null null null 5 1 null null 9 4 4 null 2 8 7 6 删除:2 删除:9 方阵下三角矩阵: null null null null 5 1 null null null 4 4 null 2 8 7 6 |
4.2.2.稀疏矩阵
由于特殊矩阵中非零元素的分布是有规律的,因此总可以找到矩阵元素与一维数的下标的对应关系。但还有一种矩阵,其中有s个非零元素,而s远远小于矩阵元素总数,通常把这种矩阵称为稀疏矩阵。为了节省存储单元,也可用压缩存储方法只存位非零元素。由于稀疏矩阵非零元素的分布一般是没有规律的,因此在存储非零元素时,除了存储非零元素的值之外,还必须同时存储该元素的行、列位置(即下标),所以可用一个称为三元组(i, j, aij)来唯一确定一个非零元素。当用三元组来表示非零元素时,对稀疏矩阵进行压缩存储通常有两种方法:顺序存储和链式存储。链式存储一般采用的是十字链表法,结构比较复杂。
1.三元组表
如果将表示稀疏矩阵非零元素的三元组按行优先的顺序排列,则可得到一个其结点均为三元组的线性表,将这种线性表的顺序存储结构称为三元组表。
为了操作方便,对稀疏矩阵的总行数、总列数以及非零元素个数均作为三元组表的辅助属性加以描述。三元组表的类型定义如下:
// spɑːs ˈtʌpəl struct SparseTupleNode { void* data; // 非0元素值 int row; // 行索引 int column; // 列索引 }; // spɑːs ˈmeɪtrɪks struct SparseMatrix { struct ArrayList* tupleArray; // 动态数组容器 int rowAmount; // 行 int columnAmount; // 列 int elementAmount; // 元素数量 }; |
编写头文件sparseMatrix.h。
#pragma once #ifndef SPARSE_MATRIX_H #define SPARSE_MATRIX_H #include "dynamicArray.h" // spɑːs ˈmeɪtrɪks struct SparseMatrix { struct ArrayList* tupleArray; // 动态数组容器 int rowAmount; // 行 int columnAmount; // 列 int elementAmount; // 元素数量 int (*insert)(int row, int column, void* data, struct SparseMatrix* sparseMatrix); void* (*get)(int row, int column, struct SparseMatrix* sparseMatrix); void (*foreach)(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix); void (*foreachTuple)(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix); void* (*removeByIndex)(int row, int column, struct SparseMatrix* sparseMatrix); void* (*removeByData)(void* data, int (*compare)(void* firstData, void* secondData), struct SparseMatrix* sparseMatrix); struct ClearArea* (*clear)(struct SparseMatrix* sparseMatrix); struct ClearArea* (*destroy)(struct SparseMatrix* sparseMatrix); }; // spɑːs ˈtʌpəl struct SparseTupleNode { void* data; // 非0元素值 int row; // 行索引 int column; // 列索引 }; struct SparseMatrix* initSparseMatrix(int rowAmount, int columnAmount); #endif |
实现头文件定义sparseMatrix.c。
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sparseMatrix.h" int insertSparseMatrix(int row, int column, void* data, struct SparseMatrix* sparseMatrix); void* getSparseMatrix(int row, int column, struct SparseMatrix* sparseMatrix); void foreachSparseMatrix(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix); void foreachSparseTupleNode(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix); void* removeBySparseIndex(int row, int column, struct SparseMatrix* sparseMatrix); void* removeBySparseData(void* data, int (*compare)(void* firstData, void* secondData), struct SparseMatrix* sparseMatrix); struct ClearArea* clearSparseMatrix(struct SparseMatrix* sparseMatrix); struct ClearArea* destroySparseMatrix(struct SparseMatrix* sparseMatrix); struct SparseMatrix* initSparseMatrix(int rowAmount, int columnAmount) { struct SparseMatrix* sparseMatrix = malloc(sizeof(struct SparseMatrix)); struct ArrayList* arrayList = initArrayList(5); if (sparseMatrix == NULL || arrayList == NULL) { printf("创建稀疏矩阵,分配内存空间失败!\n"); return NULL; } sparseMatrix->rowAmount = rowAmount; sparseMatrix->columnAmount = columnAmount; sparseMatrix->tupleArray = arrayList; sparseMatrix->insert = insertSparseMatrix; sparseMatrix->get = getSparseMatrix; sparseMatrix->foreach = foreachSparseMatrix; sparseMatrix->foreachTuple = foreachSparseTupleNode; sparseMatrix->removeByIndex = removeBySparseIndex; sparseMatrix->removeByData = removeBySparseData; sparseMatrix->clear = clearSparseMatrix; sparseMatrix->destroy = destroySparseMatrix; return sparseMatrix; } int insertSparseMatrix(int row, int column, void* data, struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return 0; } if (row < 0 || row > sparseMatrix->rowAmount) { printf("输入行号不合法或超出稀疏矩阵容量!\n"); return 0; } if (column < 0 || column > sparseMatrix->columnAmount) { printf("输入列号不合法或超出稀疏矩阵容量!\n"); return 0; } struct SparseTupleNode* tupleNode = malloc(sizeof(struct SparseTupleNode)); if (tupleNode == NULL) { printf("内存空间开辟不足,插入元素失败!\n"); return 0; } tupleNode->data = data; tupleNode->row = row; tupleNode->column = column; if (tupleArray->size(tupleArray) <= 0) { tupleArray->insert(0, tupleNode, tupleArray); sparseMatrix->elementAmount = 1; return 1; } for (int i = 0; i < tupleArray->size(tupleArray); i++) { struct SparseTupleNode* currentNode = tupleArray->get(i, tupleArray); if (row < currentNode->row) { tupleArray->insert(i, tupleNode, tupleArray); sparseMatrix->elementAmount++; return 1; } if (row == currentNode->row && column < currentNode->column) { tupleArray->insert(i, tupleNode, tupleArray); sparseMatrix->elementAmount++; return 1; } if (row == currentNode->row && column == currentNode->column) { currentNode->data = data; return 1; } } tupleArray->insert(tupleArray->size(tupleArray), tupleNode, tupleArray); sparseMatrix->elementAmount++; return 1;; } void* getSparseMatrix(int row, int column, struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } if (row < 0 || row > sparseMatrix->rowAmount) { printf("输入行号不合法或超出稀疏矩阵容量!\n"); return NULL; } if (column < 0 || column > sparseMatrix->columnAmount) { printf("输入列号不合法或超出稀疏矩阵容量!\n"); return NULL; } for (int i = 0; i < tupleArray->size(tupleArray); i++) { struct SparseTupleNode* currentNode = tupleArray->get(i, tupleArray); if (row == currentNode->row && column == currentNode->column) { return currentNode->data; } } return NULL; } void foreachSparseMatrix(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } int arraySize = tupleArray->size(tupleArray); printf("稀疏矩阵共%d个非0元素!\n", arraySize); int flag = 1; for (int i = 0; i < sparseMatrix->rowAmount; i++) { for (int j = 0; j < sparseMatrix->columnAmount; j++) { flag = 1; for (int k = 0; k < arraySize; k++) { struct SparseTupleNode* currentNode = tupleArray->get(k, tupleArray); if (currentNode->row == i && currentNode->column == j) { printUpperTriangleMatrix(currentNode->data); printf("\t"); flag = 0; } } if (flag) { printf("NULL\t"); } } printf("\n"); } } void foreachSparseTupleNode(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } int arraySize = tupleArray->size(tupleArray); printf("稀疏矩阵共%d个非0元素!\n", arraySize); for (int i = 0; i < arraySize; i++) { struct SparseTupleNode* currentNode = tupleArray->get(i, tupleArray); printf("%d ", currentNode->row); printf("%d ", currentNode->column); printUpperTriangleMatrix(currentNode->data); printf("\n"); } } void* removeBySparseIndex(int row, int column, struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } for (int i = 0; i < tupleArray->size(tupleArray); i++) { struct SparseTupleNode* currentNode = tupleArray->get(i, tupleArray); if (row == currentNode->row && column == currentNode->column) { struct SparseTupleNode* tupleNode = tupleArray->removeByIndex(i, tupleArray); sparseMatrix->elementAmount = tupleArray->size(tupleArray); return tupleNode->data; } } return NULL; } void* removeBySparseData(void* data, int (*compare)(void* firstData, void* secondData), struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } for (int i = 0; i < tupleArray->size(tupleArray); i++) { struct SparseTupleNode* currentNode = tupleArray->get(i, tupleArray); if (compare(currentNode->data, data)) { struct SparseTupleNode* tupleNode = tupleArray->removeByIndex(i, tupleArray); sparseMatrix->elementAmount = tupleArray->size(tupleArray); return tupleNode->data; } } return NULL; } struct ClearArea* clearSparseMatrix(struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } struct ClearArea* result = malloc(sizeof(struct ClearArea)); void** resultArray = malloc(sizeof(void*) * sparseMatrix->elementAmount); if (result == NULL || resultArray == NULL) { printf("清空动态数组时,清空的数据没有足够的内存可以记录。\n"); return NULL; } struct ClearArea* clearArea = tupleArray->clear(tupleArray); struct SparseTupleNode** tupleNode = clearArea->array; for (int i = 0; i < sparseMatrix->elementAmount; i++) { resultArray[i] = tupleNode[i]->data; free(tupleNode[i]); tupleNode[i] = NULL; } result->array = resultArray; result->size = sparseMatrix->elementAmount; sparseMatrix->elementAmount = 0; return result; } struct ClearArea* destroySparseMatrix(struct SparseMatrix* sparseMatrix) { struct ArrayList* tupleArray = sparseMatrix->tupleArray; if (tupleArray == NULL) { printf("稀疏矩阵存储容器不存在!\n"); return NULL; } struct ClearArea* clearArea = clearSparseMatrix(sparseMatrix); tupleArray->destroy(tupleArray); free(sparseMatrix); sparseMatrix = NULL; return clearArea; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sparseMatrix.h" void print(void* data) { printf("%d", data); } int compare(void* firstData, void* secondData) { return firstData = secondData; } int main() { struct SparseMatrix* matrix = initSparseMatrix(5, 3); matrix->insert(0, 2, 99, matrix); matrix->insert(1, 1, 87, matrix); matrix->insert(3, 2, 15, matrix); matrix->foreachTuple(print, matrix); matrix->foreach(print, matrix); printf("获取数据:%d", matrix->get(0, 2, matrix)); matrix->removeByIndex(1, 1, matrix); matrix->removeByData(15, compare, matrix); matrix->foreach(print, matrix); struct ClearArea* clearArea = matrix->destroy(matrix); printf("销毁矩阵,移除数据:%d", clearArea->array[0]); return 0; } |
运行结果:
稀疏矩阵共3个非0元素! 0 2 99 1 1 87 3 2 15 稀疏矩阵共3个非0元素! NULL NULL 99 NULL 87 NULL NULL NULL NULL NULL NULL 15 NULL NULL NULL 获取数据:99稀疏矩阵共1个非0元素! NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 15 NULL NULL NULL 销毁矩阵,移除数据:15 |
【例4.4】试写一个算法,建立顺序存储稀疏矩阵的三元组表。
分析:假设A为一个稀疏矩阵,其数据存储在二维数组matrix中,sparseMatrix为一个存放对应于A矩阵生成的三元组表。在这个算法中,要进行二重循环来判断每个矩阵元素是否为零,若不为零,则将其行、列下标及其值存入sparseMatrix中。其算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sparseMatrix.h" void print(void* data) { printf("%d", data); } void setVlue(int matrix[4][5], struct SparseMatrix* sparseMatrix) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 5; j++) { if (matrix[i][j] != 0) { sparseMatrix->insert(i, j, matrix[i][j], sparseMatrix); } } } sparseMatrix->foreach(print, sparseMatrix); } int main() { int matrix[4][5] = { 0 }; matrix[0][1] = 99; matrix[1][4] = 15; matrix[3][4] = 66; struct SparseMatrix* sparseMatrix = initSparseMatrix(4, 5); setVlue(matrix, sparseMatrix); return 0; } |
运行结果:
稀疏矩阵共3个非0元素! NULL 99 NULL NULL NULL NULL NULL NULL NULL 15 NULL NULL NULL NULL NULL NULL NULL NULL NULL 66 |
【例4.5】试写一个算法,实现以三元组表结构存储的稀疏矩阵的转置运算。
分析:转置运算是一种常用的简单矩阵运算。对于一个m x n的矩阵M,它的转置矩阵T是一个n x m的矩阵,而且Mij=Tij (0<i<m,os<n),即M的行是T的列,M的列是T的行。如图4.4所示为稀疏矩阵M和相应的转置矩阵T。
稀疏矩阵的转置方法有多种,假设有两个矩阵---稀疏矩阵M与稀疏矩阵T。从上面的分析可知,要将M转置成T,就是将M的三元组表a.data转置为T的三元组表b->data。要想得到如图4.5所示的按行优先顺序存储的b->data,就必须重新排列三元组表的顺序。由于M的行是T的列,所以按a.data的列转置,所得到的转置矩阵T的三元组表b->data一定是按行优先顺序存放的。实现这种方法的基本思想是:对M中的每一列col (0<col<a.n-1)从头至尾依次扫描三元组表,找出所有列号等于col的那些三元组,并将它们的行号和列号互换后再依次存入b->data中,这样就可得到T的按行优先的三元组表。
其具体算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sparseMatrix.h" // spɑːs ˈtʌpəl struct SparseTupleNode { void* data; // 非0元素值 int row; // 行索引 int column; // 列索引 }; void print(void* data) { printf("%d", data); } void overturn(struct SparseMatrix* mMatrix, struct SparseMatrix* tMatrix) { struct ArrayList* tupleArray = mMatrix->tupleArray; for (int col = 0; col < mMatrix->columnAmount; col++) { for (int element = 0; element < mMatrix->elementAmount; element++) { struct SparseTupleNode* tupleNode = tupleArray->get(element, tupleArray); if (tupleNode->column == col) { tMatrix->insert(tupleNode->column, tupleNode->row, tupleNode->data, tMatrix); } } } printf("转置后:\n"); tMatrix->foreach(print, tMatrix); } int main() { struct SparseMatrix* mMatrix = initSparseMatrix(4, 5); mMatrix->insert(2, 2, 36, mMatrix); mMatrix->insert(3, 4, 66, mMatrix); mMatrix->insert(0, 1, 99, mMatrix); mMatrix->insert(0, 2, 38, mMatrix); mMatrix->insert(1, 2, 17, mMatrix); mMatrix->insert(1, 4, 15, mMatrix); printf("转置前:\n"); mMatrix->foreach(print, mMatrix); struct SparseMatrix* tMatrix = initSparseMatrix(5, 4); overturn(mMatrix, tMatrix); return 0; } |
运行结果:
转置前: 稀疏矩阵共6个非0元素! NULL 99 38 NULL NULL NULL NULL 17 NULL 15 NULL NULL 36 NULL NULL NULL NULL NULL NULL 66 转置后: 稀疏矩阵共6个非0元素! NULL NULL NULL NULL 99 NULL NULL NULL 38 17 36 NULL NULL NULL NULL NULL NULL 15 NULL 66 |
分析这个算法,它的主要运算是在col和element的两重循环中完成的,因此该算法的时间复杂度为O(col × element),即与稀疏矩阵M的列数和非零元素个数的乘积成正比。
我们知道,一般的矩阵转置算法的时间复杂度为O(m × n)。当稀疏矩阵的非零元素个数element和m × n同数量级时,上述算法的时间复杂度就是O(m × n)了,其算法虽然节省了存储空间,但时间复杂度提高了。因此,该算法仅适用于非零元素个数element远远小于矩阵元素个数m x n的情况。
因为我们对稀疏矩阵的结构做了封装,所以上述的算法无需两重循环,只需要借助于封装的稀疏矩阵结构即可自动完成稀疏矩阵转置。
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sparseMatrix.h" void print(void* data) { printf("%d", data); } void overturn(struct SparseMatrix* mMatrix, struct SparseMatrix* tMatrix) { struct ArrayList* tupleArray = mMatrix->tupleArray; for (int element = 0; element < mMatrix->elementAmount; element++) { struct SparseTupleNode* tupleNode = tupleArray->get(element, tupleArray); tMatrix->insert(tupleNode->column, tupleNode->row, tupleNode->data, tMatrix); } printf("转置后:\n"); tMatrix->foreach(print, tMatrix); } int main() { struct SparseMatrix* mMatrix = initSparseMatrix(4, 5); mMatrix->insert(2, 2, 36, mMatrix); mMatrix->insert(3, 4, 66, mMatrix); mMatrix->insert(0, 1, 99, mMatrix); mMatrix->insert(0, 2, 38, mMatrix); mMatrix->insert(1, 2, 17, mMatrix); mMatrix->insert(1, 4, 15, mMatrix); printf("转置前:\n"); mMatrix->foreach(print, mMatrix); struct SparseMatrix* tMatrix = initSparseMatrix(5, 4); overturn(mMatrix, tMatrix); return 0; } |
运行结果:
转置前: 稀疏矩阵共6个非0元素! NULL 99 38 NULL NULL NULL NULL 17 NULL 15 NULL NULL 36 NULL NULL NULL NULL NULL NULL 66 转置后: 稀疏矩阵共6个非0元素! NULL NULL NULL NULL 99 NULL NULL NULL 38 17 36 NULL NULL NULL NULL NULL NULL 15 NULL 66 |
2.带行表三元组表
为了便于随机存取任意一行的非零元素,可在按行优先存储的三元组表中,增加一个存储每一行的第一个非零元素在三元组表中位置的数组。这样就得到稀疏矩阵的另一种顺序存储结构——带行表的三元组表,又称为行逻辑链接的顺序表。其类型描述如下:
// spɑːs ˈmeɪtrɪks struct SparseMatrix { struct ArrayList* tupleArray; // 动态数组容器 int* rowTable; // 行表(记录当前行之前元素个数 第一行与最后一行不记录) int rowAmount; // 行 int columnAmount; // 列 int elementAmount; // 元素数量 }; |
在有了行表结构定义之后,由图4.5所示的三元组表a.data,就可以得到如表4.1所示的稀疏矩阵M的行表RowPos及其相关信息。
在这个表中,可以看到对于任一给定的行号i,都能迅速地确定该行的第一个非零元素在三元组表中的存储位置RowPos[i]。同时, RowPos[i]又表示第i行之前的所有行的非零元素总数,而且可以通过RowPos[i+1]-RowPos[i]计算出第i行上的非零元素个数。
编写头文件sparseMatrix.h。
#pragma once #ifndef SPARSE_MATRIX_H #define SPARSE_MATRIX_H #include "dynamicArray.h" // spɑːs ˈmeɪtrɪks struct SparseMatrix { struct ArrayList* tupleArray; // 动态数组容器 int* rowTable; // 行表(记录当前行之前元素个数 第一行与最后一行不记录) int rowAmount; // 行 int columnAmount; // 列 int elementAmount; // 元素数量 int (*insert)(int row, int column, void* data, struct SparseMatrix* sparseMatrix); void* (*get)(int row, int column, struct SparseMatrix* sparseMatrix); void* (*getByRow)(int row, int column, struct SparseMatrix* sparseMatrix); void (*foreach)(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix); void (*foreachTuple)(void (*printUpperTriangleMatrix)(void* data), struct SparseMatrix* sparseMatrix); void* (*removeByIndex)(int row, int column, struct SparseMatrix* sparseMatrix); void* (*removeByRow)(int row, int column, struct SparseMatrix* sparseMatrix); void* (*removeByData)(void* data, int (*compare)(void* firstData, void* secondData), struct SparseMatrix* sparseMatrix); struct ClearArea* (*clear)(struct SparseMatrix* sparseMatrix); struct ClearArea* (*destroy)(struct SparseMatrix* sparseMatrix); }; // spɑːs ˈtʌpəl struct SparseTupleNode { void* data; // 非0元素值 int row; // 行索引 int column; // 列索引 }; struct SparseMatrix* initSparseMatrix(int rowAmount, int columnAmount); #endif |
实现头文件定义sparseMatrix.c。
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "symmetricMatrix.h" struct MatrixArray* initMatrixArray(int matrixSize); int insertSymmetricMatrix(int row, int column, void* data, struct SymmetricMatrix* matrix); void* getSymmetricMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix); int capacitySymmetricMatrix(struct SymmetricMatrix* matrix); void foreachSymmetricMatrix(void (*printSymmetricMatrix)(void* data), struct SymmetricMatrix* matrix); void* removeMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix); void* removeMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct SymmetricMatrix* matrix); void** clearSymmetricMatrix(struct SymmetricMatrix* matrix); void** destroySymmetricMatrix(struct SymmetricMatrix* matrix); struct SymmetricMatrix* initSymmetricMatrix(int matrixSize) { struct SymmetricMatrix* symmetricMatrix = malloc(sizeof(struct SymmetricMatrix)); struct MatrixArray* matrix = initMatrixArray(matrixSize); if (symmetricMatrix == NULL || matrix == NULL) { printf("创建对称矩阵,分配内存空间失败!\n"); return NULL; } symmetricMatrix->matrixArray = matrix; symmetricMatrix->insert = insertSymmetricMatrix; symmetricMatrix->get = getSymmetricMatrixByIndex; symmetricMatrix->capacity = capacitySymmetricMatrix; symmetricMatrix->foreach = foreachSymmetricMatrix; symmetricMatrix->removeByIndex = removeMatrixByIndex; symmetricMatrix->removeByData = removeMatrixByData; symmetricMatrix->clear = clearSymmetricMatrix; symmetricMatrix->destroy = destroySymmetricMatrix; return symmetricMatrix; } struct MatrixArray { void** array; int capacity; }; int getMatrixArrayIndex(int row, int column) { if (row >= column) { return row * (row + 1) / 2 + column; } else { return column * (column + 1) / 2 + row; } } struct MatrixArray* initMatrixArray(int matrixSize) { struct MatrixArray* matrix = malloc(sizeof(struct MatrixArray)); if (matrix == NULL) { return NULL; } matrix->capacity = getMatrixArrayIndex(matrixSize - 1, matrixSize - 1) + 1; matrix->array = calloc(matrix->capacity, sizeof(void*)); if (matrix == NULL) { return NULL; } return matrix; } int insertSymmetricMatrix(int row, int column, void* data, struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return 0; } if (row < 0 || column < 0) { printf("插入对称矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } if (data == NULL) { printf("插入对称矩阵数据不存在!"); return 0; } int index = getMatrixArrayIndex(row, column); if (index >= matrixArray->capacity) { printf("对称矩阵行列越界异常!"); return 0; } matrixArray->array[index] = data; return 1; } void* getSymmetricMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } if (row < 0 || column < 0) { printf("获取对称矩阵数据坐标不合法,坐标轴不能小于0!"); return 0; } int index = getMatrixArrayIndex(row, column); if (index >= matrixArray->capacity) { printf("对称矩阵行列越界异常!"); return 0; } void* result = matrixArray->array[index]; return matrixArray->array[index]; } int capacitySymmetricMatrix(struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return -1; } return matrixArray->capacity; } void foreachSymmetricMatrix(void (*printSymmetricMatrix)(void* data), struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return; } int weight = 1; int pedometer = weight; printf("方阵对称矩阵:\n"); for (int i = 0; i < matrixArray->capacity; i++) { printSymmetricMatrix(matrixArray->array[i]); printf("\t"); if ((i + 1) % pedometer == 0) { printf("\n"); pedometer = pedometer + ++weight; } } } void* removeMatrixByIndex(int row, int column, struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } int index = getMatrixArrayIndex(row, column); if (index >= matrixArray->capacity) { printf("对称矩阵行列越界异常!"); return 0; } void* result = matrixArray->array[index]; matrixArray->array[index] = NULL; return result; } void* removeMatrixByData(void* data, int (*compare)(void* firstData, void* secondData), struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } for (int i = 0; i < matrixArray->capacity; i++) { if (compare(data, matrixArray->array[i])) { void* result = matrixArray->array[i]; matrixArray->array[i] = NULL; return result; } } return NULL; } void** clearSymmetricMatrix(struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } void** result = calloc(matrixArray->capacity, sizeof(void*)); memcpy(result, matrixArray->array, sizeof(void*) * matrixArray->capacity); memset(matrixArray->array, NULL, sizeof(void*) * matrixArray->capacity); return result; } void** destroySymmetricMatrix(struct SymmetricMatrix* matrix) { struct MatrixArray* matrixArray = matrix->matrixArray; if (matrixArray == NULL) { printf("对称矩阵内存空间不存在!"); return NULL; } void** result = clearSymmetricMatrix(matrix); free(matrixArray); matrixArray = NULL; free(matrix); matrix = NULL; return result; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sparseMatrix.h" void print(void* data) { printf("%d", data); } int compare(void* firstData, void* secondData) { return firstData = secondData; } int main() { struct SparseMatrix* sparseMatrix = initSparseMatrix(4, 5); sparseMatrix->insert(2, 2, 36, sparseMatrix); sparseMatrix->insert(3, 4, 66, sparseMatrix); sparseMatrix->insert(0, 1, 99, sparseMatrix); sparseMatrix->insert(0, 2, 38, sparseMatrix); sparseMatrix->insert(1, 2, 17, sparseMatrix); sparseMatrix->insert(1, 4, 15, sparseMatrix); sparseMatrix->foreach(print, sparseMatrix); printf("第0行之前有:%d个\n", sparseMatrix->rowTable[0]); printf("第1行之前有:%d个\n", sparseMatrix->rowTable[1]); printf("第2行之前有:%d个\n", sparseMatrix->rowTable[2]); printf("第3行之前有:%d个\n", sparseMatrix->rowTable[3]); printf("按行获取:%d\n", sparseMatrix->getByRow(2, 2, sparseMatrix)); printf("按行获取:%d\n", sparseMatrix->getByRow(2, 0, sparseMatrix)); printf("按行获取:%d\n", sparseMatrix->getByRow(1, 4, sparseMatrix)); sparseMatrix->removeByIndex(0,2, sparseMatrix); printf("根据索引删除38\n"); printf("第0行之前有:%d个\n", sparseMatrix->rowTable[0]); printf("第1行之前有:%d个\n", sparseMatrix->rowTable[1]); printf("第2行之前有:%d个\n", sparseMatrix->rowTable[2]); printf("第3行之前有:%d个\n", sparseMatrix->rowTable[3]); sparseMatrix->foreach(print, sparseMatrix); sparseMatrix->removeByRow(2, 2, sparseMatrix); printf("借助行表删除36\n"); printf("第0行之前有:%d个\n", sparseMatrix->rowTable[0]); printf("第1行之前有:%d个\n", sparseMatrix->rowTable[1]); printf("第2行之前有:%d个\n", sparseMatrix->rowTable[2]); printf("第3行之前有:%d个\n", sparseMatrix->rowTable[3]); sparseMatrix->foreach(print, sparseMatrix); sparseMatrix->removeByData(99, compare, sparseMatrix); printf("删除99\n"); printf("第0行之前有:%d个\n", sparseMatrix->rowTable[0]); printf("第1行之前有:%d个\n", sparseMatrix->rowTable[1]); printf("第2行之前有:%d个\n", sparseMatrix->rowTable[2]); printf("第3行之前有:%d个\n", sparseMatrix->rowTable[3]); sparseMatrix->foreach(print, sparseMatrix); return 0; } |
运行结果:
稀疏矩阵共6个非0元素! NULL 99 38 NULL NULL NULL NULL 17 NULL 15 NULL NULL 36 NULL NULL NULL NULL NULL NULL 66 第0行之前有:0个 第1行之前有:2个 第2行之前有:4个 第3行之前有:5个 按行获取:36 按行获取:0 按行获取:15 根据索引删除38 第0行之前有:0个 第1行之前有:1个 第2行之前有:3个 第3行之前有:4个 稀疏矩阵共5个非0元素! NULL 99 NULL NULL NULL NULL NULL 17 NULL 15 NULL NULL 36 NULL NULL NULL NULL NULL NULL 66 借助行表删除36 第0行之前有:0个 第1行之前有:1个 第2行之前有:3个 第3行之前有:3个 稀疏矩阵共4个非0元素! NULL 99 NULL NULL NULL NULL NULL 17 NULL 15 NULL NULL NULL NULL NULL NULL NULL NULL NULL 66 删除99 第0行之前有:0个 第1行之前有:0个 第2行之前有:2个 第3行之前有:2个 稀疏矩阵共3个非0元素! NULL NULL NULL NULL NULL NULL NULL 17 NULL 15 NULL NULL NULL NULL NULL NULL NULL NULL NULL 66 |
以上介绍的是稀疏矩阵的顺序压缩存储方式,相应的算法及其运算都比较简单,但是当矩阵的非零元素个数和位置在操作运算过程中变化较大时,就不宜采用顺序存储来表示三元组的线性表。例如,在将一个矩阵加到另一个矩阵中的操作时,由于非零元素的插入或删除将会引起三元组表中不少元素的移动。为此,对这种类型的矩阵,采用链式存储结构表示三元组的线性表更为合适。由于这种链式存储结构比较复杂,而且不是很常用,因此在这里不作介绍,如果读者感兴趣,可参看相关书籍。
4.3.广义表基础
广义表是线性表的推广,又称列表。线性表的元素仅限于原子项,即每个数据元素只能是一个数或一个记录,如果放松对线性表元素的这种限制,允许它们自身具有结构,由此就产生了广义表的概念。
4.3.1.定义
广义表是n(n≥0)个元素a1,a2,...,an的有限序列,其中ai或者是原子项、或者是一个广义表,通常记作LS=(a1, a2, ..., an) LS是广义表的名字,n为它的长度。若ai又是广义表,则称它为LS的子表。为了区分原子和广义表,在书写时习惯上用大写字母表示广义表,用小写字母表示原子。通常用圆括号将广义表括起来,用逗号分隔其中的元素。当广义表LS非空时,称第一个元素a1是LS的表头(head),其余元素组成的表(a2, ..., an)称为LS的表尾(tail)。
显然,广义表的定义是一个递归定义,因为在描述广义表时又用到了广义表的概念。下面列举一些广义表的例子:
- A=()——A是一个空表,其长度为零。
- B=(a)一一B是一个只有一个原子的广义表,其长度为1。
- C=(a, (b, c))——C是一个长度为2的广义表,第一个元素是原子,第二个元素是子表。
- D=(A, B, C) = ((), (a), (a, (b, c)))——D是一个长度为3的广义表,其中三个元素均为子表。
- E=(C, d) =((a, (b, c)), d)——E是一个长度为2的广义表,第一个元素是子表,第二个元素是原子。
- F=(e, F) = (e, (e, (e, )——F是一个递归的表,它的长度为2,第一个元素是原子,第二个是表自身,展开后它是一个无限的广义表。
一个表展开后所含括号的层数称为广义表的深度。例如,表A、B、C、D、E的深度分别为1、1、2、3、3,而表F的深度为无穷大∞。
从广义表的定义和上述例子,可以得出广义表的几个重要性质:
1.广义表的元素可以是子表,而子表又可以含有子表,因此广义表是一个多层次结构的表,它可以用图来形象地表示。例如,图4.6表示的分别是广义表A、B、C、D、E的层次结构。
2.广义表具有递归和共享的性质,例如,图4.7表F就是一个递归的广义表, 图4.7表D是共享的表,在表D中可以不必列出子表的值,而是通过子表的名字来引用。
4.3.2.基本运算
广义表是一种多层次的线性结构,由图4.6可以看出,它像一棵倒画的树。实际上,这就是一种树形结构(在后面章节中介绍)。由此可知,广义表不仅是线性表的推广,也是树结构的推广。因此,广义表的运算也与这些数据结构上的运算类似。由于广义表的结构相对来说比较复杂,其各种运算的实现也不像线性表那样简单,因此我们先通过一些简单的例子来了解广义表的基本运算。
【例4.6】取表头head (LS)、取表尾tail (LS)运算。由表头和表尾的定义可知,任何一个非空的广义表其表头可能是原子,也可能是子表,而其表尾一定是子表。以上一节中所给的广义表为例,给出下列运算的结果:
head (B)=a, tail (B)=(),
head (C) =a, tail (C) =( (a,b) ), tail (tail (C) )=()
head (D)=A, tail (D)=(B,C) , tail (tail (D))=(C)=( (a, (b,c)) )
head (E)=C, head (head (E) ) =a, tail (E) = (d)
【例4.7】 已知有下列的广义表,试求出下面广义表的表头head()、表尾tail()、表长length()和深度depth()。
(1) A=(a, (b,c, d) ,e, (f,g)); (2) B=( (a));
(3) C=(y,(z,w), (x, (z,w) ,a) ) ; (4) D=(x, ((y) , B), D)
解答:
(1) head (A) =a, tail (A) =((b,c,d),e,(f,g) ), length (A) =4,depth (A) =2;
(2) head (B) = (a),tail (B) =(), length (B) =1, depth (B)=2;
(3) head (C) =y, tail(C)=((z,w),(x,(z,w),a)),Length (C) =3, depth (C)=3;
(4) head (D)=x,tail (D) =(((y),B),D),ength (D) =3,depth (D)=∞。
要特别注意的是,广义表()和(())是不同的,前者为空表,长度为0;而后者是由空表作元素的广义表,其长度为1,它可以分解得到表头、表尾为空表()。
4.3.3.存储结构
由于广义表中的元素本身又可以具有结构,是一种带有层次的非线性结构,因此难以用顺序存储结构表示,通常采用链式存储结构,每个元素可用一个结点表示,结点结构如下所示。
每个结点由三个域构成,其中tag是一个标志位,用来区分当前结点是原子还是子集表,当tag为零值时,该结点是子表,第二个域为slink,用以存放子表的地址;当tag为1时,该结点是原子结点,第二个域为data,用以存放元素值。link域是用来存放与本元素同一层的下一个元素对应结点的地址,当该元素是所在层的最后一个元素时, link的值为NULL,其广义表及结点类型描述如下:
struct GLNode { enum { childLink, atom } tag; // childLink=0表示子表 atom=1表示原子 union { char* data; // 原子数据 struct GLNode* childLink; // 子表指针 }; struct GLNode* nextlink; }; struct GeneralizedList { char* name; // 广义表名称 struct GLNode* header; // 广义表头指针 }; |
上一节中给出的六个广义表A~F所对应的存储结构如图4.7所示。
4.3.4.基本运算
在上述的链式存储结构上如何实现广义表的各种操作,是本节的主要讨论内容。
1.数据结构creat
通过用户输入的广义表表达式建立相应的广义表,并且边输入边建立。
基本思想:
• 在广义表表达式中,遇到左括号"("时递归构造子表,否则构造原子结点;
• 遇到逗号时递归构造后续广义表,遇到")"后续广义表指针置空,结束当前递归。
因此,实现算法的C语言函数定义如下:
void createGLNode(struct GLNode** gLNode, int isHeader) { char ch; scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } if (isHeader && ch == '(') { isHeader = 0; createGLNode(gLNode, isHeader); return; } *gLNode = malloc(sizeof(struct GLNode)); if (*gLNode == NULL) { printf("广义表结点创建失败,内存空间不足!\n"); return; } if (ch == '(') { (*gLNode)->tag = childLink; createGLNode(&(*gLNode)->childLink, isHeader); scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } } else { struct ArrayList* list = initArrayList(5); char* chr; while (ch != ',' && ch != ')') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } (*gLNode)->tag = atom; (*gLNode)->data = list->toString(list); list->ruin(list); } if (ch == ',') { createGLNode(&(*gLNode)->nextlink, isHeader); } else if (ch == ')') { (*gLNode)->nextlink = NULL; } return; } struct GeneralizedList* createGList() { struct GeneralizedList* gList = malloc(sizeof(struct GeneralizedList)); if (gList == NULL) { printf("广义表创建失败,内存空间不足!\n"); return NULL; } char ch; scanf("%c", &ch); struct ArrayList* list = initArrayList(5); char* chr; while (ch != '=') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return NULL; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } gList->name = list->toString(list); list->ruin(list); createGLNode(&gList->header, 1); return gList; } |
2.遍历结构foreach
输出广义表所采用的算法思想:先打印输出一个"("号,如果广义表为空,则输出一个空格符,否则递归调用输出广义表,广义表打印输出完后,再打印一个")”号;若遇到tag=atom的结点,则直接输出其数据域的值。若还有后续元素,则递归调用打印后续每个元素,直到遇到nextLink域为NULL。因此,算法可用C语言函数实现如下:
void foreachGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { return; } if (gLNode->tag == atom) { printf("%s", gLNode->data); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } else { printf("("); foreachGLNode(gLNode->childLink); printf(")"); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } } void foreachGList(struct GeneralizedList* gList) { printf("%s=(", gList->name); foreachGLNode(gList->header); printf(")\n"); } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "dynamicArray.h" struct GLNode { enum { childLink, atom } tag; // childLink=0表示子表 atom=1表示原子 union { char* data; // 原子数据 struct GLNode* childLink; // 子表指针 }; struct GLNode* nextlink; }; struct GeneralizedList { char* name; // 广义表名称 struct GLNode* header; // 广义表头指针 }; void createGLNode(struct GLNode** gLNode, int isHeader) { char ch; scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } if (isHeader && ch == '(') { isHeader = 0; createGLNode(gLNode, isHeader); return; } *gLNode = malloc(sizeof(struct GLNode)); if (*gLNode == NULL) { printf("广义表结点创建失败,内存空间不足!\n"); return; } if (ch == '(') { (*gLNode)->tag = childLink; createGLNode(&(*gLNode)->childLink, isHeader); scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } } else { struct ArrayList* list = initArrayList(5); char* chr; while (ch != ',' && ch != ')') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } (*gLNode)->tag = atom; (*gLNode)->data = list->toString(list); list->ruin(list); } if (ch == ',') { createGLNode(&(*gLNode)->nextlink, isHeader); } else if (ch == ')') { (*gLNode)->nextlink = NULL; } return; } struct GeneralizedList* createGList() { struct GeneralizedList* gList = malloc(sizeof(struct GeneralizedList)); if (gList == NULL) { printf("广义表创建失败,内存空间不足!\n"); return NULL; } char ch; scanf("%c", &ch); struct ArrayList* list = initArrayList(5); char* chr; while (ch != '=') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return NULL; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } gList->name = list->toString(list); list->ruin(list); createGLNode(&gList->header, 1); return gList; } void foreachGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { return; } if (gLNode->tag == atom) { printf("%s", gLNode->data); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } else { printf("("); foreachGLNode(gLNode->childLink); printf(")"); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } } void foreachGList(struct GeneralizedList* gList) { printf("%s=(", gList->name); foreachGLNode(gList->header); printf(")\n"); } int main() { printf("创建广义表:\n"); struct GeneralizedList* gList = createGList(); printf("展示广义表:\n"); foreachGList(gList); return 0; } |
运行结果:
创建广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 展示广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) |
3.查找数据get
在给定的广义表中查找数据域为data的结点,采用的算法思想是:若遇到tag=0的原子结点,如果是要找的结点,则查找成功;否则,若还有后续元素,则递归调用本过程查找后续元素,直到遇到nextLink域为NULL的元素。若遇到tag=1的结点,则递归调用本过程在该子表中查找,若还有后续元素,则递归调用本过程查找后续每个元素,直到遇到nextLink域为NULL的元素。
具体实现C函数如下:
struct GLNode* getGLNode(struct GLNode* gLNode, char* data) { if (gLNode->tag == atom) { if (strcmp(gLNode->data, data) == 0) { struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取结点数据,内存空间不足!\n"); return NULL; } node->tag = atom; node->data = gLNode->data; node->nextlink = NULL; return node; } if (gLNode->nextlink == NULL) return NULL; } else { struct GLNode* node = getGLNode(gLNode->childLink, data); if (node != NULL) { return node; } if (gLNode->nextlink == NULL) return NULL; } return getGLNode(gLNode->nextlink, data); } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "dynamicArray.h" struct GLNode { enum { childLink, atom } tag; // childLink=0表示子表 atom=1表示原子 union { char* data; // 原子数据 struct GLNode* childLink; // 子表指针 }; struct GLNode* nextlink; }; struct GeneralizedList { char* name; // 广义表名称 struct GLNode* header; // 广义表头指针 }; void createGLNode(struct GLNode** gLNode, int isHeader) { char ch; scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } if (isHeader && ch == '(') { isHeader = 0; createGLNode(gLNode, isHeader); return; } *gLNode = malloc(sizeof(struct GLNode)); if (*gLNode == NULL) { printf("广义表结点创建失败,内存空间不足!\n"); return; } if (ch == '(') { (*gLNode)->tag = childLink; createGLNode(&(*gLNode)->childLink, isHeader); scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } } else { struct ArrayList* list = initArrayList(5); char* chr; while (ch != ',' && ch != ')') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } (*gLNode)->tag = atom; (*gLNode)->data = list->toString(list); list->ruin(list); } if (ch == ',') { createGLNode(&(*gLNode)->nextlink, isHeader); } else if (ch == ')') { (*gLNode)->nextlink = NULL; } return; } struct GeneralizedList* createGList() { struct GeneralizedList* gList = malloc(sizeof(struct GeneralizedList)); if (gList == NULL) { printf("广义表创建失败,内存空间不足!\n"); return NULL; } char ch; scanf("%c", &ch); struct ArrayList* list = initArrayList(5); char* chr; while (ch != '=') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return NULL; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } gList->name = list->toString(list); list->ruin(list); createGLNode(&gList->header, 1); return gList; } void foreachGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { return; } if (gLNode->tag == atom) { printf("%s", gLNode->data); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } else { printf("("); foreachGLNode(gLNode->childLink); printf(")"); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } } void foreachGList(struct GeneralizedList* gList) { printf("%s=(", gList->name); foreachGLNode(gList->header); printf(")\n"); } void printfGLNode(struct GLNode* gLNode) { printf("结点:("); foreachGLNode(gLNode); printf(")\n"); } struct GLNode* getGLNode(struct GLNode* gLNode, char* data) { if (gLNode->tag == atom) { if (strcmp(gLNode->data, data) == 0) { struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取结点数据,内存空间不足!\n"); return NULL; } node->tag = atom; node->data = gLNode->data; node->nextlink = NULL; return node; } if (gLNode->nextlink == NULL) return NULL; } else { struct GLNode* node = getGLNode(gLNode->childLink, data); if (node != NULL) { return node; } if (gLNode->nextlink == NULL) return NULL; } return getGLNode(gLNode->nextlink, data); } int main() { printf("创建广义表:\n"); struct GeneralizedList* gList = createGList(); printf("展示广义表:\n"); foreachGList(gList); struct GLNode* gLNode = getGLNode(gList->header, "44"); printfGLNode(gLNode); return 0; } |
运算结果:
创建广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 展示广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 结点:(44,5) |
4.表头header
一个广义表的表头指的是该广义表的第一个元素,因此求表头操作比较简单,实现算法描述如下:
struct GLNode* headerGList(struct GeneralizedList* gList) { if (gList->header->tag == atom && (strcmp(gList->header->data, "") == 0)) { printf("广义表为空,没有头节点!\n"); return NULL; } struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取广义表头,内存空间不足!\n"); return NULL; } if (gList->header->tag == atom) { node->tag = atom; node->data = gList->header->data; node->nextlink = NULL; } else { node->tag = childLink; node->childLink = gList->header->childLink; node->nextlink = NULL; } return node; } |
测试
int main() { printf("创建广义表:\n"); struct GeneralizedList* gList = createGList(); struct GLNode* header = headerGList(gList); if (header != NULL) { printf("表头->"); printfGLNode(header); } return 0; } |
运行结果:
创建广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 表头->结点:(3) |
5.表尾tail
一个广义表的表尾指的是除去该广义表的第一个元素后的所有剩余部分,空广义表没有表头与表尾的概念。由此,实现上述算法描述如下:
struct GLNode* tailGList(struct GeneralizedList* gList) { if (gList->header->tag == atom && (strcmp(gList->header->data, "") == 0)) { printf("广义表为空,没有尾节点!\n"); return NULL; } return gList->header->nextlink; } |
测试
int main() { printf("创建广义表:\n"); struct GeneralizedList* gList = createGList(); struct GLNode* tail = tailGList(gList); if (tail != NULL) { printf("表尾->"); printfGLNode(tail); } return 0; } |
运行结果:
创建广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 表尾->结点:(56,(6,7,87),23,(),(45,(5,6,(44,5))),77) |
6.深度depth
假设广义表是一个无共享子表的非递归表,求其表深度的算法思想是:扫描广义表的第一层的每个结点,对每个结点递归调用计算出其子表的深度,取最大的子表深度,然后加1即为广义表的最大深度。其递归模型如下:
maxdh (GL) =0 GL为单个元素即GL->tag==0
maxdh (GL) =1 GL为空表即GL->tag==1且GL->slink==NULL
maxdh (GL) =max (maxdh (GL1) , maxdh (GL2),..., maxdh (GLn) ) +1
其中,GL=(GL1, GL2, ..., GLn)。
因此,实现该功能算法描述如下:
void depthGLNode(struct GLNode* gLNode,int counter, int* depth) { if (gLNode == NULL) { if (*depth < counter) { *depth = counter; } return; } if (gLNode->tag == childLink && !(gLNode->childLink->tag == atom && (strcmp(gLNode->childLink->data, "") == 0))) { counter++; depthGLNode(gLNode->childLink, counter, depth); counter = 1; } depthGLNode(gLNode->nextlink, counter, depth); } int depthGList(struct GeneralizedList* gList) { int depth = 1; depthGLNode(gList->header, 1, &depth); if (gList->header->tag == atom && (strcmp(gList->header->data, "") == 0)) { return 0; } return depth; } |
7.测验
测试代码:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "dynamicArray.h" struct GLNode { enum { childLink, atom } tag; // childLink=0表示子表 atom=1表示原子 union { char* data; // 原子数据 struct GLNode* childLink; // 子表指针 }; struct GLNode* nextlink; }; struct GeneralizedList { char* name; // 广义表名称 struct GLNode* header; // 广义表头指针 }; void createGLNode(struct GLNode** gLNode, int isHeader) { char ch; scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } if (isHeader && ch == '(') { isHeader = 0; createGLNode(gLNode, isHeader); return; } *gLNode = malloc(sizeof(struct GLNode)); if (*gLNode == NULL) { printf("广义表结点创建失败,内存空间不足!\n"); return; } if (ch == '(') { (*gLNode)->tag = childLink; createGLNode(&(*gLNode)->childLink, isHeader); scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } } else { struct ArrayList* list = initArrayList(5); char* chr; while (ch != ',' && ch != ')') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } (*gLNode)->tag = atom; (*gLNode)->data = list->toString(list); list->ruin(list); } if (ch == ',') { createGLNode(&(*gLNode)->nextlink, isHeader); } else if (ch == ')') { (*gLNode)->nextlink = NULL; } return; } struct GeneralizedList* createGList() { struct GeneralizedList* gList = malloc(sizeof(struct GeneralizedList)); if (gList == NULL) { printf("广义表创建失败,内存空间不足!\n"); return NULL; } char ch; scanf("%c", &ch); struct ArrayList* list = initArrayList(5); char* chr; while (ch != '=') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return NULL; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } gList->name = list->toString(list); list->ruin(list); createGLNode(&gList->header, 1); return gList; } void foreachGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { return; } if (gLNode->tag == atom) { printf("%s", gLNode->data); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } else { printf("("); foreachGLNode(gLNode->childLink); printf(")"); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } } void foreachGList(struct GeneralizedList* gList) { printf("%s=(", gList->name); foreachGLNode(gList->header); printf(")\n"); } void printfGLNode(struct GLNode* gLNode) { printf("结点:("); foreachGLNode(gLNode); printf(")\n"); } struct GLNode* getGLNode(struct GLNode* gLNode, char* data) { if (gLNode->tag == atom) { if (strcmp(gLNode->data, data) == 0) { struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取结点数据,内存空间不足!\n"); return NULL; } node->tag = atom; node->data = gLNode->data; node->nextlink = NULL; return node; } if (gLNode->nextlink == NULL) return NULL; } else { struct GLNode* node = getGLNode(gLNode->childLink, data); if (node != NULL) { return node; } if (gLNode->nextlink == NULL) return NULL; } return getGLNode(gLNode->nextlink, data); } struct GLNode* headerGList(struct GeneralizedList* gList) { if (gList->header->tag == atom && (strcmp(gList->header->data, "") == 0)) { printf("广义表为空,没有头节点!\n"); return NULL; } struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取广义表头,内存空间不足!\n"); return NULL; } if (gList->header->tag == atom) { node->tag = atom; node->data = gList->header->data; node->nextlink = NULL; } else { node->tag = childLink; node->childLink = gList->header->childLink; node->nextlink = NULL; } return node; } struct GLNode* tailGList(struct GeneralizedList* gList) { if (gList->header->tag == atom && (strcmp(gList->header->data, "") == 0)) { printf("广义表为空,没有尾节点!\n"); return NULL; } return gList->header->nextlink; } void depthGLNode(struct GLNode* gLNode,int counter, int* depth) { if (gLNode == NULL) { if (*depth < counter) { *depth = counter; } return; } if (gLNode->tag == childLink && !(gLNode->childLink->tag == atom && (strcmp(gLNode->childLink->data, "") == 0))) { counter++; depthGLNode(gLNode->childLink, counter, depth); counter = 1; } depthGLNode(gLNode->nextlink, counter, depth); } int depthGList(struct GeneralizedList* gList) { int depth = 1; depthGLNode(gList->header, 1, &depth); if (gList->header->tag == atom && (strcmp(gList->header->data, "") == 0)) { return 0; } return depth; } int main() { printf("创建广义表:\n"); struct GeneralizedList* gList = createGList(); printf("展示广义表:\n"); foreachGList(gList); struct GLNode* gLNode = getGLNode(gList->header, "44"); printfGLNode(gLNode); struct GLNode* header = headerGList(gList); if (header != NULL) { printf("表头->"); printfGLNode(header); } struct GLNode* tail = tailGList(gList); if (tail != NULL) { printf("表尾->"); printfGLNode(tail); } printf("深度:%d", depthGList(gList)); return 0; } |
运行结果:
创建广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 展示广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 结点:(44) 表头->结点:(3) 表尾->结点:(56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 深度:4 |
4.3.5.重构广义表
创建头文件generalizedList.h:
#pragma once #ifndef GENERALIZED_LIST_H #define GENERALIZED_LIST_H struct GeneralizedList { char* name; // 广义表名称 struct GLNode* headerNode; // 广义表头指针 void (*foreach)(struct GeneralizedList* gList); void (*printfGLNode)(struct GLNode* gLNode); void (*printfGList)(struct GLNode* gLNode); struct GLNode* (*get)(char* data, struct GeneralizedList* gList); struct GeneralizedList* (*header)(struct GeneralizedList* gList); struct GeneralizedList* (*tail)(struct GeneralizedList* gList); int (*depth)(struct GeneralizedList* gList); void (*ruin)(struct GeneralizedList* gList); }; struct GLNode { enum { childLink, atom } tag; // childLink=0表示子表 atom=1表示原子 union { char* data; // 原子数据 struct GLNode* childLink; // 子表指针 }; struct GLNode* nextlink; }; struct GeneralizedList* initGeneralizedList(); struct GeneralizedList* createGListByGLNode(struct GLNode* header); #endif |
实现头文件逻辑generalizedList.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "dynamicArray.h" #include "generalizedList.h" #define PRINT 1 struct GeneralizedList* createGList(); void foreachGList(struct GeneralizedList* gList); void printfGLNode(struct GLNode* gLNode); void printfGList(struct GLNode* gLNode); struct GLNode* getGLNodeByList(char* data, struct GeneralizedList* gList); struct GeneralizedList* headerGList(struct GeneralizedList* gList); struct GeneralizedList* tailGList(struct GeneralizedList* gList); int depthGList(struct GeneralizedList* gList); void ruinGList(struct GeneralizedList* gList); void setGListFunction(struct GeneralizedList* gList) { gList->foreach = foreachGList; gList->printfGLNode = printfGLNode; gList->printfGList = printfGList; gList->get = getGLNodeByList; gList->header = headerGList; gList->tail = tailGList; gList->depth = depthGList; gList->ruin = ruinGList; } struct GeneralizedList* initGeneralizedList() { struct GeneralizedList* gList = createGList(); setGListFunction(gList); return gList; } struct GeneralizedList* createGListByGLNode(struct GLNode* header) { struct GeneralizedList* gList = malloc(sizeof(struct GeneralizedList)); if (gList == NULL) { printf("广义表创建失败,内存空间不足!\n"); return NULL; } gList->headerNode = header; setGListFunction(gList); return gList; } void createGLNode(struct GLNode** gLNode, int isHeader) { char ch; scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } if (isHeader && ch == '(') { isHeader = 0; createGLNode(gLNode, isHeader); return; } *gLNode = malloc(sizeof(struct GLNode)); if (*gLNode == NULL) { printf("广义表结点创建失败,内存空间不足!\n"); return; } if (ch == '(') { (*gLNode)->tag = childLink; createGLNode(&(*gLNode)->childLink, isHeader); scanf("%c", &ch); while (ch == ' ') { scanf("%c", &ch); } } else { struct ArrayList* list = initArrayList(5); char* chr; while (ch != ',' && ch != ')') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } (*gLNode)->tag = atom; (*gLNode)->data = list->toString(list); (*gLNode)->nextlink = NULL; list->ruin(list); } if (ch == ',') { createGLNode(&(*gLNode)->nextlink, isHeader); } else if (ch == ')') { (*gLNode)->nextlink = NULL; } return; } struct GeneralizedList* createGList() { struct GeneralizedList* gList = malloc(sizeof(struct GeneralizedList)); if (gList == NULL) { printf("广义表创建失败,内存空间不足!\n"); return NULL; } char ch; scanf("%c", &ch); while (ch == '\n') { scanf("%c", &ch); } struct ArrayList* list = initArrayList(5); char* chr; while (ch != '=') { chr = malloc(sizeof(char)); if (chr == NULL) { printf("广义表结点创建失败,记录结点值的内存空间不足!\n"); return NULL; } *chr = ch; list->insert(list->size(list), chr, list); scanf("%c", &ch); } gList->name = list->toString(list); list->ruin(list); createGLNode(&gList->headerNode, 1); return gList; } void foreachGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { return; } if (gLNode->tag == atom) { printf("%s", gLNode->data); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } else { printf("("); foreachGLNode(gLNode->childLink); printf(")"); if (gLNode->nextlink == NULL) return; printf(","); foreachGLNode(gLNode->nextlink); } } void foreachGList(struct GeneralizedList* gList) { if (gList == NULL) { printf("广义表不存在!\n"); return; } printf("%s=(", gList->name); foreachGLNode(gList->headerNode); printf(")\n"); } void printfGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { printf("广义表结点不存在!\n"); return; } struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("打印结点数据,内存空间不足!\n"); return NULL; } node->tag = gLNode->tag; node->data = gLNode->data; node->childLink = gLNode->childLink; node->nextlink = NULL; printf("结点:("); foreachGLNode(node); printf(")\n"); free(node); node = NULL; } void printfGList(struct GLNode* gLNode) { if (gLNode == NULL) { #ifdef PRINT printf("森林广义表结点不存在!\n"); #endif return; } printf("结点:("); foreachGLNode(gLNode); printf(")\n"); } struct GLNode* getGLNode(char* data, struct GLNode* gLNode) { if (gLNode == NULL) { printf("广义表结点不存在!\n"); return NULL; } if (data == NULL) { printf("获取结点输入的值为空!\n"); return NULL; } if (gLNode->tag == atom) { if (strcmp(gLNode->data, data) == 0) { struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取结点数据,内存空间不足!\n"); return NULL; } node->tag = gLNode->tag; node->data = gLNode->data; node->nextlink = NULL; return node; } if (gLNode->nextlink == NULL) return NULL; } else { struct GLNode* node = getGLNode(data, gLNode->childLink); if (node != NULL) { return node; } if (gLNode->nextlink == NULL) return NULL; } return getGLNode(data, gLNode->nextlink); } struct GLNode* getGLNodeByList(char* data, struct GeneralizedList* gList) { return getGLNode(data, gList->headerNode); } struct GLNode* saveNewGListHeader(struct GeneralizedList* gList) { struct GLNode* node = malloc(sizeof(struct GLNode)); if (node == NULL) { printf("获取广义表头,内存空间不足!\n"); return NULL; } if (gList->headerNode->tag == atom) { node->tag = atom; node->data = gList->headerNode->data; node->nextlink = NULL; } else { node->tag = childLink; node->childLink = gList->headerNode->childLink; node->nextlink = NULL; } return node; } struct GeneralizedList* headerGList(struct GeneralizedList* gList) { if (gList == NULL || gList->headerNode == NULL) { printf("广义表不存在!\n"); return NULL; } if (gList->headerNode->tag == atom && (strcmp(gList->headerNode->data, "") == 0)) { printf("广义表为空,没有头节点!\n"); return NULL; } return createGListByGLNode(saveNewGListHeader(gList)); } struct GLNode* saveNewGListTail(struct GeneralizedList* gList) { if (gList->headerNode->nextlink != NULL) { return gList->headerNode->nextlink; } char* data = malloc(sizeof(char)); struct GLNode* tail = malloc(sizeof(struct GLNode)); if (tail == NULL || data == NULL) { #ifdef PRINT printf("获取森林广义表头,内存空间不足!\n"); #endif return NULL; } tail->tag = atom; data[0] = '\0'; tail->data = data; tail->nextlink = NULL; return tail; } struct GeneralizedList* tailGList(struct GeneralizedList* gList) { if (gList == NULL || gList->headerNode == NULL) { printf("广义表不存在!\n"); return NULL; } if (gList->headerNode->tag == atom && (strcmp(gList->headerNode->data, "") == 0)) { printf("广义表为空,没有尾节点!\n"); return NULL; } return createGListByGLNode(saveNewGListTail(gList)); } void depthGLNode(struct GLNode* gLNode, int counter, int* depth) { if (gLNode == NULL) { if (*depth < counter) { *depth = counter; } return; } if (gLNode->tag == childLink && !(gLNode->childLink->tag == atom && (strcmp(gLNode->childLink->data, "") == 0))) { counter++; depthGLNode(gLNode->childLink, counter, depth); counter = 1; } depthGLNode(gLNode->nextlink, counter, depth); } int depthGList(struct GeneralizedList* gList) { if (gList == NULL || gList->headerNode == NULL) { printf("广义表不存在!\n"); return -1; } int depth = 1; depthGLNode(gList->headerNode, 1, &depth); if (gList->headerNode->tag == atom && (strcmp(gList->headerNode->data, "") == 0)) { return 0; } return depth; } void ruinGLNode(struct GLNode* gLNode) { if (gLNode == NULL) { return; } if (gLNode->tag == atom) { if (gLNode->nextlink == NULL) { free(gLNode); gLNode = NULL; return; } ruinGLNode(gLNode->nextlink); } else { ruinGLNode(gLNode->childLink); if (gLNode->nextlink == NULL) { free(gLNode); gLNode = NULL; return; } ruinGLNode(gLNode->nextlink); } free(gLNode); gLNode = NULL; } void ruinGList(struct GeneralizedList* gList) { if (gList == NULL || gList->headerNode == NULL) { printf("广义表不存在!\n"); return; } ruinGLNode(gList->headerNode); } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "generalizedList.h" int main() { printf("创建广义表:\n"); struct GeneralizedList* gList = initGeneralizedList(); printf("展示广义表:\n"); gList->foreach(gList); struct GLNode* gLNode = gList->get("44", gList); printf("44"); gList->printfGLNode(gLNode); struct GeneralizedList* header = gList->header(gList); if (header != NULL) { printf("表头->"); gList->printfGLNode(header->headerNode); } struct GeneralizedList* tail = gList->tail(gList); if (tail != NULL) { printf("表尾->"); gList->printfGList(tail->headerNode); } printf("深度:%d\n", gList->depth(gList)); return 0; } |
运行结果:
创建广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 展示广义表: LS=(3,56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 44结点:(44) 表头->结点:(3) 表尾->结点:(56,(6,7,87),23,(),(45,(5,6,(44,5))),77) 深度:4 |
小结
前两章讨论的线性表、栈和队列都是典型的线性结构,结构中数据元素都是不能分解的非结构的原子类型。它们的逻辑特征是:每个数据元素至多有一个直接前趋和直接后续。而多维数组和广义表是一种复杂的非线性结构,它们的逻辑特征是:一个数据元素可能有多个直接前趋和直接后继。
多维数组可以看成是线性表的推广。因为一旦确定数组是按行或按列优先顺序存储之后,每个数组元素之间的关系就同一维数组一样变成线性的了。因此,只要弄清楚多维数组按行优先顺序存储结构之后,它的运算就同线性表的运算类似。
本章主要介绍数组的逻辑结构特征及其存储方法、特殊矩阵和稀疏矩阵的压缩存储方法,以及广义表的逻辑结构和存储表示。
二维数组元素在存储结构中的地址计算方法、特殊矩阵压缩存储时的下标变换方法以及广义表的表头及表尾的求解是本章的学习重点,本章难点是压缩存储特殊矩阵和稀疏矩阵的各种运算及应用。