十字链表的结构类型

   十字链表记录稀疏矩阵,可以在有效存取的同时避免空间的浪费。

组成:

  对于十字链表的每个结点都有两个指针域,一个是行指针Right,一个是列指针Down,还有一块数据域用来存取行坐标Row,列坐标Col和数值Value。

  在稀疏矩阵的十字链表实现中要注意头结点和非零结点的区分。因此可以在结构中先加入一个Tag来标志下这个节点是头结点还是非零结点,然后在结构中采用联合union来存放这两种不同的类型的结点(就是将他们融合在一起),

  对于矩阵的入口就是第一个结点,要记录这个矩阵的基本信息,比如几行几列,有多少个非零元素。注意每行每列的头结点,是一个数组。头结点指后面的行和列结点,行和列结点指对应的数据。

 因为十字链表结点里面有一个行指针和一个列指针,所以就可以理解为一个结构里由两个链表,一个行链表和一个列链表,然后每一行每一列的结点都是一个循环链表,最后都要指回这个行或列结点的头结点,如果没有后继结点就要指回自己,对于矩阵中就是有一列或者一行是空的也要有结点表示

定义:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include <stdbool.h>

//这边是十字链表矩阵
typedef int ElementType;
typedef enum { Head, Term } NodeTag;//定义枚举类型,用01来区分Head和Term
struct TermNode {
	//用来表示记录一个非零结点的信息
	int Row, Col;//记录行列的坐标位置
	ElementType Value;//记录对应点处的值
};
typedef struct MNode* PtrToMNode;//这是个指针类型的矩阵结点,表示这个结点的内容
struct MNode {
	PtrToMNode Down, Right;//分别用来链接下边的结点和右边的结点
	NodeTag Tag;//结点类型标记,0为Head表示头结点,1为Term表示非零结点;
	union {
		PtrToMNode Next;//表示头结点,记录下一个位置;
		struct TermNode Term;//表示一个数据域的类型
	}URegion;//用联合来表示存放的内容:是数据域还是头结点
};
typedef PtrToMNode Matrix;//把指针类型的矩阵结点定义为矩阵类型

功能实现:

输出十字链表:

//输出十字链表
void PutMatrix(Matrix M) {
	Matrix a, b;
	printf("矩阵->   行:%d   列:%d\n", M->URegion.Term.Col, M->URegion.Term.Row);//打印矩阵入口结点的基本信息
	a = M->Right;//a指向矩阵结点的下一个行列头结点,准备遍历
	while (a!=M) {//a结点就是遍历头结点的遍历,每次while都指向下一个行列的的头结点
		//输出方式要么一行一行遍历,要么一列一列遍历
		b = a->Right;//b指向a所在该行列头结点的下一个结点,即数据结点
		//这里b也可以是a的down,取决于遍历方式
		//每次while进来都是a头结点里面行列的数据结点
		while (b!=a) {//只要b没指回头结点就继续进行
			printf("行:%d\t列:%d\t值:%d\n", b->URegion.Term.Col, b->URegion.Term.Row, b->URegion.Term.Value);//输出结点信息
			b = b->Right;
		}
		a=a->URegion.Next;
	}
}

创建十字链表:

