概述
数据结构:是计算机存储和组织数据的方式,是相互间存在着一种或多种关系的数据元素的集合和该集合中数据元素的关系。记作Data_Structure(D,R)
D: 是数据元素的集合 R: 是该集合中所有元素之间的关系的有限集合。
数据结构的研究对象
(一) 数据的逻辑结构: 反应数据元素之间的前后件关系,与计算机的存储位置无关。
1)集合:数据结构中的元素之间除了"同属一个集合”的相互关系外,别无其余关系。
2)线性结构:数据结构中的元素存在 一对一 的关系。
3)树形结构:数据结构中的元素存在 一对多 的关系。
4)图结构:数据结构中的元素存在 多对多 的关系。
(二) 数据的物理结构: 是指数据在计算机存储空间的存放形式,一种逻辑结构可以表示成多种存储结构。
(三)数据结构的运算:
(1)Creat: 建立一个数据结构
(2)Destroy:消除一个数据结构
(3)Delete:从一个数据结构中删除一个数据元素
(4)Insert:把一个数据元素插入到一个数据结构中
(5)Access:对一个数据结构进行访问
(6)Modify:对一个数据结构中的数据元素进行修改
(7)Sort:对一个数据元素中的数据元素进行排序
(8)Search:对一个数据元素中数据元素进行查找
线性表
a[ 0 … n] 构成的一个表,长度为 n, 若 n=0,该表是空表
1)当 i = 1 ..... n-1, a[ i ] 有且仅有一个直接前驱 a[ i -1 ]。
2)当 i = 0 ..... n-2, a[ i ] 有且仅有一个直接后继a[ i + 1 ]。
3)第一个元素没有前驱。
4)最后一个元素没有后继。
栈与队列
(1) 栈:遵循先进后出的原则,只允许在栈顶对元素进行插入和删除的操作,另一
端就是栈底。
(2) 队列:先进先出表 当 front == rear 表示队空
队头 ( front ) 进行删除操作 , 队尾 ( rear ) 端进行删除操作。
二叉树
一种有限元素的集合,该集合或者为空,或者由一个称为 根 的元素及两个不相交的
左子树和右子树组成。
先序遍历
void PreOrderTraverse(BNode *p)
{
if(p!=NULL) //若二叉树为空,则空操作
{
printf("%c*",p->data); //访问根节点
PreOrderTraverse(p->lchild); //先序遍历左子树
PreOrderTraverse(p->rchild); //先序遍历右子树
}
}
中序遍历
void InOrderTraverse(BNode *p)
{
if(p!=NULL)
{
InOrderTraverse(p->lchild); //中序遍历左子树
printf("%c*",p->data); //访问根节点
InOrderTraverse(P->rchild); //中序遍历右子树
}
}
后序遍历
void PostOrderTraverse(BNode *p)
{
if(p!=NULL)
{
PostOrderTraverse(p->lchild); //后序遍历左子树
PostOrderTraverse(p->rchild); //后序遍历右子树
printf("%c*",p->data); //访问根节点
}
}
图
图的遍历:从图中的某个顶点出发访遍图中其余顶点且仅访问一次的过程。
深度优先遍历:从图中某个点出发,然后访问完图中与这个点路径相通的点, 若图
中还有未被访问的点,从未被访问的点开始重复上述步骤。(一条道走到黑)
void DFS(Graph G, int v)
{
//从顶点 v 出发递归地深度优先遍历图 G
visited[v] = TRUE; Visit(v); //访问顶点 v
for(w = FirstAdjVex(G,v); w; w = NextAjVex(G,V,W))
if(!visited[w]) DFS(G,w); //对 v 的尚未访问的邻接顶点 w 递归地深度优先遍历
}
广度优先遍历:从图中某个点出发,然后先访问完图中与这个点路径相通最近的点,
然后再从这些最近的点访问与这些点最近的子节点,若图中还有未被访问的点,从未
被访问的点开始重复上述步骤。(分身术)
void BFSTraverse(Graph G, Status(*Visited)(int v))
{
//按广度优先递归遍历图 G 使用辅助队列 Q 和访问标志数组 visited
for(v = 0; v < G, vexnum; ++v)
Visited[v] = FALSE;
InitQueue(Q); //置空 队列 Q
if(!visited[v]) //尚未访问
{
EnQueue(Q,v); //v 入队列
while(!QueueEmpty(Q))
{
DeQueue(Q,u); //队头元素出队并置为u
Visited[u] = FALSE;visit(u); //访问 u
for(w = FirstAdjVex(G,u); w; w = NextAdjVex(G,u,w))
if(!Visited[w]) EnQueue(Q,w); //u 的尚未访问的邻接点 w 入队列 Q
}
}
}
最短路径
源点与终点之间经过的边上权值之和最小的路径。
D i j k s t r a Dijkstra Dijkstra(没有负权的单源最短路径)
步骤一:初始化时令 S = v 0 S = { v_0 } S=v0, T = V − S = T = V - S = T=V−S= { 其 余 顶 点 } \{ 其余顶点 \} {
其余顶点}, S S S 代表已经走过的顶点,设 d ( v , u ) d(v,u) d(v,u) 代表顶点 v v v 到 u u u 的边权值。
<1> 若存在 < v0, vi>,d(v0,vi)为<v0,vi>弧上的权值
<2> 若不存在<v0,vi>,d(v0,vi)为 ∞
步骤二:从 T T T 中选取一个与 S S S 中顶点有关联边且权值小的顶点 w w w,加入到 S S S 中。
步骤三:对 T T T 中其余顶点的距离值进行修改 d ( v 0 , v i ) = m i n ( d ( v 0 , v i ) , d ( v 0 , w ) + d ( w , v i ) ) d(v_0,v_i) = min(d(v_0,v_i),d(v_0,w)+d(w,v_i)) d(v0,vi)=min(d(v0,vi),d(v0,w)+d(w,vi))若加进 w w w 作中间顶点,从 v 0 v_0 v0 到 v i v_i vi 的距离值缩短,则修改此距离。
步骤四:重复上述步骤 2、3,直到 S S S 中 包含所有顶点,即 w = v i w = v_i w=vi 为止。
#include <iostream>
#include <cstdio>
#define MAX 1000000
using namespace std;
int arcs[10][10]; //邻接矩阵
int D[10]; //保存最短路径长度
int p[10][10]; //路径
int final[10]; //若final[i] = 1; 则说明 v[i] 已在集合 S中
int n = 0; //顶点个数
int v0 = 0; //顶点
int v,w;
void Dijkstra()
{
for(v = 0; v < n; v++) //循环初始化
{
final[v] = 0; D[v] = arcs[v0][v];
for(w = 0; w < n; w++) p[v][w] = 0; //设空路径
if(D[v] < MAX)
{
p[v][v0] = 1;
p[v][v] = 1;
}
}
D[v0] = 0; final[v0] = 0; //初始化 v0 顶点属于集合 S
//开始1主循环,每次求得 v0 到某个顶点 v 的最短路径,并将 v 加到 集合 S 中
for(int i = 1; i < n; i++)
{
int min = MAX;
for(w = 0; w < n; w++)
{
if(!final[w]) //如果顶点 w 在 v - s 中
{
if(D[w] < min)
{
//这个过程最终选出的点应当是当前 v-s 中与 s 有关联边且权值最小的顶点
v = w;
min = D[w];
}
}
}
final[v] = 1; //选出该点后加入到集合 s 中
for(w = 0; w < n; w++) //更新当前最短路径和距离
{
/***
在此循环中, v 为当前刚选入集合 s 中的点,则以 v 为中间点 考察 d(v0,v) + d(v,w)
是否小于D[w],如果是,则更新。
***/
if(!final[w] && (min + arcs[v][w] < D[w]))
{
D[w] = min + arcs[v][w];
p[w][w] = 1;
}
}
}
}
int main()
{
cin>>n;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
cin>>arcs[i][j];
Dijkstra();
for(int i = 0; i < n; i++)
printf("D[%d]=%d\n",i,D[i]);
return 0;
}
F l o y e d Floyed Floyed( 多源最短路径)
步骤一:初始化最短路径。两点之间的距离时边的权,如果两点之间没有边相连,则
权为无穷大。
步骤二:对于没一对顶点 u,v ,检查是否存在一个顶点 w 使得从 u 到 w 再到 v
比已知的路径更短。如果是,则更新最短路径。
步骤三:把图用邻接矩阵 G 表示出来。如果 vi 到 vj 有路可达,则G[ i ][ j ]
= d, d 表示该路的长度;否则G[ i ][ j ] = 无穷大。 定义一个 矩阵 D 用来记
录所插入点的信息,D[ i ][ j ] 表示从 vi 到 vj 需要经过路上的点,初始化
D[ i ][ j ] = j。把各个顶点插入 图中,比较插入顶点后 的距离 与原来的距离,
G[ i ][ j ] = min(G[ i ][ j ],G[ i ][ k ] + G[ k ][ j ]),如果
G[ i ][ j ] 的值变小,则更新D[ i ][ j ] = k。在 G 中包含两点之间的最短路
径的长度,在D中包含两点之间最短路径经过的点。
#include <iostream>
#include <cstdio>
#define max 1000000000
using namespace std;
int d[1000][1000], path[1000][1000];
int main()
{
int i,j,k,m,n;
int x,y,z;
scanf("%d%d",&n,&m);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
d[i][j] = max;
path[i][j] = j;
}
}
for(i = 1; i <= m; i++)
{
scanf("%d%d%d",&x,&y,&z);
d[x][y] = z;
d[y][x] = z;
}
for(k = 1; k <= n; k++)
for(i = 1; i <= n; i++)
for(j = 1; j <= n; j++)
{
if(d[i][k] + d[k][j] < d[i][j])
{
d[i][j] = d[i][k] + d[k][j];
path[i][j] = path[i][k];
}
}
for(i = 1; i <= n; i++)
for(j = 1; j <= n; j++)
if(i != j) printf("%d->%d:%d\n",i,j,d[i][j]);
int st,en;
scanf("%d%d",&st,&en);
while(st != en)
{
printf("%d->",st);
st = path[st][en];
}
printf("%d\n",en);
return 0;
}