一:概述:
数据结构:是计算机存储和组织数据的方式,是相互间存在着一种或多种关系的数据元素的集合和该集合中
数据元素的关系。记作: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 ) 端进行删除操作。
五:二叉树:一种有限元素的集合,该集合或者为空,或者由一个称为 根 的元素及两个不相交的
左子树和右子树组成。
(1) 先序遍历:
1 void PreOrderTraverse(BNode *p) 2 { 3 if(p!=NULL) //若二叉树为空,则空操作 4 { 5 printf("%c*",p->data); //访问根节点 6 PreOrderTraverse(p->lchild); //先序遍历左子树 7 PreOrderTraverse(p->rchild); //先序遍历右子树 8 } 9 }
(2)中序遍历:
1 void InOrderTraverse(BNode *p) 2 { 3 if(p!=NULL) 4 { 5 InOrderTraverse(p->lchild); //中序遍历左子树 6 printf("%c*",p->data); //访问根节点 7 InOrderTraverse(P->rchild); //中序遍历右子树 8 } 9 }
(3)后序遍历:
void PostOrderTraverse(BNode *p) { if(p!=NULL) { PostOrderTraverse(p->lchild); //后序遍历左子树 PostOrderTraverse(p->rchild); //后序遍历右子树 printf("%c*",p->data); //访问根节点 } }
(六)图:
图的遍历:从图中的某个顶点出发访遍图中其余顶点且仅访问一次的过程。
(1)深度优先遍历:从图中某个点出发,然后访问完图中与这个点路径相通的点, 若图中还有未被
访问的点,从未被访问的点开始重复上述步骤。(一条道走到黑)
1 void DFS(Graph G, int v) 2 {//从顶点 v 出发递归地深度优先遍历图 G 3 visited[v] = TRUE; Visit(v); //访问顶点 v 4 for(w = FirstAdjVex(G,v); w; w = NextAjVex(G,V,W)) 5 if(!visited[w]) DFS(G,w); //对 v 的尚未访问的邻接顶点 w 递归地深度优先遍历 6 }
(2)广度优先遍历:从图中某个点出发,然后先访问完图中与这个点路径相通最近的点,然后再从这些最
近的点访问与这些点最近的子节点,若图中还有未被访问的点,从未被访问的点开始重复上述步骤。(分身术)
1 void BFSTraverse(Graph G, Status(*Visited)(int v)) 2 {//按广度优先递归遍历图 G 使用辅助队列 Q 和访问标志数组 visited 3 for(v = 0; v < G, vexnum; ++v) 4 Visited[v] = FALSE; 5 InitQueue(Q); //置空 队列 Q 6 if(!visited[v]) //尚未访问 7 { 8 EnQueue(Q,v); //v 入队列 9 while(!QueueEmpty(Q)) 10 { 11 DeQueue(Q,u); //队头元素出队并置为u 12 Visited[u] = FALSE;visit(u); //访问 u 13 for(w = FirstAdjVex(G,u); w; w = NextAdjVex(G,u,w)) 14 if(!Visited[w]) EnQueue(Q,w); //u 的尚未访问的邻接点 w 入队列 Q 15 } 16 } 17 }
七:最短路径:源点与终点之间经过的边上权值之和最小的路径。
(1)Dijkstra(没有负权的单源最短路径)
步骤一:初始化时令 S = { v0 }, T = V - S ={ 其余顶点 },S 代表已经走过的顶点,设d(v,u)
代表顶点 v 到 u 的边权值。
<1> 若存在 < v0, vi>,d(v0,vi)为
<2> 若不存在
步骤二:从 T 中选取一个与 S 中顶点有关联边且权值小的顶点 w,加入到 S 中。
步骤三:对 T 中其余顶点的距离值进行修改
d(v0,vi) = min(d(v0,vi),d(v0,w)+d(w,vi))
若加进 w 作中间顶点,从 v0 到 vi 的距离值缩短,则修改此距离。
步骤四:重复上述步骤 2、3,直到 S 中 包含所有顶点,即 w = vi 为止。
1 #include2 #include 3 #define MAX 1000000 4 using namespace std; 5 int arcs[10][10]; //邻接矩阵 6 int D[10]; //保存最短路径长度 7 int p[10][10]; //路径 8 int final[10]; //若final[i] = 1; 则说明 v[i] 已在集合 S中 9 int n = 0; //顶点个数 10 int v0 = 0; //顶点 11 int v,w; 12 void Dijkstra() 13 { 14 for(v = 0; v < n; v++) //循环初始化 15 { 16 final[v] = 0; D[v] = arcs[v0][v]; 17 for(w = 0; w < n; w++) p[v][w] = 0; //设空路径 18 if(D[v] < MAX) 19 { 20 p[v][v0] = 1; 21 p[v][v] = 1; 22 } 23 } 24 D[v0] = 0; final[v0] = 0; //初始化 v0 顶点属于集合 S 25 //开始1主循环,每次求得 v0 到某个顶点 v 的最短路径,并将 v 加到 集合 S 中 26 for(int i = 1; i < n; i++) 27 { 28 int min = MAX; 29 for(w = 0; w < n; w++) 30 { 31 if(!final[w]) //如果顶点 w 在 v - s 中 32 { 33 if(D[w] < min) 34 { //这个过程最终选出的点应当是当前 v-s 中与 s 有关联边且权值最小的顶点 35 v = w; 36 min = D[w]; 37 } 38 } 39 } 40 final[v] = 1; //选出该点后加入到集合 s 中 41 for(w = 0; w < n; w++) //更新当前最短路径和距离 42 { 43 /*** 44 在此循环中, v 为当前刚选入集合 s 中的点,则以 v 为中间点 考察 d(v0,v) + d(v,w) 45 是否小于D[w],如果是,则更新。 46 ***/ 47 if(!final[w] && (min + arcs[v][w] < D[w])) 48 { 49 D[w] = min + arcs[v][w]; 50 p[w][w] = 1; 51 } 52 } 53 } 54 } 55 56 int main() 57 { 58 cin>>n; 59 for(int i = 0; i < n; i++) 60 for(int j = 0; j < n; j++) 61 cin>>arcs[i][j]; 62 63 Dijkstra(); 64 for(int i = 0; i < n; i++) 65 printf("D[%d]=%d\n",i,D[i]); 66 return 0; 67 }
(2)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中包含两点之间最短路径经过的点。
1 #include2 #include 3 #define max 1000000000 4 using namespace std; 5 int d[1000][1000], path[1000][1000]; 6 int main() 7 { 8 int i,j,k,m,n; 9 int x,y,z; 10 scanf("%d%d",&n,&m); 11 for(i = 1; i <= n; i++) 12 { 13 for(j = 1; j <= n; j++) 14 { 15 d[i][j] = max; 16 path[i][j] = j; 17 } 18 } 19 for(i = 1; i <= m; i++) 20 { 21 scanf("%d%d%d",&x,&y,&z); 22 d[x][y] = z; 23 d[y][x] = z; 24 } 25 for(k = 1; k <= n; k++) 26 for(i = 1; i <= n; i++) 27 for(j = 1; j <= n; j++) 28 { 29 if(d[i][k] + d[k][j] < d[i][j]) 30 { 31 d[i][j] = d[i][k] + d[k][j]; 32 path[i][j] = path[i][k]; 33 } 34 } 35 for(i = 1; i <= n; i++) 36 for(j = 1; j <= n; j++) 37 if(i != j) printf("%d->%d:%d\n",i,j,d[i][j]); 38 int st,en; 39 scanf("%d%d",&st,&en); 40 while(st != en) 41 { 42 printf("%d->",st); 43 st = path[st][en]; 44 } 45 printf("%d\n",en); 46 return 0; 47 }