【数据结构】十字链表

【数据结构】十字链表介绍了有向图的链式存储结构:十字链。

大家好,又见面了,我是你们的朋友全栈君。

十字链表

上篇文章讲解了图的邻接表的链式存储方式,这篇文章讲解图的十字链存储方式。

基本概念

十字链表是为了便于求得图中顶点的度(出度和入度)而提出来的。它是综合邻接表和逆邻接表形式的一种链式存储结构。

在十字链表存储结构中,有向图中的顶点的结构如下所示:

【数据结构】十字链表

其中data表示顶点的具体数据信息,而firstIn则表示指向以该顶点为弧头的第一个弧节点。而firstOut则表示指向以该顶点为弧尾的第一个弧节点。为了表示有向图中所有的顶点,采用一个顶点数组存储每一个结点,如下图所示。

【数据结构】十字链表

另外,在十字链表存储结构中,有向图中的每一条弧都有一个弧结点与之对应,具体的弧结点结构如下所示:

【数据结构】十字链表

其中的tailVex表示该弧的弧尾顶点在顶点数组xList中的位置,headVex表示该弧的弧头顶点在顶点数组中的位置。hLink则表示指向弧头相同的下一条弧,tLink则表示指向弧尾相同的下一条弧。

从十字链表的数据结构来看,每一个顶点对应两个链表:以该顶点为弧尾的弧结点所组成的链表以及以该顶点为弧头的弧结点所组成的链表。

如下图所示的一个有向图:

【数据结构】十字链表

其对应的顶点以及弧结点如下所示。拿结点A说明,该结点对应两个链表(绿色和黄色标记的)。绿色链表表示以结点A为弧头的弧组成的链表。黄色链表表示以结点A为弧尾的弧组成的链表。

【数据结构】十字链表

基本操作

1、创建图的十字链表

Status createDG(OLGraph &G, int vexNum, int arcNum){
	G.vexNum = vexNum;
	G.arcNum = arcNum;

	for(int i = 0; i<G.vexNum; i++){
		cin>>G.xList[i].data;
		G.xList[i].firstIn = NULL;
		G.xList[i].firstOut = NULL;
	}//初始化

	for(int i = 0; i<G.arcNum; i++){
		vexNode node1, node2;
		cout<<"please input two nodes of "<<i+1<<"-th arc"<<endl;
		cin>>node1.data>>node2.data;

		insertArc(G, node1, node2); 
	}
	return 1;
}

测试结果:

如下图所示首先输入四个顶点:A、B、C、D,然后输入7条弧。

【数据结构】十字链表

既可以构建如下的有向图。

【数据结构】十字链表

2、按照十字链表形式打印图

Status printDG(OLGraph &G){
	for(int i = 0; i<G.vexNum; i++){
		arcBox *ptail = G.xList[i].firstOut;
		arcBox *phead = G.xList[i].firstIn;

		//打印以结点i为弧尾的链
		cout<<"以结点"<<i<<"为弧尾的链 "<<G.xList[i].data;
		while(ptail){
			cout<<"-->"<<"|"<<ptail->tailVex<<"|"<<ptail->headVex;
			ptail = ptail-> tLink;
		}
		cout<<"-->NULL"<<endl;

		//打印以结点i为弧头的链

		cout<<"以结点"<<i<<"为弧头的链 "<<G.xList[i].data;
		while(phead){
			cout<<"-->"<<"|"<<phead->tailVex<<"|"<<phead->headVex;
			phead = phead->hLink;
		}
		cout<<"-->NULL"<<endl;
	}
	return 1;
}

测试结果:

每一个顶点对应两个链表(以该结点为弧头的链表和以该结点位弧尾的链表),分别将其打印出来。

【数据结构】十字链表

3、向图中插入一个结点

Status insertNode(OLGraph &G, vexNode node){

	G.xList[G.vexNum].data = node.data;
	G.xList[G.vexNum].firstIn = NULL;
	G.xList[G.vexNum].firstOut = NULL;
	G.vexNum = G.vexNum + 1;
	return 1;
}

测试结果:

向原来的有向图插入一个新的结点E。新插入的节点没有与之前图中的顶点建立弧,因此其打印结果如下所示。

【数据结构】十字链表

此时有向图变成了:

【数据结构】十字链表

4、向图中结点之间插入一条弧

void insertArcAction(OLGraph &G, int index1, int index2){

	arcBox* pArc = new arcBox[1];
	pArc->tailVex = index1;
	pArc->headVex = index2;
	pArc->info = NULL;

	arcBox *ptail = G.xList[index1].firstOut;
	arcBox *phead = G.xList[index2].firstIn;

	if(!ptail){pArc->tLink = NULL;}
	else{pArc->tLink = ptail;}

	if(!phead){pArc->hLink = NULL;}
	else{pArc->hLink = phead;}

	G.xList[index1].firstOut = pArc;//链头部插入弧结点
	G.xList[index2].firstIn = pArc;
}

