十字链表

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

基本概念

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

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

其中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;//删除第一个弧结点elsepre->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;elsepre->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 20typedef 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;//删除第一个弧结点elsepre->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;elsepre->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;
}

【数据结构】十字链表相关推荐

  1. 数据结构——十字链表(C语言实现)

    十字链表是将邻接表和逆邻接表结合在一起的一种有向图的数据结构 十字链表的节点结构体表示的是一个节点到另一个节点的边,并且此由指出节点(from)和指入节点(to)共同使用,因此大大节省了内存. 先直观 ...

  2. 数据结构 - 十字链表之稀疏矩阵的存储

    当数组中非零元素非常少时,我们可以称之为稀疏矩阵.为了减少空间的浪费,在存储稀疏矩阵的时候,我们对它进行压缩存储,即指存储非零信息. 一.数组的顺序存储 在存储稀疏矩阵的时候,除了存储非零元素的值,还 ...

  3. 日撸 Java 三百行(37 天: 十字链表)

    注意:这里是JAVA自学与了解的同步笔记与记录,如有问题欢迎指正说明 目录 前言: 一.关于十字链表 1.特殊情况下邻接表的局限 2.十字链表的结构与特点 二.十字链表的代码实现 1.基本属性 2.出 ...

  4. 阿里巴巴校园招聘 —— 灵犀游戏开发测试岗笔试题目总结(菜鸡版解析)涉及知识点——十字链表、线程与堆栈、FTP、Telnet、红黑树、哈夫曼树、平衡二叉树、乐观锁、悲观锁、HTTP、NIM游戏

    内容 感觉内容无非是那几个: 数据结构与算法 + 计算机网络 + 操作系统 + C++基础语法知识 简单的送分题我就不说了,我说几个还有点迷惑性的点来整理一下: 1. 十字链表 在Linux内核中应用 ...

  5. noj数据结构稀疏矩阵的加法十字链表_数据结构学习(C )稀疏矩阵(十字链表1)

    CSDN 先说说什么叫稀疏矩阵. 你说, 这个问题很简单吗, 那你一定不知道中国学术界的嘴皮子仗, 对一个字眼的"抠"将会导致两种相反的结论.这是清华 2000 年的一道考研题:& ...

  6. 西工大NOJ数据结构理论——013.以十字链表为存储结构实现矩阵相加(严5.27)

      我第一下拿到这个题目,第一反应就是先定义好数据结构,然后构建好十字链表基础操作的函数,也就是"创插遍历"这些操作.下面是我的定义和函数操作. typedef int ElemT ...

  7. 数据结构——图的十字链表实现

    十字链表定义 之前的邻接表对于有向图来说,可分为邻接表和逆邻接表,对于出入度要求不同时,生成的邻接表也不同 十字链表的出现就是将 邻接表和逆邻接表整合在一起 在同一个代码中,对于一个顶点可以找到其出度 ...

  8. 【数据结构】图的存储结构(邻接矩阵、邻接表、十字链表、邻接多重表)及实现(C语言)

    目录 1. 邻接矩阵表示法 1.1 图的邻接矩阵 1.2 创建有向网的邻接矩阵 2. 邻接表表示法 2.1 图的邻接表存储结构 2.2 创建有向图的邻接表 3. 十字链表表示法 3.1 图的十字链表存 ...

  9. 【swjtu】数据结构实验5_基于十字链表的稀疏矩阵转置

    实验内容及要求: 编写程序,从字符文件读入三个正整数m, n, t以及t个三元组(i, j, e)建立稀疏矩阵的十字链表存储结构.其中,m.n分别表示矩阵行数和列数:i,  j为非零元素行号和列号.编 ...

  10. 数据结构之图的存储结构:十字链表法

    图的存储结构:十字链表法 思维导图: 产生条件: 十字链表法的定义: 十字链表法的代码定义: 性能分析: 思维导图: 产生条件: 当用邻接矩阵存储时:空间复杂度为O(|v|^2),太大 当用邻接表法存 ...

最新文章

  1. 【数学专题】约数个数与欧拉函数
  2. Ajax、Comet与Websocket
  3. 【论文】引用格式 NoteExpress管理文献
  4. java全面的计算器代码_Java实现计算器的代码
  5. python计算机视觉编程(全)(强烈推荐)_推荐一个计算机视觉图书:python计算机视觉编程...
  6. ActiveMQ_Windows版本的安装部署
  7. Forever让NodeJS应用后台执行
  8. [转]wchar_t char std::string std::wstring CString 转换
  9. oracle中创建表的自增ID
  10. visualroute 很棒的一款工具
  11. 保证金监控中心查询期货开户密码
  12. stars-one的原创工具——星之小说下载器(JavaFx应用 )
  13. 苹果cmsv10仿爱美剧网自适应美化模板免费模板
  14. [Power Query] 删除重复项
  15. 7大子论坛回顾 | PGConf.Asia亚洲技术大会DAY2精彩继续
  16. CPU动态调频二:interactive governor
  17. 征婚网站php免费源码,php交友网站系统正式版php征婚网站模板
  18. 米思齐编程?很简单。
  19. linux12.040串口驱动,pci串行口驱动下载
  20. 欧拉角和旋转矩阵的学习心得

热门文章

  1. java rslinx_如何使用AB PLC仿真软件Studio 5000 Logix Emulate
  2. 三大协议NetBEUI和 IPX/SPX TCP/IP
  3. 2020年7月的光立方包含程序和PCB文件
  4. CentOS安装锐捷认证客户端
  5. 会计计算机,什么是好的计算机会计软件?
  6. 【龙讯module小课堂】“光”怪陆离:PWmat计算光学性质(一)
  7. 通信原理 | 信道特性及其数学模型
  8. 静态网页-学校官网(附源码)
  9. 在 Mac 端获取【网易云音乐】付费音乐的播放地址(注意下载要坚决支持正版!)
  10. qq音乐网络异常获取音乐失败_qq音乐网络异常获取音频失败怎么办