数据结构书上表示邻接表比较复杂,一般形式如下:
1 struct ArcNode{ //边结点 2 int adjvex; //有向边的另一个邻接点的序号 3 ArcNode *nextarc; //指向下一个边结点的指针 4 }; 5 6 struct VNode { //顶点 7 int data; //顶点信息 8 ArcNode *head1; //出边表的表头指针 9 ArcNode *head2; //入边表的表头指针 10 }; 11 12 struct LGraph{ //图的邻接表储存结构 13 VNode vertes[MAXN]; //顶点数组 14 int vexnum,arcnum; //顶点数和边(弧)数 15 }; 16 LGraph lg; //图(邻接表存储)
输入n,m分别为图的顶点数目和边数
接下来m行输入分表输入一条边的起点和终点
输出每个顶点的出度和入度
1 #include "iostream" 2 #include "algorithm" 3 #include "memory.h" 4 #include "cmath" 5 using namespace std; 6 #define MAXN 111 7 8 9 struct ArcNode{ //边结点 10 int adjvex; //有向边的另一个邻接点的序号 11 ArcNode *nextarc; //指向下一个边结点的指针 12 }; 13 14 struct VNode { //顶点 15 int data; //顶点信息 16 ArcNode *head1; //出边表的表头指针 17 ArcNode *head2; //入边表的表头指针 18 }; 19 20 struct LGraph{ //图的邻接表储存结构 21 VNode vertes[MAXN]; //顶点数组 22 int vexnum,arcnum; //顶点数和边(弧)数 23 }; 24 LGraph lg; //图(邻接表存储) 25 26 27 void CreateLG() //采用邻接表存储表示,构造有向图G 28 { 29 int i = 0; //循环变量 30 ArcNode *pi; //用来构造边链表的边结点指针 31 int v1,v2; //有向边的两个顶点 32 //lg.vexnum = lg.arcnum = 0; 33 //scanf("%d%d",&lg.vexnum,&lg.arcnum); 34 for (int i = 0;i < lg.vexnum; ++ i) //初始化表头指针为空 35 lg.vertes[i].head1 = lg.vertes[i].head2 = NULL; 36 for (int i = 0;i < lg.arcnum; ++ i) 37 { 38 scanf("%d%d",&v1,&v2); //输入一条边的起点和终点 39 v1--,v2--; 40 pi = new ArcNode; 41 pi -> adjvex = v2; 42 pi -> nextarc = lg.vertes[v1].head1; //插入链表 43 lg.vertes[v1].head1 = pi; 44 pi = new ArcNode; 45 pi -> adjvex = v1; 46 pi -> nextarc = lg.vertes[v2].head2; //插入链表 47 lg.vertes[v2].head2 = pi; 48 }//end of for 49 }//end of CreateLG 50 51 void DeleteLG() 52 { 53 int i; 54 ArcNode *pi; 55 for (i = 0;i < lg.vexnum; ++ i) 56 { 57 pi = lg.vertes[i].head1; 58 while (pi != NULL) 59 { 60 lg.vertes[i].head1 = pi -> nextarc; 61 delete pi; 62 pi = lg.vertes[i].head1; 63 } 64 pi = lg.vertes[i].head2; 65 //释放第i个顶点去边表各边结点所占的存储空间 66 while( pi != NULL) 67 { 68 lg.vertes[i].head2 = pi->nextarc; 69 delete pi; 70 pi = lg.vertes[i].head2; 71 } 72 } 73 } 74 75 76 int main() 77 { 78 int i; //循环变量 79 int id,od; //顶点的入度和出度 80 ArcNode *pi; //用来遍历边链表的边结点指针 81 while (1) 82 { 83 lg.vexnum = lg.arcnum = 0; 84 scanf("%d%d",&lg.vexnum,&lg.arcnum); 85 //首先输入顶点个数和边数 86 if (lg.vexnum == 0) break; //输入数据结束 87 CreateLG(); //构造有向图的邻接表结构 88 for (i = 0;i < lg.vexnum; ++ i) //统计各顶点出度并输出 89 { 90 od = 0; 91 pi = lg.vertes[i].head1; 92 while (pi != NULL) 93 { 94 od++; 95 pi = pi -> nextarc; 96 } 97 if (i == 0) printf("%d",od); 98 else printf(" %d",od); 99 } 100 puts(""); 101 for (i = 0;i < lg.vexnum; ++ i) //统计各顶点入度并输出 102 { 103 id = 0; 104 pi = lg.vertes[i].head2; 105 while (pi != NULL) 106 { 107 id++; 108 pi = pi -> nextarc; 109 } 110 if (i == 0 ) printf("%d",id); 111 else printf(" %d",id); 112 } 113 puts(""); 114 DeleteLG(); //释放 115 } 116 return 0; 117 }
其实有种简洁且高效的表示形式:
1 typedef struct 2 { 3 int to; 4 int w; 5 int next; 6 }Edge; 7 Edge e[MAX]; 8 int pre[MAX]; 9 10 //初始化 11 memset(pre,-1,sizeof(pre)); 12 13 //输入 14 scanf("%d %d %d",&from,&to,&w1); 15 e[i].to = to; e[i].w = w1; e[i].next = pre[from]; pre[from] = i; 16 i++;
上面这段代码中,边的结构体Edge由三个元素组成:弧头结点序号,边权值,下一条边的序号。e[i]指的是第i条边。pre[i]记录的是从当前输入的情况来看,序号为i的弧尾结点发出的第一条边的序号是pre[i]。
这样,在操作某个结点发出的边时,可以像这么做:
/*now为弧尾结点序号,i为now所发出的边序号,adj为弧头结点序号,w为now-->adj这条边的权值*/ for(i = pre[now]; i != -1; i = edge[i].next) { int adj = edge[i].to; int w = edge [i].w; //do something... }
其实,对于哈希表这类的存储结构(链表法解决冲突),与图的邻接表类似,也可以用类似的表示方法:
1 typedef struct 2 { 3 char e[11]; //value 4 char f[11]; //key 5 int next; //下一个结果(hash冲突) 6 }Entry; 7 Entry entry[M]; 8 int hashIndex[M]; //哈希值为M的结果集的第一个在entry中的序号。 9 10 //输入:对key进行hash, 11 sscanf(str,"%s %s",entry[i].e,entry[i].f); 12 int hash = ELFHash(entry[i].f); 13 entry[i].next = hashIndex[hash]; 14 hashIndex[hash] = i; 15 i++; 16 17 //使用: 18 for(int k = hashIndex[hash]; k; k = entry[k].next) 19 { 20 //do something.. 21 }