Status insertArc(OLGraph &G, vexNode node1, vexNode node2){

	int index1 = locateVertex(G, node1);
	int index2 = locateVertex(G, node2);

	insertArcAction(G, index1, index2);
	return 1;
}

测试结果:

插入一条结点B到结点C的弧。再次打印该有向图时,结果如下所示。

【数据结构】十字链表

此时有向图变成了:

【数据结构】十字链表

5、删除一条弧

void deleteOutArcAction(OLGraph &G, int index1, int index2){
	arcBox *cur = G.xList[index1].firstOut;
	arcBox *pre = cur;

	int count = 0;
	if(!cur)
		return;
	else{
		while(cur){
			count++;
			if(cur->headVex == index2)
				break;
			pre = cur;
			cur = cur->tLink;
		}
	}
	if(!cur)
		return;//该结点没有对应的弧
	else if(count <=1)
		G.xList[index1].firstOut = pre->tLink;//删除第一个弧结点
	else
		pre->tLink = cur->tLink;//删除非第一个弧结点
}

void deleteInArcAction(OLGraph &G, int index1, int index2){
	arcBox *cur = G.xList[index2].firstIn;
	arcBox *pre = cur;

	int count = 0;
	if(!cur)
		return;
	else{
		while(cur){
			count++;
			if(cur->tailVex == index1)
				break;
			pre = cur;
			cur = cur->hLink;
		}
	}
	if(!cur)
		return;//该结点没有对应的弧
	else if(count <=1)
		G.xList[index2].firstIn = pre->hLink;
	else
		pre->hLink = cur->hLink;
}

Status deleteArc(OLGraph &G, vexNode node1, vexNode node2){
	//删除从结点1到结点2的弧(有方向)
	int index1 = locateVertex(G, node1);
	int index2 = locateVertex(G, node2);

	deleteOutArcAction(G, index1, index2);//删除两条链表里面的值
	deleteInArcAction(G, index1, index2);

	return 1;
}

测试结果:

删除从结点A到结点B的弧。重新打印该有向图,结果如下所示:

【数据结构】十字链表

此时有向图变成了:

【数据结构】十字链表

6、删除一个顶点

Status deleteNode(OLGraph &G, vexNode node){
	//删除结点后,该xList顶点数组中该结点后面的结点不前移,而只是将该被删除的结点的data设置成为一个较大的值
	int index = locateVertex(G, node);

	for(int i = 0; i<G.vexNum; i++){
		if(i == index)
			continue;
		else{
			deleteArc(G, G.xList[index], G.xList[i]);//删除以该结点为弧尾的弧
			deleteArc(G, G.xList[i], G.xList[index]);//删除以该结点为弧头的弧
		}
	}
	G.xList[index].data = '0';//置'0'表示该结点被删除
	G.xList[index].firstIn = NULL;
	G.xList[index].firstOut = NULL;

	return 1;
}

测试结果:

删除结点D,重新打印该有向图,其结果如下所示(只剩下3条弧,4个有效结点,结点D被删除后,该结点的内容变从‘D’变为‘0’):

【数据结构】十字链表

测试有向图变成了:

【数据结构】十字链表

7、全部代码

// 十字链表.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>

using namespace std;

#define MAX_VERTEX_NUM 20

typedef int Status;
typedef int infoType;
typedef char vertexType;

typedef struct arcBox{
	int tailVex, headVex;
	struct arcBox *hLink, *tLink;
	infoType *info;
}arcBox;//弧结点

typedef struct vexNode{
	vertexType data;
	arcBox *firstIn, *firstOut;
}vexNode;//顶点节点

typedef struct{
	vexNode xList[MAX_VERTEX_NUM];
	int vexNum, arcNum;
}OLGraph;//十字链

int locateVertex(OLGraph &G, vexNode node){
	int index = -1;
	for(int i = 0; i<G.vexNum; i++){
		if(node.data == G.xList[i].data){
			index = i;
			break;
		}
	}
	return index;
}

void insertArcAction(OLGraph &G, int index1, int index2);
Status insertArc(OLGraph &G, vexNode node1, vexNode node2);

