Too easy...
只关心节点的出度,链表里的是以 i 指向的节点
只关心节点的入度,链表里的是指向 i 的节点
尤适用于有向图
头节点
data firstIn firstOut 一般节点
head tail 行链接 列链接
适用于无向图,解决了空间的浪费,方便了遍历,插入,删除
m v1 v2 link1 link2 每条边只有一个节点表示,但这个节点同时在两个链表里
m:标记是否被检查过
link1: 对应 v1相关的边
link2: 对应 v2相关的边
link 时就近原则就不会乱
//创建有向图的十字链表
int CreatDG(OLGraph &G, int vexnum, int arcnum)
{
for (int i = 0; i < vexnum; i++) {
printf("Please enter %d data:", i+1);
scanf(" %c", &G.xlist[i].data);
//输入顶点值
G.xlist[i].nodenum = i;
//记录顶点的位置
G.xlist[i].firstin = NULL;
//初始化指针
G.xlist[i].firstout = NULL;
}
char v1, v2;
//一条弧的两个顶点
int i, j;
//弧尾位置,弧头位置
for (int k = 0; k < G.arcnum; k++) {
printf("Please enter %d arc(tail -> head): ", k + 1);
scanf_s(" %c", &v1);
//输入弧的第一个顶点,这里%c前面必须要有一个空格!
getchar();
//把输入的空格读走
v2 = getchar();
//输入弧的第二个顶点
i = LocateVex(G, v1);
//获取弧的第一个节点位置
j = LocateVex(G, v2);
//获取弧的第二个节点位置
ArcBox *p = (ArcBox *)malloc(sizeof(ArcBox));
//分配弧结点空间
p->tailvex = i; p->tlink = G.xlist[i].firstout;
//i放进tailvex,tlink(尾巴出去firstout)
p->headvex = j; p->hlink = G.xlist[j].firstin;
//j放进headvex,hlink(头进来firstin)
G.xlist[j].firstin = G.xlist[i].firstout = p;
//入弧与出弧的插入(数组的firstin firstout有了指向)
}
return 0;
}
尤指编个小程序(十字链表)
/******************************************************
** DFS深度优先遍历
*******************************************************/
bool DFSvisted[20];
//这里定义最多有20个结点
void DFSTraverse(OLGraph &G)
{
for (int i = 0; i < G.vexnum; i++)
//先把每个结点都标记为假,没有访问过
DFSvisted[i] = false;
for (int i = 0; i < G.vexnum; i++) {
if (!DFSvisted[i])
//对没有访问的结点,按深度优先遍历
DFS(G, i);
}
}
void DFS(OLGraph &G, int v)
{
DFSvisted[v] = true;
//访问结点标识--真
printf("%c\t",G.xlist[v].data);
ArcBox *DFStemp = G.xlist[v].firstout;
//用DFStemp来存储该节点的第一个出去的弧
while (DFStemp)
{
if (!DFSvisted[DFStemp->headvex])
//访问此结点第一个出去的弧
DFS(G, DFStemp->headvex);
DFStemp = DFStemp->tlink;
//该结点还有指向其他的弧
}
}
/******************************************************
** BFS广度优先遍历
*******************************************************/
bool BFSvisted[20];
//这里定义最多有20个结点
void BFSTraverse(OLGraph &G)
{
for (int i = 0; i < G.vexnum; i++)
//先把每个结点都标记为假,没有访问过
BFSvisted[i] = false;
LinkQueue Q;
//初始化队列
InitQueue(Q);
ArcBox *BFStemp;
char HeadQueue;
//出队元素(队头)第一次会出现一进队就出队
int headnum = 0;
//队头元素的位置,才好找到十字链表的下一个
for (int i = 0; i < G.vexnum; i++) {
if (!BFSvisted[i]) {
BFSvisted[i] = true;
printf("%c\t", G.xlist[i].data);
//访问元素
EnQueue(Q, G.xlist[i].data);
//把访问的元素都入队
while (!QueueEmpty(Q)) {
DeQueue(Q, HeadQueue);
//出队
//printf("出队:%c\t", DeQueue(Q, HeadQueue));
for (int j = 0; j < G.vexnum; j++) {
if (G.xlist[j].data == HeadQueue)
headnum = G.xlist[j].nodenum;
//获取队头位置
}
BFStemp = G.xlist[headnum].firstout;
//用BFStemp来存储该节点的第一个出去的弧
while (BFStemp) {
if (!BFSvisted[BFStemp->headvex]) {
BFSvisted[BFStemp->headvex] = true;
//把结点都标记为真
printf("%c\t", G.xlist[BFStemp->headvex].data);
//访问元素
EnQueue(Q, G.xlist[BFStemp->headvex].data);
//把访问的元素都入队
}
BFStemp = BFStemp->tlink;
}
}
}
}
迪杰斯特拉算法Dijkstra
- 贪婪算法的计算过程,证明, 举例哪些情形下不好用
- 约束条件: 没有负边
- 以类似于广搜解决了单源任意目的地最短路径问题
- 复杂度
算法 | 最坏时间复杂度 |
---|---|
使用邻接表的戴克斯特拉算法 | |
使用二叉堆优化的戴克斯特拉算法 | |
使用斐波那契堆优化的戴克斯特拉算法 |
- 算法
- 用一个数组 L 来标记是否为最短路径
- 用 dist 数组存储路径
- 每次遍历 dist 数组找出最短路径,标记它,并以它对未标记点做松弛操作
- code
// n is the num of vertexs, v is the algorithm's start vertex
void dijkstra(int n, int v)
{
for(int i = 0; i < n; i++)
{
L[i] = false;
dist[i] = length[v][i];
}
//for lenght[v][v] = 0; so dist[v]=0 must be the shortest one
L[v] = true;
for(int i = 0; i < n-2; i++)
{
int u = choose(dist);//dist[u] is the shortest in dist except those mark as true
L[u] = true;
for(int w = 0; w < n; w++)
{
if(!L[w]&&dist[u]+length[u][w]<dist[w])
{
dist[w] = dist[u]+length[u][w];
}
}
}
}
int choose(int* dist)
{
int k=0;
for(int j=1;j<=n;++j)//找出距离最近的点
{
if(!v[j]&&(k==0||dis[j]<dis[k]))
k=j;
}
return k;
}
Activity on Edge network(用边来表示活动,顶点表示事件即任务完成的标志) 两个表,每个事件开始的最早最迟时间
- 关键路径: 工程完成所需最短时间是最长路径的长度,最长路径叫做关键路径
- 事件 i 最早发生时间: 起点 0 到顶点 i 的最长路径长度
- 事件 i 最迟开始时间: 不延长工程持续时间的前提下,每个活动 ai最迟开始时间
- 关键活动: e(i) = l(i)的活动
- 拓扑排序: 若 i 是 j 的前驱,则在这种排序里 i 一定在 j 前
按拓扑顺序先计算时间的最早最迟时间,然后计算活动的最早最迟时间,从而确定关键活动
-
向前步骤计算事件最早发生事件 ee[j]
ee[0] = 0; ee[j] = max{ee[i]+<i,j>}
-
向后步骤计算事件最迟发生时间 le[j]
le[n-1] = ee[n-1]; le[j] = min{le[j]-<i,j>}
-
计算活动的最早最迟时间 若 ai用边<k,l>表示,则
i | ee[i] | le[i] |
---|---|---|
0 | 0 | 0 |
1 | 5 | 9 |
2 | 6 | 6 |
3 | 12 | 12 |
4 | 15 | 15 |
5 | 16 | 19 |
6 | 16 | 16 |
7 | 19 | 19 |
8 | 21 | 21 |
9 | 23 | 23 |
eg:
ai | e[i] | l[i] |
---|---|---|
1 | 0 | 4 |
2 | 0 | 0 |
3 | 5 | 9 |
4 | 6 | 6 |
5 | 6 | 12 |
6 | 12 | 12 |
7 | 12 | 15 |
8 | 12 | 12 |
9 | 15 | 15 |
10 | 15 | 15 |
11 | 16 | 19 |
12 | 16 | 16 |
13 | 19 | 19 |
14 | 21 | 21 |
-
关键活动: a2a4a6a8a9a10a12a13a14
-
关键路径
- 2-4-8-12-14
- 2-4-6-9-12-14
- 2-4-6-10-13-14
-
加速工程: 2,4,14 这三个在所有的关键路径上,所以要缩短他们才能加速工程