//创建十字链表
Matrix createMatrix(Matrix MN,int M ,int N,int ab[][4]) {//也可以传入一个矩阵判断非零节点创建十字链表。
	//ab为一个二维数组,*((int*)array + n*i + j);表示行列位置
	int max = (M > N ? M : N);
	int i, j;
	Matrix* Mhead = (Matrix*)malloc(max * sizeof(Matrix));//根据行列最大值创建一个指针数组,保存头结点
	//用数组的原因是方便行列数据的结点的插入,实现行节点和列结点时循环链表的实现
	// 
	//创建头结点
	//MN = (Matrix)malloc(sizeof(struct MNode));
	MN->URegion.Term.Col = M;
	MN->URegion.Term.Row = N;
	// 
	//利用尾插法插入行列头结点的循环链表
	Matrix a, b, c;

	a = MN;//指向头结点
	for (i = 0; i < max; i++) {//尾插法
		Mhead[i] = (Matrix)malloc(sizeof(struct MNode));
		Mhead[i]->Tag = 0;
		Mhead[i]->Down = Mhead[i]->Right = Mhead[i];//结点创建后行列一开始指向自己
		if (a->Tag == 1) {
			a->Down = Mhead[i];
			a->Right = Mhead[i];
		}
		else {
			a->URegion.Next = Mhead[i];
		}//做一个结点的链接
		//注意这里uregion是联合体公用域会修改行列值
		a = Mhead[i];//指向结点的之后后移
	}
	a->URegion.Next = MN;//注意最后要指回去构成循环链表
	//数据载入
	for (i = 0; i < M; i++) {//行遍历
		for (j = 0; j < N; j++) {//列遍历
			if (ab[i][j] != 0) {//结点判断
				b = (Matrix)malloc(sizeof(struct MNode));//生成一个结点,并进行赋值
				b->Tag = 1; b->URegion.Term.Value = ab[i][j];
				b->URegion.Term.Col = i; b->URegion.Term.Row = j;
				//在行表中插入,形成一个行的循环链表
				c = Mhead[i];//找到对应的行
				while (c->Right != Mhead[i] && c->Right->URegion.Term.Row < j) {
					//在这一行找到合适的列插入
					c = c->Right;
				}
				b->Right = c->Right;
				c->Right = b;
				//在列表中插入,形成一个列的循环链表
				c = Mhead[j];//找到对应的列
				while (c->Down != Mhead[j] && c->Down->URegion.Term.Col< i) {
					//在这一行找到合适的行插入
					c = c->Down;
				}
				b->Down = c->Down;
				c->Down = b;
			}
		}
	}
	return MN;
}

函数使用:

#include<stdio.h>
#include<stdlib.h>
#include <stdbool.h>

int main() {
	int ab[3][4] = { 0,0,0,0,0,1,0,0,0,1,0,1 };//二维数组传入函数必须要有列数
	//或者传入二维数组指针,要开辟新空间,就是新建一个指针类型的二维数组
	// 对这个二维数组要空间,先是分配双指针类型的行,再是对每行分配列空间
	// 最后利用for取copy二维数组
	//或者把ab转为int**类型然后传入函数用利用二维数组地址连续*((int *)p + i * col + j)去遍历读取
	Matrix MN = (Matrix)malloc(sizeof(struct MNode));
	MN->Tag = 1; MN->Down = MN; MN->Right = MN;
	MN= createMatrix(MN, 3, 4, ab);
	PutMatrix(MN);
	return 0;
}

汇总:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include <stdbool.h>

//这边是十字链表矩阵
typedef int ElementType;
typedef enum { Head, Term } NodeTag;//定义枚举类型,用01来区分Head和Term
struct TermNode {
	//用来表示记录一个非零结点的信息
	int Row, Col;//记录行列的坐标位置
	ElementType Value;//记录对应点处的值
};
typedef struct MNode* PtrToMNode;//这是个指针类型的矩阵结点,表示这个结点的内容
struct MNode {
	PtrToMNode Down, Right;//分别用来链接下边的结点和右边的结点
	NodeTag Tag;//结点类型标记,0为Head表示头结点,1为Term表示非零结点;
	union {
		PtrToMNode Next;//表示头结点,记录下一个位置;
		struct TermNode Term;//表示一个数据域的类型
	}URegion;//用联合来表示存放的内容:是数据域还是头结点
};
typedef PtrToMNode Matrix;//把指针类型的矩阵结点定义为矩阵类型

