刚刚考完,纪念一下,虽说不是考试提交的最终代码,但是是我提前准备的考试源码,供给考试使用。
下面的是单权
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <malloc.h>
/*
* 有向带权图
*/
// 弧的结点结构体定义
typedef struct arcnode
{
int adjvex; // 该弧的终点位置,整型数据,存顶点在表中的位置
struct arcnode *nextarc; // 指向下一条弧的指针
int weight; // 弧的相关信息,用来存放权值
} arcnode;
// 顶点的结点结构体定义
typedef struct vexnode
{
char data; // 顶点信息
struct arcnode *firstarc; // 指向第一条弧的指针
} vexnode;
//typedef vexnode adjlist[20];
typedef struct
{
vexnode adjlist[20]; // 邻接表
int vexnum; // 顶点数
int arcnum; // 边数
} ALGraph;
typedef struct queue
{
int queue[20];
int front;
int rear;
} queue;
后加的
#define MAX 20
#define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)
#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a) (sizeof(a)/sizeof(a[0]))
// 边的结构体
typedef struct _edata
{
char start; // 边的起点
char end; // 边的终点
int weight; // 边的权重
}EData;
// 顶点
static char gVexs[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
// 边//无向图用
static EData gEdges[] = {
// 起点 终点 权
{'A', 'B', 12},
{'A', 'F', 16},
{'A', 'G', 14},
{'B', 'C', 10},
{'B', 'F', 7},
{'C', 'D', 3},
{'C', 'E', 5},
{'C', 'F', 6},
{'D', 'E', 4},
{'E', 'F', 2},
{'E', 'G', 8},
{'F', 'G', 9},
};
// 边//有向图用
static EData gEdges_one[] = {
// 起点 终点 权
{'A', 'B', 12},
{'B', 'C', 10},
{'C', 'E', 5},
{'C', 'F', 6},
{'D', 'C', 3},
{'D', 'E', 4},
{'E', 'G', 8},
{'F', 'A', 16},
{'F', 'B', 7},
{'F', 'E', 2},
{'G', 'A', 14},
{'G', 'F', 9},
};
//
void InitGraph(ALGraph *ALG);
void AddVex(ALGraph *ALG, char vex);
void DisplayVexs(ALGraph *ALG);
void AddArc(ALGraph *ALG, int index1, int index2, int weight);
int GetIndexOfVexs(ALGraph *ALG, char vex);
void DisplayArcs(ALGraph *ALG);
void DeleteVex(ALGraph *ALG, int index);
void DeleteArc(ALGraph *ALG, int index1, int index2);
void DFSTraversal(ALGraph *ALG, int index);
void BFSTraversal(ALGraph *ALG, int index);
void InitVisit(ALGraph *ALG);
void InitQueue(queue *q);
void enqueue(queue *q, int data);
int dequeue(queue *q);
后加的
void create_example_lgraph(ALGraph *ALG);
void link_last(arcnode *list, arcnode *node);
void DFS(ALGraph *ALG);
void BFS(ALGraph *ALG);
void dijkstra(const ALGraph *G, int vs, int prev[], int dist[]);
int get_weight(const ALGraph *G, int start, int end);
void displaypath(const ALGraph *G, int prev[], int start, int end);
void create_example_lgraph_one(ALGraph * ALG);
/
int visit[20];
queue Q;
int main()
{
printf("======================================== MENU =================================================================\n");
printf("= | exit:0 | Init:1 | AddVex:2 | AddArc:3 | DispVexs:4 | DispArcs:5 | DelArc:6 | DelVex:7 | DFS:8 | BFS:9 | dijkstra:9 | =\n");
printf("=====================================================================================================================\n");
int choice;
int index1, index2, weight;
char ch;
int j;
ALGraph G;
int prev[MAX] = { 0 }; //存储前驱结点
int dist[MAX] = { 0 }; //存储某点到各点最短路程
// printf("%d\n", sizeof(ALGraph));
InitGraph(&G);
//create_example_lgraph_one(&G); //有向图例图
//create_example_lgraph(&G); //无向图例图
printf("\nchoice : ");
scanf_s("\t%d", &choice);
while (choice) {
switch (choice) {
case 0: exit(1);
break;
case 1: InitGraph(&G);
printf(">>>初始化完成 !\n");
break;
case 2: // AddVex:2
printf(">>>输入增加的顶点: ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1 == -1)
{
AddVex(&G, ch);
printf(">>>增加顶点成功 !\n");
}
else
printf(">>>图中已有此点\n");
break;
case 3: // AddArc:3
printf(">>>请输入弧尾 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1 != -1)
{
printf(">>>请输入弧头 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index2 = GetIndexOfVexs(&G, ch);
if (index2 != -1)
{
printf(">>>请输入权值 : ");
scanf_s("\t%d", &weight);
AddArc(&G, index1, index2, weight);
AddArc(&G, index2, index1, weight);
printf(">>>增加弧成功 !\n");
}
else
{
printf(">>>图中没有此顶点\n");
}
}
else
{
printf(">>>图中没有此顶点\n");
}
break;
case 4: // DispVexs:4
DisplayVexs(&G);
break;
case 5: // DispAdjMatrix:5
DisplayArcs(&G);
break;
case 6: // DeleteArc
printf(">>>请输入弧尾 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1 != -1)
{
printf(">>>请输入弧头 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index2 = GetIndexOfVexs(&G, ch);
if (index2 != -1)
{
DeleteArc(&G, index1, index2);//删除一条弧
DeleteArc(&G, index2, index1);//删除一条弧
printf(">>>删除弧成功 !\n");
}
else
printf(">>>图中没有此顶点\n");
}
else
printf(">>>图中没有此顶点\n");
break;
case 7: // DeleteVex
printf(">>>输入删除的顶点: ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1 != -1)
{
DeleteVex(&G, index1);
printf(">>>删除顶点成功 !\n");
}
else
printf(">>>图中没有此顶点\n");
break;
case 8: // DFS
InitVisit(&G);
printf(">>>输入起点 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1!=-1)
{
printf("深度优先搜索结果:\t");
do {
DFSTraversal(&G, index1);
for (j = 0; j < G.vexnum; ++j) //可能同时又多个未连接的图,需要寻找
{
if (visit[j] == 0)
{
index1 = j;
break;
}
}
} while (j < G.vexnum);
}
else
printf(">>>图中没有此顶点\n");
printf("\n");
break;
case 9: // BFS
InitVisit(&G);
InitQueue(&Q);
printf(">>>输入起点 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1!=-1)
{
printf("广度优先搜索结果:\t");
do {
BFSTraversal(&G, index1);
for (j = 0; j < G.vexnum; ++j)
{
if (visit[j] == 0)
{
index1 = j;
break;
}
}
} while (j < G.vexnum);
printf("\n");
}
else
printf(">>>图中没有此顶点\n");
break;
case 10:
printf(">>>输入起点 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(&G, ch);
if (index1 != -1)
{
printf(">>>输入终点 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index2 = GetIndexOfVexs(&G, ch);
if (index2 != -1)
{
dijkstra(&G, index1, prev, dist);
displaypath(&G, prev, index1, index2);
}
else
printf(">>>图中没有此顶点\n");
}
else
printf(">>>图中没有此顶点\n");
break;
default:
printf("错误的输入选择!!!\n");
getchar();
}
printf("\nchoice : ");
scanf_s("\t%d", &choice);
}
return 0;
}
//初始化
void InitGraph(ALGraph *ALG)
{
ALG->vexnum = 0;
ALG->arcnum = 0;
}
//在顶点数组中搜索vex的位置下标,从0开始
int GetIndexOfVexs(ALGraph *ALG, char vex)
{
int i;
for (i = 0; i < ALG->vexnum; ++i)
{
if (ALG->adjlist[i].data == vex) return i;
}
return -1;
}
//增加顶点
void AddVex(ALGraph *ALG, char vex)
{
ALG->adjlist[ALG->vexnum].data = vex;
ALG->adjlist[ALG->vexnum].firstarc = NULL;
ALG->vexnum++;
}
//显示顶点
void DisplayVexs(ALGraph *ALG)
{
int i;
printf("\n\t[ ");
for (i = 0; i < ALG->vexnum; ++i)
{
printf("%c ", ALG->adjlist[i].data);
}
printf("]\n");
}
//增加一条弧
//index1弧头(起点),index2弧尾(终点)
void AddArc(ALGraph *ALG, int index1, int index2, int weight)
{
arcnode *p, *q;
q = (arcnode*)malloc(sizeof(arcnode));
q->adjvex = index2;
q->nextarc = NULL;
q->weight = weight;
// 下面的代码,有向图执行一次,无向图要将 index1 换成 index2,再执行一遍;
p = ALG->adjlist[index1].firstarc;
if (p == NULL) //第一种情况,顶点指向没有时,直接加上
{
ALG->adjlist[index1].firstarc = q;
ALG->arcnum++;
}
else
{
while (p)
{
if (p->adjvex == index2)
{
p->weight = weight;
printf(">>>该弧已经存在,无法再增加,进行修改权值\n");
return;
}
else if (p->nextarc) p = p->nextarc; //第二种情况,循环寻找是不是弧链表最后一个
else break; //找到跳出
}
p->nextarc = q;
ALG->arcnum++;
}
//printf(">>>弧的数量为: %d\n", ALG->arcnum);
}
//显示弧
void DisplayArcs(ALGraph *ALG)
{
int i, weight;
char vex1, vex2;
arcnode *p;
for (i = 0; i < ALG->vexnum; ++i) //遍历每个顶点
{
printf(">>>");
p = ALG->adjlist[i].firstarc;
vex1 = ALG->adjlist[i].data;
while (p)
{
vex2 = ALG->adjlist[p->adjvex].data; //找到指向弧头位置的数据
weight = p->weight;
printf("(%c,%c)<W:%d> ", vex1, vex2, weight);
p = p->nextarc;
}
printf("\n"); //调整格式
}
printf("\n>>>弧的数量为: %d\n", ALG->arcnum);
}
//删除顶点的辅助函数:递归调用删除弧结点内容
int DeleteAssist(arcnode *p)
{
if (p)
{
DeleteAssist(p->nextarc);
free(p);
return 1;
}
else
return 0;
}
//删除数组中节点 //删除index位置所有有关的弧和点信息
void DeleteVex(ALGraph *ALG, int index)
{
arcnode *p, *del, *pre = NULL;
int i;
p = ALG->adjlist[index].firstarc;
while (p) //感觉暂时没有用处
{
ALG->arcnum--;
p = p->nextarc;
}
DeleteAssist(ALG->adjlist[index].firstarc); //删除这个顶点储存的弧信息
for (i = index; i < ALG->vexnum - 1; i++) //数组内容移位
{
ALG->adjlist[i].data = ALG->adjlist[i + 1].data;
ALG->adjlist[i].firstarc = ALG->adjlist[i + 1].firstarc; //顶点信息和第一条弧的指针都移位
}
ALG->adjlist[ALG->vexnum - 1].data = -1; //最后一个数组结点初始化掉
ALG->adjlist[ALG->vexnum - 1].firstarc = NULL;
ALG->vexnum--; //顶点个数减1
for (i = 0; i < ALG->vexnum; i++) //删除所有头是index的弧
{
p = ALG->adjlist[i].firstarc;
while (p)
{
if (p->adjvex == index)
{
if (p == ALG->adjlist[i].firstarc)
{
del = p;
p = p->nextarc;
ALG->adjlist[i].firstarc = p;
pre = NULL;
free(del);
}
else
{
del = p;
p = p->nextarc;
pre->nextarc = p;
free(del);
}
ALG->arcnum--; //前面没有删除点的弧时候没有减所以减双倍的
}
if (p != NULL)
{
if (p->adjvex > index)
{
p->adjvex--;
}
}
else
break;
pre = p;
p = p->nextarc;
}
}
}
//删除弧 只删除从index1到index2的弧
void DeleteArc(ALGraph *ALG, int index1, int index2)
{
arcnode *p, *q;
p = ALG->adjlist[index1].firstarc;
if (p->adjvex == index2)//p为头结点的情况
{
q = p;
ALG->adjlist[index1].firstarc = p->nextarc;
free(q);
ALG->arcnum--;//边个数减一
return;
}
//p = ALG->adjlist[index1].firstarc;
while (p->nextarc != NULL)
{
if (p->nextarc->adjvex == index2)
{
q = p->nextarc;
p->nextarc = q->nextarc;
free(q);
break;
}
p = p->nextarc;
}
ALG->arcnum--;//边个数减一
}
//图的深度搜索
void DFSTraversal(ALGraph *ALG, int index)
{
arcnode *p;
visit[index] = 1; //先标记
printf(" --> %c", ALG->adjlist[index].data);
p = ALG->adjlist[index].firstarc; //拿到下一个顶点链表地址
while (p)
{
if (visit[p->adjvex] == 0) DFSTraversal(ALG, p->adjvex); //如果这个顶点没有访问,就进行深度搜索
p = p->nextarc;
}
}
//图的广度搜索
void BFSTraversal(ALGraph *ALG, int index)
{
arcnode *p;
int j;
visit[index] = 1; //先标记
printf(" --> %c", ALG->adjlist[index].data);
p = ALG->adjlist[index].firstarc;
while (p) //每次寻找一层
{
if (visit[p->adjvex] == 0) //没有访问过就入队
{
//printf("\n\np->adjvex=%d\n\n", p->adjvex);
enqueue(&Q, p->adjvex);
visit[p->adjvex] = 1;
}
p = p->nextarc;
}
j = dequeue(&Q); //出队
//printf("\n\nj=%d\n\n", j);
if (j != -1)
{
BFSTraversal(ALG, j); //从新的顶点开始广度搜索
}
}
图的广度搜索//不用递归
//void BFSTraversal(ALGraph *ALG, int index)
//{
// int j, k; //
// arcnode *node = NULL;
//
// if (!visit[index])
// {
// visit[index] = 1;
// printf("--> %c", ALG->adjlist[index].data);
// enqueue(&Q, index);
// }
//
// while (Q.front != Q.rear)
// {
// j = dequeue(&Q); //出队
// node = ALG->adjlist[j].firstarc;
// while (node != NULL)
// {
// k = node->adjvex;
// if (!visit[k])
// {
// visit[k] = 1;
// printf("--> %c", ALG->adjlist[k].data);
// enqueue(&Q, k); //入队
// }
// node = node->nextarc;
// }
// }
// printf("\n");
//}
//初始化访问数组
void InitVisit(ALGraph *ALG)
{
int i;
for (i = 0; i < ALG->vexnum; ++i)
{
visit[i] = 0;
}
}
//初始化队列
void InitQueue(queue *q)
{
q->front = 0;
q->rear = 0;
}
//入队
void enqueue(queue *q, int data)
{
q->queue[q->rear++] = data;
}
//出队
int dequeue(queue *q)
{
if (q->front != q->rear)
{
return q->queue[q->front++];
}
return -1;
}
//将node链接到list的末尾
void link_last(arcnode *list, arcnode *node)
{
arcnode *p = list;
while (p->nextarc)
{
p = p->nextarc;
}
p->nextarc = node;
}
//创造一个例图//是无向图的
void create_example_lgraph(ALGraph * ALG)
{
char c1, c2;
int vlen = LENGTH(gVexs); //点
int alen = LENGTH(gEdges); //边
int i, p1, p2;
int weight;
arcnode *node1, *node2;
InitGraph(ALG); //初始化
//初始化 顶点数 边数
ALG->vexnum = vlen;
ALG->arcnum = alen * 2; //边数是二倍的
//初始化 领接表 的顶点
for (i = 0; i < ALG->vexnum; i++)
{
ALG->adjlist[i].data = gVexs[i];
ALG->adjlist[i].firstarc = NULL;
}
//初始化 领接表 的边
for (i = 0; i < alen; i++)
{
// 读取边的起始顶点,结束顶点,权
c1 = gEdges[i].start;
c2 = gEdges[i].end;
weight = gEdges[i].weight;
p1 = GetIndexOfVexs(ALG, c1);
p2 = GetIndexOfVexs(ALG, c2);
// 初始化node1
node1 = (arcnode*)malloc(sizeof(arcnode));
node1->adjvex = p2;
node1->weight = weight;
node1->nextarc = NULL;
// 将node1链接到"p1所在链表的末尾"
if (ALG->adjlist[p1].firstarc == NULL)
ALG->adjlist[p1].firstarc = node1;
else
link_last(ALG->adjlist[p1].firstarc, node1);
// 初始化node2
node2 = (arcnode*)malloc(sizeof(arcnode));
node2->adjvex = p1;
node2->weight = weight;
node2->nextarc = NULL;
// 将node2链接到"p2所在链表的末尾"
if (ALG->adjlist[p2].firstarc == NULL)
ALG->adjlist[p2].firstarc = node2;
else
link_last(ALG->adjlist[p2].firstarc, node2);
}
}
//创造一个例图//是有向图的
void create_example_lgraph_one(ALGraph * ALG)
{
char c1, c2;
int vlen = LENGTH(gVexs); //点
int alen = LENGTH(gEdges_one); //边
int i, p1, p2;
int weight;
arcnode *node1;
InitGraph(ALG); //初始化
//初始化 顶点数 边数
ALG->vexnum = vlen;
ALG->arcnum = alen; //边数
//初始化 领接表 的顶点
for (i = 0; i < ALG->vexnum; i++)
{
ALG->adjlist[i].data = gVexs[i];
ALG->adjlist[i].firstarc = NULL;
}
//初始化 领接表 的边
for (i = 0; i < alen; i++)
{
// 读取边的起始顶点,结束顶点,权
c1 = gEdges_one[i].start;
c2 = gEdges_one[i].end;
weight = gEdges_one[i].weight;
p1 = GetIndexOfVexs(ALG, c1);
p2 = GetIndexOfVexs(ALG, c2);
// 初始化node1
node1 = (arcnode*)malloc(sizeof(arcnode));
node1->adjvex = p2;
node1->weight = weight;
node1->nextarc = NULL;
// 将node1链接到"p1所在链表的末尾"
if (ALG->adjlist[p1].firstarc == NULL)
ALG->adjlist[p1].firstarc = node1;
else
link_last(ALG->adjlist[p1].firstarc, node1);
初始化node2
//node2 = (arcnode*)malloc(sizeof(arcnode));
//node2->adjvex = p1;
//node2->weight = weight;
//node2->nextarc = NULL;
将node2链接到"p2所在链表的末尾"
//if (ALG->adjlist[p2].firstarc == NULL)
// ALG->adjlist[p2].firstarc = node2;
//else
// link_last(ALG->adjlist[p2].firstarc, node2);
}
}
//深度优先搜索
void DFS(ALGraph *ALG)
{
char ch;
int index1, j;
InitVisit(ALG);
printf(">>>输入起点 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(ALG, ch);
printf("深度优先搜索结果:\t");
do {
DFSTraversal(ALG, index1);
for (j = 0; j < ALG->vexnum; ++j) //可能同时又多个未连接的图,需要寻找
{
if (visit[j] == 0)
{
index1 = j;
break;
}
}
} while (j < ALG->vexnum);
printf("\n");
}
//广度优先搜索
void BFS(ALGraph * ALG)
{
char ch;
int index1, j;
InitQueue(&Q); //初始化队列
InitVisit(ALG); //初始化访问标记数组
printf(">>>输入起点 : ");
scanf_s("\t%c", &ch, sizeof(ch));
index1 = GetIndexOfVexs(ALG, ch);
do {
printf("广度优先搜索结果:\t");
BFSTraversal(ALG, index1);
for (j = 0; j < ALG->vexnum; ++j)
{
if (visit[j] == 0)
{
index1 = j;
break;
}
}
} while (j < ALG->vexnum);
printf("\n");
}
/*
* 获取G中边<start, end>的权值;若start和end不是连通的,则返回无穷大。
*/
int get_weight(const ALGraph *G, int start, int end)
{
arcnode *node;
if (start == end)
return 0;
node = G->adjlist[start].firstarc;
while (node != NULL)
{
if (end == node->adjvex)
return node->weight;
node = node->nextarc;
}
return INF;
}
/*
* Dijkstra最短路径。
* 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。
*
* 参数说明:
* G -- 图
* vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
* prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
* dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
*/
void dijkstra(const ALGraph *G, int vs, int prev[], int dist[])
{
int i, j, k;
int min;
int tmp;
int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
if (vs < 0 || vs >= G->vexnum) //
{
return;
}
// 初始化
for (i = 0; i < G->vexnum; i++)
{
flag[i] = 0; // 顶点i的最短路径还没获取到。
prev[i] = vs; // 顶点i的前驱顶点为0。
dist[i] = get_weight(G, vs, i); // 顶点i的最短路径为"顶点vs"到"顶点i"的权。
}
// 对"顶点vs"自身进行初始化
flag[vs] = 1;
dist[vs] = 0;
// 遍历G->vexnum-1次;每次找出一个顶点的最短路径。
for (i = 1; i < G->vexnum; i++)
{
// 寻找当前最小的路径;
// 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
min = INF;
for (j = 0; j < G->vexnum; j++)
{
if (flag[j] == 0 && dist[j] < min)
{
min = dist[j];
k = j;
}
}
// 标记"顶点k"为已经获取到最短路径
flag[k] = 1;
// 修正当前最短路径和前驱顶点
// 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
for (j = 0; j < G->vexnum; j++)
{
tmp = get_weight(G, k, j);
tmp = (tmp == INF ? INF : (min + tmp)); // 防止溢出
if (flag[j] == 0 && (tmp < dist[j]))
{
dist[j] = tmp;
prev[j] = k;
}
}
}
// 打印dijkstra最短路径的结果
printf("最短路径(%c): \n", G->adjlist[vs].data);
for (i = 0; i < G->vexnum; i++)
printf(" 最短路程(%c, %c)=%d\n", G->adjlist[vs].data, G->adjlist[i].data, dist[i]);
printf("\n");
}
//打印某点到另一点路径
void displaypath(const ALGraph *G, int prev[], int start, int end)
{
//用来输出所有路径 //可以考虑用栈
char* str = NULL;
int pos; //用来标记位置
int m = 0; //m用来计数
if ((str = (char*)malloc(sizeof(char)*G->vexnum)) == NULL) //用来存储输出字符
return;
pos = prev[end]; //pos最先指向end的前驱结点位置
for (m = 0; m < G->vexnum; m++) //循环寻找前驱结点
{
str[m] = G->adjlist[pos].data;
if (str[m] == G->adjlist[start].data) //如果是出发点,就不用再找了
{
break;
}
pos = prev[pos]; //寻找下一个位置
}
for (int j = m; j >= 0; j--)
{
printf("%c-> ", str[j]);
}
printf("%c\n", G->adjlist[end].data);
}
[这是双权](https://blog.csdn.net/qq_43774943/article/details/106939132)