稀疏矩阵数据结构的存储——十字链表法

个人理解:

可以设想一个场景,一个矩阵是从左往右,从上往下,按左上角到右下角的方向生成的。在每个行和列交叉的节点上存储数据。那这些数据间有何种联系,采用何种方式可以方便的对任意节点的数据进行读写操作?

假设每个节点上存储了除了数据外还有本节点的行、列标号,另外还都有两个钩子,一个向右钩住同一行上下一个节点,另一个钩子向下钩住同一列上下一个节点,以此类推便可以生成一个每个节点都是通过钩子相连的矩阵。每个行或者列的开头都有一个代表行或者列开始位置的头钩子(是可移动的);(钩子即是指针)

读取节点:即头钩子钩住(指向)的节点的行列标号若匹配即是要读取的节点位置

插入节点:行、列的头钩子遍历本行、列时,遇到第一个大于新节点的行标、列标就停下。此时钩子钩住的就是新节点的下一个节点,而钩子所在的节点位置,就是新节点要插入的位置

删除节点:同插入节点开始一样,通过头钩子找到,要插入节点的前后节点,然后这两个节点相连,摘除要删除的节点即可

注意:头钩子的移动,要在行、列两个方向上都有

#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;
}

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值