Status createDG(OLGraph &G, int vexNum, int arcNum){
	G.vexNum = vexNum;
	G.arcNum = arcNum;

	for(int i = 0; i<G.vexNum; i++){
		cin>>G.xList[i].data;
		G.xList[i].firstIn = NULL;
		G.xList[i].firstOut = NULL;
	}//初始化

	for(int i = 0; i<G.arcNum; i++){
		vexNode node1, node2;
		cout<<"please input two nodes of "<<i+1<<"-th arc"<<endl;
		cin>>node1.data>>node2.data;

		insertArc(G, node1, node2); 
	}
	return 1;
}

Status printDG(OLGraph &G){
	for(int i = 0; i<G.vexNum; i++){
		arcBox *ptail = G.xList[i].firstOut;
		arcBox *phead = G.xList[i].firstIn;

		//打印以结点i为弧尾的链
		cout<<"以结点"<<i<<"为弧尾的链 "<<G.xList[i].data;
		while(ptail){
			cout<<"-->"<<"|"<<ptail->tailVex<<"|"<<ptail->headVex;
			ptail = ptail-> tLink;
		}
		cout<<"-->NULL"<<endl;

		//打印以结点i为弧头的链

		cout<<"以结点"<<i<<"为弧头的链 "<<G.xList[i].data;
		while(phead){
			cout<<"-->"<<"|"<<phead->tailVex<<"|"<<phead->headVex;
			phead = phead->hLink;
		}
		cout<<"-->NULL"<<endl;
	}
	return 1;
}

void insertArcAction(OLGraph &G, int index1, int index2){

	arcBox* pArc = new arcBox[1];
	pArc->tailVex = index1;
	pArc->headVex = index2;
	pArc->info = NULL;

	arcBox *ptail = G.xList[index1].firstOut;
	arcBox *phead = G.xList[index2].firstIn;

	if(!ptail){pArc->tLink = NULL;}
	else{pArc->tLink = ptail;}

	if(!phead){pArc->hLink = NULL;}
	else{pArc->hLink = phead;}

	G.xList[index1].firstOut = pArc;//链头部插入弧结点
	G.xList[index2].firstIn = pArc;
}

Status insertArc(OLGraph &G, vexNode node1, vexNode node2){

	int index1 = locateVertex(G, node1);
	int index2 = locateVertex(G, node2);

	insertArcAction(G, index1, index2);
	return 1;
}

Status insertNode(OLGraph &G, vexNode node){

	G.xList[G.vexNum].data = node.data;
	G.xList[G.vexNum].firstIn = NULL;
	G.xList[G.vexNum].firstOut = NULL;
	G.vexNum = G.vexNum + 1;
	return 1;
}

Status deleteArc(OLGraph &G, vexNode node1, vexNode node2);

Status deleteNode(OLGraph &G, vexNode node){
	//删除结点后,该xList顶点数组中该结点后面的结点不前移,而只是将该被删除的结点的data设置成为一个较大的值
	int index = locateVertex(G, node);

	for(int i = 0; i<G.vexNum; i++){
		if(i == index)
			continue;
		else{
			deleteArc(G, G.xList[index], G.xList[i]);//删除以该结点为弧尾的弧
			deleteArc(G, G.xList[i], G.xList[index]);//删除以该结点为弧头的弧
		}
	}
	G.xList[index].data = '0';//置'0'表示该结点被删除
	G.xList[index].firstIn = NULL;
	G.xList[index].firstOut = NULL;

	return 1;
}

void deleteOutArcAction(OLGraph &G, int index1, int index2){
	arcBox *cur = G.xList[index1].firstOut;
	arcBox *pre = cur;

	int count = 0;
	if(!cur)
		return;
	else{
		while(cur){
			count++;
			if(cur->headVex == index2)
				break;
			pre = cur;
			cur = cur->tLink;
		}
	}
	if(!cur)
		return;//该结点没有对应的弧
	else if(count <=1)
		G.xList[index1].firstOut = pre->tLink;//删除第一个弧结点
	else
		pre->tLink = cur->tLink;//删除非第一个弧结点
}

void deleteInArcAction(OLGraph &G, int index1, int index2){
	arcBox *cur = G.xList[index2].firstIn;
	arcBox *pre = cur;

	int count = 0;
	if(!cur)
		return;
	else{
		while(cur){
			count++;
			if(cur->tailVex == index1)
				break;
			pre = cur;
			cur = cur->hLink;
		}
	}
	if(!cur)
		return;//该结点没有对应的弧
	else if(count <=1)
		G.xList[index2].firstIn = pre->hLink;
	else
		pre->hLink = cur->hLink;
}

