前言:
本系列是笔者暑假自学数据结构的笔记整理而来,共126页,3w+字。现在正式开学上课,补充老师所讲内容,并且仔细勘误,根据老师的教学进度分章节发布在CSDN上。
教材使用的是王红梅等所著的数据结构——从概念到C++实现(第三版)。
暑假自学时期主要参考的网课:
青岛大学-王卓
武汉大学-李春葆
隔了好久,补发。
定义与概念
图的存储结构
/*
图的基本运算
InitGraph(&g):图初始化
ClearGraph(&g):销毁图
DFS(G, V):从顶点v出发深度优先遍历
BFS(G, v):从顶点v出发广度优先遍历
*/
#define MAXV 100//最大顶点个数
#define InfoType int
typedef struct
{
int no;//顶点编号
InfoType info;//顶点其他信息
}VertexType;
typedef struct //图的定义
{
int edge[MAXV][MAXV];//邻接矩阵
int n, e;//顶点数边数
VertexType vexs[MAXV];//存放顶点信息
}MGraph;
//图的邻接表存储类型定义如下:
typedef struct ANode
{
int adjvect;//该边的终点编号
struct ANode *nextare;//指向下一条边的指针
InfoType info;//该边的权值等信息
}ArcNode;
typedef struct Vnode
{
VertexType data;//顶点信息
ArcNode *firstarc;//指向第一条便
}VNode;
typedef struct
{
VNode adjlist[MAXV];//邻接表
int n, e;//图中顶点数n和边数e
}ALGraph;
图的遍历
//DFS,时间复杂度O(n+e)
void DFS(ALGraph *G, int v)
{
ArcNode *p;
int w;
int visited[MAXV];
visited[v] = 1;//用visited数组表示节点是否被访问过
cout << v;//输出被访问顶点的编号
p = G->adjlist[v].firstarc;//p指向顶点v的第一条边的边头节点
while (p != nullptr)
{
w = p->adjvect;
if (visited[w] == 0)
{
DFS(G, w);//若w顶点未访问,递归访问它
}
p = p->nextare;//p指向顶点v的下一条边的边头节点
}
}
//BFS,时间复杂度为O(n+e)
void BFS(ALGraph *G, int v)
{
ArcNode *p;
int w, i;
int queue[MAXV], front = 0, rear = 0;//定义循环队列
int visited[MAXV];
for (int i = 0; i < G->n; i++)
{
visited[i] = 0;//访问标志数组初始化
}
cout << v;//输出被访问顶点的编号
visited[v] = 1;//置已被访问顶点的编号
rear = (rear + 1)%MAXV;
queue[rear] = v;//v进队
while (front != rear)//队列不空
{
front = (front + 1)%MAXV;
w = queue[front];//出队并赋给w
p = G->adjlist[w].firstarc;//找w的第一个邻接点
while (p != nullptr)
{
if (visited[p->adjvect] == 0)
{
cout << p->adjvect;//访问之
visited[p->adjvect] = 1;
rear = (rear + 1)%MAXV;//相邻顶点进队
queue[rear] = p->adjvect;
}
p = p->nextare;//找下一个邻接顶点
}
}
}
void DFS1(ALGraph *G)
{
int i;
for (int i = 0; i < G->n; i++)
{
if (visited[i] == 0)
{
DFS(G, i);
}
}
}
void BFS1(ALGraph *G)
{
int i;
for (int i = 0; i < G->n; i++)
{
if (visited[i] == 0)
{
BFS(G, i)
}
}
}
int visited[MAXV];
bool Connect(ALGraph *G)//判断无向图G的连通性
{
int i;
bool flag = true;
for (int i = 0; i < G->n; i++)
{
visited[i] = 0;//visited[]数组置初值
}
DFS(G, 0);//从顶点0开始深度优先遍历
for (int i = 0; i < G->n; i++)
{
if (visited[i] == 0)
{
flag = false;
break;
}
}
return flag;
}
void ExistPath(ALGraph *G, int u, int v, bool &has)
{
//has表示u到v是否有路径
int w;
ArcNode *p;
visited[u] = 1;//置已访问标记
if (u == v)//找到一条路径
{
has = true;//置has为true并结束算法
return;
}
p = G->adjlist[u].firstarc;//p指向顶点u的第一个相邻点
while (p != nullptr)
{
w = p->adjvect;//w为顶点u的相邻顶点
if (visited[w] == 0)//若w顶点未访问,递归访问它
{
ExistPath(G, w, v, has);
}
p = p->nextare;//p指向顶点u的下一个相邻点
}
}
void FindaPath(ALGraph *G, int u, int v, int path[], int d)
{
//d表示path中的路径长度,初值为-1
int w, i;
ArcNode *p;
visited[u] = 1;
d++;//路径长度d增1,顶点u加入到路径中
path[d] = u;
if (u == v)//找到一条路径后输出并返回
{
cout << "一条简单路径为:";
for (int i = 0; i <= d; i++)
{
cout << path[i];
}
cout << '\n';
return;//找到一条路径后返回
}
p = G->adjlist[u].firstarc;//p指向顶点u的第一个相邻点
while (p != nullptr)
{
w = p->adjvect;//相邻点的编号为w
if (visited[w] == 0)
{
FindaPath(G, w, v, path, d);
}
p = p->nextare;//p指向顶点u的下一个相邻点
}
}
void PathAll(ALGraph *G, int u, int v, int l, int path[], int d)
{
//d是到当前为止已走过的路径长度,调用时初值为-1
int w, i;
ArcNode *p;
visited[u] = 1;//路径长度增1
d++;
path[d] = u;//将当前顶点添加到路径中
if (u == v && d==l)//输出一条路径
{
for (int i = 0; i <= d; i++)
{
cout << path[i] << '\n';
}
}
p = G->adjlist[u].firstarc;//p指向u的第一条边的边头节点
while (p != nullptr)
{
w = p->adjvect;//w为u的邻接顶点
if (visited[w] == 0)//若顶点未标记访问,则递归访问之
{
PathAll(G, w, v, l, path, d)
}
p = p->nextare;//找u的下一个邻接顶点
}
visited[u] = 0; //恢复环境,使该顶点可重新使用
}
typedef struct
{
int data;//顶点编号
int parent;//前一个顶点的位置
}Quere;
void ShortPath(ALGraph *G, int u, int v)
{
//输出从顶点u到顶点v的最短逆路径
ArcNode *p;
int w, i;
Quere qu[MAXV];//定义非循环队列
int front = -1, rear = -1;//队列的头、尾指针
int visited[MAXV];
for (int i = 0; i < G->n; i++)//访问标记置初值0
{
visited[i] = 0;
}
rear++;//顶点u进队
qu[rear].data = u;
qu[rear].parent = -1;
visited[u] = 1;
while (front != rear)//队不空时循环
{
front++;//出队顶点w
w = qu[front].data;
if (w == v)//输出逆路径
{
i = front;
while (qu[i].parent != -1)
{
cout << qu[i].data;
i = qu[i].parent;
}
cout << qu[i].data;
break;
}
p = G->adjlist[w].firstarc;//找w的第一个邻接点
while (p != nullptr)
{
if (visited[p->adjvect] == 0)
{
visited[p->adjvect] = 1;
rear++;//将w的未访问过的邻接点进队
qu[rear].data = p->adjvect;
qu[rear].parent = front;
}
p = p->nextare;//找w的下一个邻接点
}
}
}
生成树
最小生成树
#define INF 32767
void Prim(MGraph g, int v)
{
int lowcost[MAXV];
int min;
int closet[MAXV], i, j, k;
for (int i = 0; i < g.n; i++)
{
lowcost[i] = g.edge[v][i];
closet[i] = v;
}
for (int i = 1; i < g.n; i++)
{
min = INF;
for (int j = 0; j < g.n; j++)
{
if (lowcast[j] != 0 && lowcost[j] < min)
{
min = lowcost[j];
k = j;
}
}
cout << closet[k] << k << min;
lowcast[k] = 0;
for (int j = 0; j < g.n; j++)
{
if (lowcast[j] != 0 && g.edges[k][j] < lowcast[j])
{
lowcost[j] = g.edge[k][j];
closet[j] = k;
}
}
}
}
typedef struct
{
int u;//边的起始顶点
int v;//边的终止顶点
int w;//边的权值
}Edge;
void Kruskal(MGraph g)
{
int i, j, u1, v1, sn1, sn2, k;
int vset[MAXV];
Edge E[MAXV];//存放所在边
k = 0;//E数组的下标从0开始计
for (int i = 0; i < g.n; i++)//由g产生的边集E
{
for (int j = 0; j < g.n; j++)
{
if (g.edge[i][j] != 0 && g.edge[i][j] != INF)
{
E[k].u = i;
E[k].v = j;
E[k].w = g.edge[i][j];
k++;
}
}
}
InsertSort(E, g, e);//用直接插入排序对E数组按权值递增排序
for (int i = 0; i < g.n; i++)//初始化辅助数组
{
vset[i] = i;
k = 1;//k表示当前构造生成树的第几条边
j = 0;//E中边的下标,初值为0
while (k < g.n)//生成的边数小于n时循环
{
u1 = E[j].u;//取一条边的头尾顶点
v1 = E[j].v;
sn1 = vset[u1];
sn2 = vset[v1];//分别得到两个顶点所属的集合编号
if (sn1 != sn2)//两顶点属于不同的集合
{
cout << u1 << v1 << E[j].w;
k++;//生成边数增1
for (int i = 0; i < g.n; i++)//两个集合统一编号
{
if (vset[i] == sn2)//集合编号为sn2的改为sn1
{
vset[i] = sn1;
}
}
}
j++;//扫描下一条边
}
}
}
最短路径
void Dijkstra(MGraph g, int v)
{
int dist[MAXV], path[MAXV];
int s[MAXV];
int mindis, i, j, u;
for (int i = 0; i < g.n; i++)
{
dist[i] = g.edge[v][i];//距离初始化
s[i] = 0;//s[]置空
if (g.edge[v][i] < INF)//路径初始化
{
path[i] = v;//顶点v到i有边时
}
else
{
path[i] = -1;//顶点v到i没边时
}
}
s[v] = 1;//源点v放入s中
for (int i = 0; i < g.n; i++)//循环n-1次
{
mindis = INF;
for (int j = 0; j < g.n; j++)
{
if (s[j] == 0 && dist[j] < mindis)
{
u = j;
mindis = dist[j];
}
}
s[u] = 1;//顶点u放入s中
for (int j = 0; j < g.n; j++)//修改不在s中的顶点的距离
{
if (s[j] == 0)
{
if (g.edge[u][j] < INF && dist[u] + g.edge[u][j] < dist[j])
{
dist[j] = dist[u] + g.edge[u][j];
path[j] = u;
}
}
}
}
Dispath(dist, path, s, g.n, v);//输出最短路径
}
void Floyd(MGraph g)//求每对顶点之间的最短路径
{
int A[MAXV][MAXV];//建立数组A
int path[MAXV][MAXV];//建立path数组
int i, j, k;
for (int i = 0; i < g.n; i++)
{
for (int j = 0; j < g.n; j++)
{
A[i][j] = g.edge[i][j];
if (i != j && g.edge[i][j] < INF)
{
path[i][j] = i;//i和j顶点之间有一条边时
}
else
{
path[i][j] = -1;//i和j顶点之间没有一条边时
}
}
}
for(k = 0; k < g.n; k++)//求Ak[i][j]
{
for(i = 0; i < g.n; i++)
{
for(j = 0; j < g.n; j++)
{
if (A[i][j] > A[i][k] + A[i][j])//找到更短路径
{
A[i][j] = A[i][k] + A[i][j];//修改路径长度
path[i][j] = path[k][j];//修改最短路径为经过顶点k
}
}
}
}
}
typedef struct//表头节点类型
{
VertexType data;//顶点信息
int count;//存放顶点入度
ArcNode *firstarc;//指向第一条边
}VNode;
void TopSort(Vnode adj[], int n)
{
int i, j;
int St[MAXV], top = -1;//栈的指针为top
ArcNode *p;
for (int i = 0; i < n; i++)
{
if (adj[i].count == 0)//入度为0的顶点进栈
{
top++;
St[top] = i;
}
}
while (top > -1)//栈不空时循环
{
i = St[top];//出栈
top--;
cout << i;
p = adj[i].firstarc;
while (p != nullptr)
{
j = p->adjvect;
adj[j].count--;
if (adj[j].count == 0)
{
top++;
St[top] = j;
}
p = p->nextare;//找下一个相邻顶点
}
}
}
AOE网和关键路径
以上 如果此篇博客对您有帮助欢迎点赞与转发 有疑问请留言或私信 2021/1/12