//输出十字链表
void PutMatrix(Matrix M) {
	Matrix a, b;
	printf("矩阵->   行:%d   列:%d\n", M->URegion.Term.Col, M->URegion.Term.Row);//打印矩阵入口结点的基本信息
	a = M->Right;//a指向矩阵结点的下一个行列头结点,准备遍历
	while (a!=M) {//a结点就是遍历头结点的遍历,每次while都指向下一个行列的的头结点
		//输出方式要么一行一行遍历,要么一列一列遍历
		b = a->Right;//b指向a所在该行列头结点的下一个结点,即数据结点
		//这里b也可以是a的down,取决于遍历方式
		//每次while进来都是a头结点里面行列的数据结点
		while (b!=a) {//只要b没指回头结点就继续进行
			printf("行:%d\t列:%d\t值:%d\n", b->URegion.Term.Col, b->URegion.Term.Row, b->URegion.Term.Value);//输出结点信息
			b = b->Right;
		}
		a=a->URegion.Next;
	}
}
//创建十字链表
Matrix createMatrix(Matrix MN,int M ,int N,int ab[][4]) {//也可以传入一个矩阵判断非零节点创建十字链表。
	//ab为一个二维数组,*((int*)array + n*i + j);表示行列位置
	int max = (M > N ? M : N);
	int i, j;
	Matrix* Mhead = (Matrix*)malloc(max * sizeof(Matrix));//根据行列最大值创建一个指针数组,保存头结点
	//用数组的原因是方便行列数据的结点的插入,实现行节点和列结点时循环链表的实现
	// 
	//创建头结点
	//MN = (Matrix)malloc(sizeof(struct MNode));
	MN->URegion.Term.Col = M;
	MN->URegion.Term.Row = N;
	// 
	//利用尾插法插入行列头结点的循环链表
	Matrix a, b, c;

	a = MN;//指向头结点
	for (i = 0; i < max; i++) {//尾插法
		Mhead[i] = (Matrix)malloc(sizeof(struct MNode));
		Mhead[i]->Tag = 0;
		Mhead[i]->Down = Mhead[i]->Right = Mhead[i];//结点创建后行列一开始指向自己
		if (a->Tag == 1) {
			a->Down = Mhead[i];
			a->Right = Mhead[i];
		}
		else {
			a->URegion.Next = Mhead[i];
		}//做一个结点的链接
		//注意这里uregion是联合体公用域会修改行列值
		a = Mhead[i];//指向结点的之后后移
	}
	a->URegion.Next = MN;//注意最后要指回去构成循环链表
	//数据载入
	for (i = 0; i < M; i++) {//行遍历
		for (j = 0; j < N; j++) {//列遍历
			if (ab[i][j] != 0) {//结点判断
				b = (Matrix)malloc(sizeof(struct MNode));//生成一个结点,并进行赋值
				b->Tag = 1; b->URegion.Term.Value = ab[i][j];
				b->URegion.Term.Col = i; b->URegion.Term.Row = j;
				//在行表中插入,形成一个行的循环链表
				c = Mhead[i];//找到对应的行
				while (c->Right != Mhead[i] && c->Right->URegion.Term.Row < j) {
					//在这一行找到合适的列插入
					c = c->Right;
				}
				b->Right = c->Right;
				c->Right = b;
				//在列表中插入,形成一个列的循环链表
				c = Mhead[j];//找到对应的列
				while (c->Down != Mhead[j] && c->Down->URegion.Term.Col< i) {
					//在这一行找到合适的行插入
					c = c->Down;
				}
				b->Down = c->Down;
				c->Down = b;
			}
		}
	}
	return MN;
}
int main() {
	int ab[3][4] = { 0,0,0,0,0,1,0,0,0,1,0,1 };//二维数组传入函数必须要有列数
	//或者传入二维数组指针,要开辟新空间,就是新建一个指针类型的二维数组
	// 对这个二维数组要空间,先是分配双指针类型的行,再是对每行分配列空间
	// 最后利用for取copy二维数组
	//或者把ab转为int**类型然后传入函数用利用二维数组地址连续*((int *)p + i * col + j)去遍历读取
	Matrix MN = (Matrix)malloc(sizeof(struct MNode));
	MN->Tag = 1; MN->Down = MN; MN->Right = MN;
	MN= createMatrix(MN, 3, 4, ab);
	PutMatrix(MN);
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
引用: 十字链表是一种用于表示有向图的数据结构。它包含两种类型的节点:顶点节点和弧节点。顶点节点记录着当前顶点的相关信息,如顶点的数据、指向该顶点的入度弧节点和指出该顶点的出度弧节点。而弧节点则表示两个顶点之间的有向边关系,其中tailvex表示弧的起始顶点,headvex表示弧的结束顶点,而headlink和taillink分别指向与该节点相邻的下一条弧的节点。 引用: 邻接多重表也是一种用于表示无向图的数据结构。它与十字链表相似,同样包含顶点节点和边节点。每个顶点节点记录着当前顶点的相关信息,如顶点的数据以及与该顶点相邻的一条边的节点。而边节点则表示两个顶点之间的无向边关系,其中ivex和jvex分别表示边的两个顶点,ilink和jlink分别指向与该边相邻的下一条边的节点。需要注意的是,邻接多重表中的边是双向的,因此在图的存储中每条边都需要用两个边节点表示。 所以,十字链表是用于表示有向图的数据结构,而邻接多重表则用于表示无向图的数据结构。它们分别在记录顶点和边之间的关系上有所不同。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [6-4图-十字链表与邻接多重表](https://blog.csdn.net/weixin_45825865/article/details/116152711)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值