- 图
1.1 图的抽象数据类型
1.2 图的模版基类 - 邻接矩阵(相邻矩阵)
2.1 类定义
2.2 构造函数 - 邻接表
3.1 类定义
3.2 构造函数
3.3 析构函数
3.4 建立算法 - 图的遍历
4.1 深度优先遍历算法 (DFS)
4.2 广度优先遍历算法 (BFS) - 四叉特征数
5.1 存储结构
5.2 前序遍历算法 (深度优先遍历)
5.3 层次遍历算法 (广度优先遍历) - 迷宫问题
6.1 深度优先遍历
6.2 广度优先遍历 - 最小生成树
7.1 克鲁斯卡尔算法 (Kruskal 算法)
7.2 普里姆算法 (Prim 算法)
7.3 迪杰斯特拉算法 (dijkstra 算法) - 拓扑排序
# 一、图
# 1. 图的抽象数据类型
| |
| template<class T> |
| class Graph |
| { |
| |
| |
| public: |
| Graph(); |
| void insertVertex(const T &vertex); |
| void insertEdge(int v1,int v2,int weight); |
| void removeVertex(int v); |
| void removeEdge(int v1,int v2); |
| bool IsEmpty(); |
| T getWeight(int v1,int v2); |
| int getFirstNeighbor(int v); |
| int getNextNeighbor(int v,int w); |
| }; |
# 2. 图的模版基类
| const int maxWeight=0x3f3f3f3f; |
| const int DefaultVertices=30; |
| template<class T,class E> |
| class Graph |
| { |
| protected: |
| int maxVertices; |
| int numEdges; |
| int numVertices; |
| int getVertexPos(T vertex); |
| |
| public: |
| Graph(int sz=DefaultVertices); |
| ~Graph(); |
| bool GraphEmpty() const |
| { |
| return numEdges==0; |
| } |
| int NumberOfVertices() |
| { |
| return numVertices; |
| } |
| int NumberOfEdges() |
| { |
| return numEdges; |
| } |
| virtual T getValue(int i); |
| virtual E getWeight(int v1,int v2); |
| virtual int getFirstNeighbor(int v); |
| virtual int getNextNeighbor(int v,int w); |
| virtual bool insertVertex(const T vertex); |
| virtual bool insertEdge(int v1,int v2,E cost); |
| virtual bool removeVertex(int v); |
| virtual bool removeEdge(int v1,int v2); |
| }; |
# 二、邻接矩阵(相邻矩阵)
![image-20251121105356089]()
![image-20251121105404522]()
![image-20251121105427774]()
# 1. 类定义
| template<class T,class E> |
| class Graphmtx:public Graph<T,E> |
| { |
| friend istream &operator>>(istream &in,Graphmtx<T,E> &G); |
| friend ostream &operator<<(ostream &out,Graphmtx<T,E> &G); |
| |
| private: |
| T *VerticesList; |
| E **Edge; |
| int getVertexPos(T vertex) |
| { |
| for(int i=0;i<numVertices;i++) |
| { |
| if(VerticesList[i]==vertex) |
| { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| public: |
| Graphmtx(int sz=DefaultVertices); |
| ~Graphmtx() |
| { |
| for(int i=0;i<numVertices;i++) |
| { |
| delete [] Edge[i]; |
| } |
| |
| delete []VerticesList; |
| delete []Edge; |
| } |
| T getValue(int i) |
| { |
| return (i>=0 && i<=numVertices ? VerticesList[i] : NULL); |
| } |
| E getWeight(int v1,int v2) |
| { |
| return v1!=-1 && v2 !=-1 ? Edge[v1][v2] : 0; |
| } |
| int getFirstNeighbor(int v); |
| int getNextNeighbor(int v,int w); |
| bool insertVertex(const T vertex); |
| bool insertEdge(int v1,int v2,E cost); |
| bool removeVertex(int v); |
| bool removeEdge(int v1,int v2); |
| }; |
# 2. 构造函数
![image-20251121151454394]()
| template<class T,class E> |
| Graphmtx<T,E>::Graphmtx(int sz) |
| { |
| maxVertices=sz; |
| numVertices=0; |
| numEdges=0; |
| int i,j; |
| VerticesList=new T[maxVertices]; |
| Edge=(E**)new E *[maxVertices]; |
| for(i=0;i<maxVertices;++i) |
| { |
| Edge[i]=new int[maxVertices]; |
| for(j=0;j<maxVertices;++j) |
| { |
| Edge[i][j]=(i==j)?0:maxWeight; |
| } |
| } |
| } |
# 三、邻接表
![image-20251121151552500]()
![image-20251121151602660]()
# 1. 类定义
![image-20251121160935211]()
![image-20251121161633471]()
| |
| template<class T,class E> |
| struct Edge |
| { |
| int dest; |
| E cost; |
| Edge<T,E> *link; |
| Edge(){} |
| Edge(int num,E weight):dest(num),cost(weight),link(NULL){} |
| bool operator!=(Edge<T,E> &R) const |
| { |
| return dest!=R.dest; |
| } |
| }; |
| |
| |
| template<class T,class E> |
| struct Vertex |
| { |
| T data; |
| Edge<T,E> *adj; |
| }; |
| |
| |
| template<class T,class E> |
| class Graphlnk : public Graph<T,E> |
| { |
| friend istream &operator>>(istream &in,Graphlnk<T,E> &G); |
| friend ostream &operator<<(ostream &out,Graphlnk<T,E> &G); |
| |
| private: |
| Vertex<T,E> *NodeTable; |
| int getVertexPos(const T vertx) |
| { |
| for(int i=0;i<numVertices;++i) |
| { |
| if(NodeTable[i].data==vertx) |
| { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| public: |
| Graphlnk(int sz=DefaultVertices); |
| ~Graphlnk(); |
| T getValue(int i) |
| { |
| return(i>=0 && i<numVertices)? NodeTable[i].data:0; |
| } |
| E getWeight(int v1,int v2); |
| bool insertVertex(const T &vertex); |
| bool removevertex(int v); |
| bool insertEdge(int v1,int v2,E cost); |
| bool removeEdge(int v1,int v2); |
| int getFirstNeighbor(int v); |
| int getNextNeighbor(int i,int w); |
| void CreateNodeTable(); |
| }; |
# 2. 构造函数
| template<class T,class E> |
| Graphlnk<T,E>::Graphlnk(int sz) |
| { |
| maxVertices=sz; |
| numVertices=0; |
| numEdges=0; |
| NodeTable=new Vertex<T,E>[maxVertices]; |
| if(NodeTable==NULL) |
| { |
| cerr <<"存储分配错!" <<endl; |
| exit(1); |
| } |
| for(int i=0;i<maxVertices;++i) |
| { |
| NodeTable[i].adj=NULL; |
| } |
| } |
# 3. 析构函数
| template<class T,class E> |
| Graphlnk<T,E>::~Graphlnk() |
| { |
| for(int i=0;i<numVertices;++i) |
| { |
| Edge<T,E> *p=NodeTable[i].adj; |
| while(p!=NULL) |
| { |
| NodeTable[i].adj=p->link; |
| delete p; |
| p=NodeTable[i].adj; |
| } |
| } |
| delete []NodeTable; |
| } |
# 4. 建立算法
![image-20251121163457574]()
![image-20251121163652884]()
| template<class T,class E> |
| void Graphlnk<T,E>::CreateNodeTable() |
| { |
| |
| |
| int n,i,j,m; |
| Edge<T,E> *p; |
| cin >>n; |
| for(i=1;i<=n;++i) |
| { |
| NodeTable[i].adj=0; |
| cin >>NodeTable[i].data; |
| cin >>m; |
| for(j=0;j<m;++j) |
| { |
| p=new Edge<T,E>; |
| cin >>p->dest; |
| cin >>p->cost; |
| p->link=NodeTable[i].adj; |
| NodeTable[i].adj=p; |
| } |
| } |
# 四、图的遍历
# 1. 深度优先遍历算法 (DFS)
| |
| void Graphlnk<T,E>::DFS(int v) |
| { |
| |
| visited[v]=true; |
| cout <<NodeTable[v].data; |
| p=NodeTable[v].adj; |
| while(p!=NULL) |
| { |
| if(!visited[p->dest]) |
| { |
| DFS(p->dest); |
| p=p->link; |
| } |
| } |
| } |
| |
| bool *visited; |
| int numVertices; |
| visited=new bool[n]; |
| cin >>numVertices; |
| void Graphlnk<T,E>::dfs() |
| { |
| int i,v0; |
| for(i=0;i<numVertices;++i) |
| { |
| visited[i]=false; |
| } |
| cin >>v0; |
| dfs(v0); |
| } |
# 2. 广度优先遍历算法 (BFS)
| template<class T, class E> |
| void Graphlnk<T,E>::BFS() |
| { |
| queue<int> qu; |
| int v; |
| cin >> v; |
| |
| for(int i = 0; i < numVertices; ++i) |
| visited[i] = false; |
| |
| |
| qu.push(v); |
| visited[v] = true; |
| while(!qu.empty()) |
| { |
| int cur = qu.front(); |
| qu.pop(); |
| cout << NodeTable[cur].data; |
| Edge<T,E>* p = NodeTable[cur].adj; |
| |
| while(p != NULL) |
| { |
| if(!visited[p->dest]) |
| { |
| visited[p->dest] = true; |
| qu.push(p->dest); |
| } |
| p = p->link; |
| } |
| } |
| } |
# 五、四叉特征数
![image-20251121184654921]()
# 1. 存储结构
| |
| struct treenode |
| { |
| int data; |
| struct treenode *child[4]; |
| }; |
# 2. 前序遍历算法 (深度优先遍历)
![image-20251121194549322]()
| |
| void DFS(node *root) |
| { |
| if (root == NULL) return; |
| cout << root->data << " "; |
| for (int i = 0; i < 4; i++) |
| { |
| DFS(root->child[i]); |
| } |
| } |
| |
| void DFS(struct node *root) |
| { |
| if (root == NULL) |
| { |
| return; |
| } |
| cout << root->data << " "; |
| int i = 0; |
| while (i < 4) |
| { |
| DFS(root->child[i]); |
| i++; |
| } |
| } |
# 3. 层次遍历算法 (广度优先遍历)
![image-20251121194653396]()
| |
| void BFS(struct treenode *root) |
| { |
| if(!root) |
| { |
| return; |
| } |
| queue<treenode *> qu; |
| qu.push(root); |
| while(!qu.empty()) |
| { |
| treenode *temp = qu.front(); |
| qu.pop(); |
| cout << temp->data << " "; |
| for(int i = 0; i < 4; ++i) |
| { |
| if(temp->child[i] != NULL) |
| { |
| qu.push(temp->child[i]); |
| } |
| } |
| } |
| } |
# 六、迷宫问题
![image-20251121211945819]()
# 1. 深度优先遍历
![image-20251121211922056]()
| int MazePath(int x, int y) |
| { |
| int loop; |
| Maze[x][y] = -1; |
| for(loop = 0; loop < 8; loop++) |
| { |
| x = x + move[loop].x; |
| y = y + move[loop].y; |
| |
| if((x == m) && (y == n)) |
| { |
| PrintPath(); |
| Restore(Maze); |
| return 1; |
| } |
| |
| if(Maze[x][y] == 0) |
| { |
| .......... |
| MazePath(x, y); |
| } |
| } |
| return 0; |
| } |
# 2. 广度优先遍历
| int maze[m+2][n+2]; |
| |
| struct DataType { |
| int x, y, pre; |
| }; |
| |
| int MazePath() { |
| queue<DataType> q; |
| DataType Temp1, Temp2; |
| int x, y, loop; |
| Temp1.x = 1; |
| Temp1.y = 1; |
| Temp1.pre = -1; |
| maze[1][1] = -1; |
| q.push(Temp1); |
| while (!q.empty()) |
| { |
| Temp2 = q.front(); |
| q.pop(); |
| |
| for (loop = 0; loop < 8; loop++) |
| { |
| x = Temp2.x + move[loop].x; |
| y = Temp2.y + move[loop].y; |
| if ((x == m) && (y == n)) |
| { |
| PrintPath(q); |
| Restore(maze); |
| return (1); |
| } |
| if (maze[x][y] == 0) |
| { |
| Temp1.x = x; |
| Temp1.y = y; |
| |
| maze[x][y] = -1; |
| q.push(Temp1); |
| } |
| } |
| } |
| return (0); |
| } |
# 七、最小生成树
![image-20251122091037575]()
![image-20251122091051279]()
# 1. 克鲁斯卡尔算法 (Kruskal 算法)
![image-20251122121100868]()
![image-20251122132348587]()
通过小根堆选最小边 + 并查集判断是否成环,构建最小生成树
| |
| |
| for (u = 0; u < n; u++) |
| { |
| for (v = u + 1; v < n; v++) |
| { |
| if (Edge[u][v] < maxWeight) |
| { |
| ed.tail = u; |
| ed.head = v; |
| ed.cost = Edge[u][v]; |
| |
| |
| H.Insert(ed); |
| } |
| } |
| } |
| count = 1; |
| |
| while (count < n) |
| { |
| H.Remove(ed); |
| |
| |
| u = F.Find(ed.tail); |
| v = F.Find(ed.head); |
| if (u != v) |
| { |
| F.Union(u, v); |
| MST.Insert(ed); |
| |
| count++; |
| } |
| } |
# 2. 普里姆算法 (Prim 算法)
![image-20251122123439134]()
![image-20251122133142340]()
![image-20251122132201618]()
![image-20251122132216933]()
| template <class T, class E> |
| struct MSTEdgeNode |
| { |
| int head; |
| int tail; |
| E cost; |
| |
| }; |
| |
| MinHeap<MSTEdgeNode<T, E>> H(m); |
| |
| Edge[u][u] = true; |
| count = 1; |
| while (count < n) |
| { |
| |
| for (v = 0; v < n; v++) |
| { |
| if ((Edge[v][v] == 0) && Edge[u][v] < maxWeight) |
| { |
| ed.tail = u; |
| ed.head = v; |
| ed.cost = Edge[u][v]; |
| |
| H.Insert(ed); |
| } |
| } |
| |
| while (!H.IsEmpty() && count < n) |
| { |
| H.Remove(ed); |
| |
| if (Edge[ed.head][ed.head] == 0) |
| { |
| MST.Insert(ed); |
| |
| u = ed.head; |
| Edge[u][u] = true; |
| count++; |
| break; |
| } |
| } |
| } |
# 3. 迪杰斯特拉算法 (dijkstra 算法)
| |
| for (i = 0; i < n; i++) |
| { |
| dist[i] = G[v0][i]; |
| } |
| G[v0][v0] = 1; |
| |
| for (i = 0; i < n - 1; i++) |
| { |
| |
| min = 32767; |
| for (k = 0; k < n; k++) |
| { |
| if (G[k][k] == 0 && dist[k] < min) |
| { |
| pos = k; |
| min = dist[k]; |
| } |
| } |
| G[pos][pos] = 1; |
| |
| for (j = 0; j < n; j++) |
| { |
| if (G[j][j] == 0 && G[pos][j] + min < dist[j]) |
| { |
| dist[j] = G[pos][j] + min; |
| } |
| } |
| } |
# 八、拓扑排序
![image-20251122133723466]()
基于入度的拓扑排序:
找入度 0 → 入栈 → 出栈输出 → 消除出边 → 新的入度 0 入栈
若最终未处理完所有节点,则图有环。
| 1. 在 count 数组中找出入度为零的顶点,并分别入栈; |
| 2. while(栈非空) |
| { |
| 出栈到 v; |
| cout << NodeTable[v].data; |
| ++m; |
| p = NodeTable[v].adj; |
| while (p != NULL) |
| { |
| count[p->dest]--; |
| 如果 count[p->dest] == 0 则把它入栈 |
| p = p->link; |
| } |
| } |
| 3. if (m < n) |
| { |
| cout << "图中有回路" |
| } |