更新了数据结构第六章部分

This commit is contained in:
Alice-and-Bob
2020-07-24 18:08:52 +08:00
parent 3cd9f38007
commit e2c85698ae
12 changed files with 271 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,74 @@
# 习题5.5
## T13 2012
> 不会,以下为解答
1. 求最小合并次数类似于求最小带权路径长度,则联想到哈夫曼树。那么每次选择表集合中长度最小的两个表进行合并
第一次 合并A、B
第二次 合并AB和C
第三次 合并D、E
第四次 合并ABC和DE
第五次 合并ABCDE和F
**合并两个有序表的最坏情况需要比较m+n-1次**
按合并顺序可以算出每次需要比较的次数
2. 合并表长不同的顺序表时,最坏情况下的比较次数依赖于合并的次序,则每次选择表长最短的两个表合并,可以获得最坏情况下的最小比较次数
> 原以为要考具体的元素级别的合并算法,结果是考表与表的合并……
> 能获得启发就是:**合并两个有序表的最坏情况需要比较m+n-1次**
# 习题5.4
## T4 2016
1. `(k-1)*n+1`
2. $$
\begin{aligned}
n_{max} &= \frac{1-k^h}{1-k} \\
n_{min} &= k+\frac{1-k^{h-1}}{1-k}
\end{aligned}
$$
# 习题5.3
## T19 2014
1. 采用深度优先的递归遍历搜索,记录递归层数,当访问到叶结点时把当前层数乘以叶结点权值,最后全部叶结点的带权路径长度值相加
2. ```cpp
typedef struct BiTreeNode{
struct BiTreeNode *left;
int weight;
struct BiTreeNode *right;
}BiTreeNode, *BiTree;
```
3. ```cpp
i=1; //递归层数
WPL=0; //带权路径长度
void GetWPL(BiTree *p){
if(p->left==NULL && p->right==NULL){ //叶结点
i--;
WPL+=i*p->weight;
return ;
}
else{
if(p->left!=NULL&&p->right==NULL){
i++;
GetWPL(p->left);
}
else(p->left==NULL&&p->right!=NULL){
i++;
GetWPL(p->right);
}
}
}
```
## T20 2017
1. 中序遍历二叉树,在递归访问某结点的左孩子之前输出一个左括号,在递归访问右孩子之后输出一个右括号
2. ```cpp
void func(BTree p){
if(p!=NULL){
cout<<'(';
func(p->left);
visit(p);
func(p->right);
cout<<')';
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

View File

@@ -0,0 +1,113 @@
# 图的存储
## 邻接矩阵法
用一个一维数组存储顶点信息,用一个二维数组存储图中边的信息
结点数为n的图其邻接矩阵A是n×n的A[i][j]表示顶点i和顶点j之间的边有向图中A[i][j]与A[j][i]一般不同
不带权的图:
$$A[i][j]=
\left\{\begin{matrix}
1,若 (v_i,v_j)或<v_i,v_j>是E(G)中的边\\
0,若 (v_i,v_j)或<v_i,v_j>不是E(G)中的边\\
\end{matrix}\right.
$$
带权图:
$$A[i][j]=
\left\{\begin{matrix}
w_{ij},若 (v_i,v_j)或<v_i,v_j>是E(G)中的边\\
0或\infty,若 (v_i,v_j)或<v_i,v_j>不是E(G)中的边\\
\end{matrix}\right.
$$
邻接矩阵法的存储结构定义
```cpp
#define MaxVertexNum 100
typedef char VertexType;
typedef int EdgeType;
typedef struct{
VertexType Vex[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum, arcnum; //图的当前顶点数和弧数
}MGraph;
```
![](1.png)
## 邻接表法
i个单链表中的结点代表了依附于顶点i的边i为弧尾的边
```cpp
#define MaxVertexNum 100
typedef struct ArcNode{ //边表结点
int adjvex; //该弧指向的顶点的位置
struct ArcNode *next;
//InfoType info; 边权值
}ArcNode;
typedef struct VNode{ //顶点表结点
VertexType data;
ArcNode *first;
}VNode, AdjList[MaxVertexNum];
typedef struct{
AdjList vertices; //邻接表
int vexnum,arcnum; //图的顶点数和弧数
}ALGraph;
```
![](2.1.png)
![](2.2.png)
## 十字链表法
> 用于有向图
弧结点有5个域
* tailvex, headvex指向弧头结点和弧尾结点
* hlink指向弧头相同的下一条弧
* tlink指向弧尾相同的下一条弧
* info包括了弧的相关信息
顶点结点有3个域
* data存放顶点有关信息
* firstin和firstout指向以该节点为弧头或弧尾的第一个弧结点
![](3.png)
## 邻接多重表
> 用于无向图
与十字链表类似,每个边和顶点都有顶点
边结点:
* Mark标记是否被搜索过
* ivex指向该边依附的i结点位置
* jvex指向该边依附的j结点位置
* ilink指向下一条依附于i结点的边
* jlink指向下一条依附于j结点的边
* info表示边信息
顶点结点:
* data代表顶点信息
* firstedge指向第一条依附于该顶点的边
![](4.png)
# 图的基本操作
- Adjacent(G, x, y)
- 判断图G是否存在边(x,y)或<x,y>
- Neighbors(G, x)
- 列出G中与结点x邻接的边
- InsertVertex(G, x)
- 在G中插入顶点x
- DeleteVertex(G, x)
- 在G中删除顶点x
- AddEdge(G, x, y)
- 若G中不存在边(x,y)或<x,y>,则添加该边
- RemoveEdge(G, x, y)
- 若G中存在边(x,y)或<x,y>,则删除该边
- FirstNeighbor(G, x)
- 求G中顶点x的第一个邻接点返回顶点号出现错误则返回-1
- NextNeighbor(G, x, y)
- 设y为x的一个邻接点返回除y的下一个邻接点号其他情况返回-1
- Get_edge_value(G, x, y)
- 返回边(x,y)或<x,y>的权值
- Set_edge_value(G, x, y, v)
- 设置边(x,y)或<x,y>的权值为v

View File

@@ -0,0 +1,84 @@
# 广度优先搜索
1. **基本思想**从一个选定的顶点v出发依次访问v的邻接结点再从这些结点出发去访问与他们邻接且没有被访问过的结点重复这个过程直到图中所有的结点都被访问过
2. 伪代码:
```cpp
bool visited[MAX_VERTEX_NUM]; //标记数组
void BFSTraverse(Graph G){
for(i=0;i<G.vexnum;++i){ //初始化标记数组
visited[i]=FALSE;
}
InitQueue(Q); //辅助队列
for(i=0;i<G.vexnum;++i){ //遍历图的所有结点
if(!visited[i]){ //如果未访问过,则调用广搜
BFS(G,i); //这种做法是为了防止图不是一个连通图
}
}
}
void BFS(Graph G, int v){
visit(v); //访问结点v
visited[v]=TRUE; //立即标记访问过该节点
Enqueue(Q,v); //第一结点入队
while(!isEmpty(Q)){
DeQueue(Q,v); //对队列头v进行处理
for(w=FirstNeighbor(G,v); w>=0; w=NextNeighbor(G,v,w)){
//检测v的所有邻接点
if(!visited[w]){ //对每一个v的未曾访问过的邻接点w
visit(w); //访问
visited[w]=TRUE;//标记
EnQueue(Q,w); //入队
}
}
}
}
```
3.
-
- **** : $O(|V|^2)$
- **** : $O(|V|)$
- BFS是树的层次遍历的扩充
# 深度优先搜索
1. ****v出发访访w1访w1的邻接点中的任意一个w2退访访使
2.
```cpp
bool visited[MAX_VERTEX_NUM]; //新建标记数组
void DFSTraverse(Graph G){
for(v=0;v<G.vexnum;++v){ //初始化标记数组
visited[v]=FALSE;
}
for(v=0;v<G.vexnum;++v){ //对图的每个连通分量进行深搜
if(!visited[i]){
DFS(G,v);
}
}
}
void DFS(Graph G, int v){
visit(i);
visited[i]=TRUE;
for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
//对于v的每一个邻接结点w
if(!visited[w]){
DFS(G,w);
}
}
}
```
3. 性质分析:
- 复杂度分析:
- **时间复杂度** :
- 邻接矩阵表达: $O(|V|^2)$
- 邻接表表达:$O(|V|+|E|)$
- **空间复杂度** : $O(|V|)$
- DFS是树的先序遍历的扩展
> 图、树、森林的等效遍历方式
> | 树 | 森林 | 二叉树 | 图 |
> | :-: | :-: | :-: | :-: |
> | 先根遍历 | 先序遍历 | 先序遍历 | DFS |
> | 后根遍历 | 中序遍历 | 中序遍历 | x |
> | 层次遍历 | x | 层次遍历 | BFS |

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.