Status deleteArc(OLGraph &G, vexNode node1, vexNode node2){
	//删除从结点1到结点2的弧(有方向)
	int index1 = locateVertex(G, node1);
	int index2 = locateVertex(G, node2);

	deleteOutArcAction(G, index1, index2);//删除两条链表里面的值
	deleteInArcAction(G, index1, index2);

	return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{	
	int vexNum = 4;
	int arcNum = 7;

	OLGraph G;
	
	cout<<"Try to create a Orthogonal List of a graph..."<<endl;
	createDG(G, vexNum, arcNum);
	cout<<"Try to print the Orthogonal List of the very graph..."<<endl;
	printDG(G);
	
	cout<<"Try to insert a node into the graph..."<<endl;
	vexNode node;
	cout<<"   please input the data of the node to insert:"<<endl;
	cin>>node.data;
	insertNode(G, node);
	printDG(G);

	cout<<"Try to insert a arc into the graph..."<<endl;
	vexNode node1, node2;
	cout<<"   please choose two node:"<<endl;
	cin>>node1.data>>node2.data;
	insertArc(G, node1, node2);
	printDG(G);

	cout<<"Try to delete the arc between two nodes..."<<endl;
	vexNode node3, node4;
	cout<<"   please choose a arc with specifing two nodes:"<<endl;
	cin>>node3.data>>node4.data;
	deleteArc(G, node3, node4);
	printDG(G);

	cout<<"Try to delete a node of the graph..."<<endl;
	vexNode node5;
	cout<<"   please choose a node:"<<endl;
	cin>>node5.data;
	deleteNode(G, node5);
	printDG(G);

	system("pause");

	return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/150305.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 共享打印机错误代码709_连接共享打印机错误0*0000011b

    共享打印机错误代码709_连接共享打印机错误0*0000011b最近发现很多用户连接或安装局域网共享的打印机时出现很多问题,常见的错误代码是0x0000011b和0x00000709或0x000006d9这三个错误。要如何解决呢?下面来讲一下如何解决这两个问题。键盘组合键徽标键Win+R键打开运行,在弹出的运行框中输入【services.msc】确定打开服务窗口,检查这两个服务是否已启动:PrintSpooler和WindowsFirewall一般Win7易出的错误6d9是后面的服务未启动所致。依次查找并卸载KB5005565、KB5005566、KB5005…

    2025年10月20日
    5
  • html表格内容居中对齐_word里表格怎么居中

    html表格内容居中对齐_word里表格怎么居中加上样式如:margin:0auto;<tablestyle=”margin:0auto;”><tr><td><span>账号:</span></td><td><inputtype=”text”v-model=’user’/></td></tr><tr><td>…

    2026年1月22日
    6
  • CS和BS 到底是什么[通俗易懂]

    CS和BS 到底是什么[通俗易懂]C/S:Client/Server,客户端/服务器B/S:Browser/Server,浏览器/服务器cs,主要指的是传统的桌面级的应用程序,基于客户端的应用。bs,主要指的是web应用程序,基于浏览器的应用。区别[1]语言:C/S:c,c++,B/S:java,php,.Net,js,nodeJs[2]更新:C/S:下载新版本的客户端,升级不大方便。B/S:热更新,永远都是最新的。[3]数据通信:C/

    2025年10月14日
    4
  • 浅析瀑布流布局及其原理视频_jquery瀑布流布局

    浅析瀑布流布局及其原理视频_jquery瀑布流布局一、什么是瀑布流很多时候我们会看到一些Vlog网站或者展示图片的网站,它们的图片明明每一张的高度大小都不同,但是却能自动地适应,排成一行一行地展示,并且下拉到底的时候,加载的图片也会自动适应,这就是瀑布流,比如下图。…

    2025年6月21日
    3
  • c语言课程设计图书管理系统 报告_课程设计图书管理系统

    c语言课程设计图书管理系统 报告_课程设计图书管理系统实训项目名称:图书管理系统的设计与实现1.实训目的开发一个小型的图书管理应用软件,使用该软件可以实现图书信息的登记、浏览、借书、还书、删除和更新等操作。通过该系统的实现可以了解C++连接数据库的原理和技术,掌握VC界面的设计方法。2.实训要求(1)选择适当的程序开发语言(建议用C或C++)和数据库系统,完成实训内容。(2)程序能够正常运行,运算结果正确,满足设计要求。3.功…

    2022年10月10日
    5
  • 相机标定基础

    相机标定基础一.什么是摄像机标定从二维图像中恢复物体的三维信息,必须要知道空间坐标系中的物体点同它在图像平面上像点之间的对应关系,而这个对应关系是由摄像机的成像几何模型所决定的,这些几何模型参数就是摄像机参数。在大多数情况下这些参数必须通过实验才能得到,这个过程被称为摄像机标定。摄像机标定就是确定摄像机内部几何和光学特性(内部参数)以及摄像机坐标系相对于世界坐标系的三维位置和方向(外部参数)的过程。

    2022年5月11日
    60

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号