个人理解:
可以设想一个场景,一个矩阵是从左往右,从上往下,按左上角到右下角的方向生成的。在每个行和列交叉的节点上存储数据。那这些数据间有何种联系,采用何种方式可以方便的对任意节点的数据进行读写操作?
假设每个节点上存储了除了数据外还有本节点的行、列标号,另外还都有两个钩子,一个向右钩住同一行上下一个节点,另一个钩子向下钩住同一列上下一个节点,以此类推便可以生成一个每个节点都是通过钩子相连的矩阵。每个行或者列的开头都有一个代表行或者列开始位置的头钩子(是可移动的);(钩子即是指针)
读取节点:即头钩子钩住(指向)的节点的行列标号若匹配即是要读取的节点位置
插入节点:行、列的头钩子遍历本行、列时,遇到第一个大于新节点的行标、列标就停下。此时钩子钩住的就是新节点的下一个节点,而钩子所在的节点位置,就是新节点要插入的位置
删除节点:同插入节点开始一样,通过头钩子找到,要插入节点的前后节点,然后这两个节点相连,摘除要删除的节点即可
注意:头钩子的移动,要在行、列两个方向上都有
#include <stdio.h>
#include <stdlib.h>
#define elemtype int//元素类型自定义
//节点定义
typedef struct _node{
int linumb, conumb;//行号,列号
elemtype data;//数据域
struct _node *right;//向右指向指针
struct _node *down;//向下指向指针
}Node, *PtrNode;
//矩阵
typedef struct _table{
int mu, nu;//链表的行数,列数,
PtrNode *rhead, *chead;//链表行、列头指针
}CrossTable;
//初始化矩阵
CrossTable CreaTable(CrossTable M){
int mu,nu;//矩阵的行数,列数
int i, j, e;//节点的行标,列标,数据
Node *p, *q;
mu = 4; nu = 3;
M.mu = mu;
M.nu = nu;
M.rhead = (PtrNode*)malloc((mu + 1)*sizeof(PtrNode));//+1是因为行、列都是从1开始
M.chead = (PtrNode*)malloc((nu + 1)*sizeof(PtrNode));
if (!M.rhead || !M.chead){
printf("初始化矩阵失败\n");
exit(0);
}
for (int i = 1; i <= mu; i++){
M.rhead[i] = NULL;
}
for (int j = 1; j <= nu; j++){
M.chead[j] = NULL;
}
int num = mu;
while(num-- != 0){
scanf_s("%d%d%d", &i, &j, &e);
if (!(p = (Node*)malloc(sizeof(Node)))){//新节点申请内存
printf("初始化三元组失败\n");
exit(0);
}
p->linumb = i;
p->conumb = j;
p->data = e;
//连接到行的指定位置
if (M.rhead[i] == NULL || M.rhead[i]->conumb > j){
//rhead为头指针,刚开始为空,它应该是指向每行第一个元素的位置,这里的意思是,
//只要头指针的列标大,
//也即此时头指针不在最前面,就要把头指针往前移,以保持头指针一直在最前面
p->right = M.rhead[i];
//把新节点\行头指针互换位置,即都指针往开头方向移动
M.rhead[i] = p;
}
else{
//新节点在同行中比当前行头列标大,就要遍历这行中已有的列标
//以便新节点插入到合适的列标位置
q = M.rhead[i];
//从该行开始遍历每个已有的节点,每遇到比新节点列标小的,
//就移到下一个节点继续比,直到遇见比新节点列标大的
//表示新节点就在该节点前
//注意:是同q->right所指向的节点比,即同当前q的下一个节点比
for (; (q->right) && q->right->conumb < j; q = q->right);
//找到,把新节点加到q的后面,q->right的前面
p->right = q->right;
q->right = p;
}
//连接到列的指定位置
if (M.chead[j] == NULL || M.chead[j]->linumb > i){
p->down = M.chead[j];
M.chead[j] = p;
}
else{
q = M.chead[j];
for (; (q->down) && q->down->linumb < i; q = q->down);
p->down = q->down;
q->down = p;
}
}
return M;
}
void display(CrossTable M){//行序为主,按照列号从小到大,每一行来打印
for (int i = 1; i <= M.mu; i++){
if (M.rhead[i] != NULL){
PtrNode p = M.rhead[i];
while (p != NULL){
printf("%d\t%d\t%d\n", p->linumb, p->conumb, p->data);
p = p->right;
}
}
}
}
void display2(CrossTable M){//列序为主,按照行号从小到大,每一列来打印
for (int i = 1; i <= M.nu; i++){
if (M.chead[i] != NULL){
PtrNode p = M.chead[i];
while (p != NULL){
printf("%d\t%d\t%d\n", p->linumb, p->conumb, p->data);
p = p->down;
}
}
}
}
int main(){
CrossTable M;
M.rhead = NULL;
M.chead = NULL;
M = CreaTable(M);
printf("\n行序为主输出矩阵M:\n");
display(M);
printf("\n列序为主输出矩阵M:\n");
display2(M);
return 0;
}