一、有向图概念:
- 顶点:有向图的每一个节点
- 弧:每一条线
- 弧头:线的起始点
- 弧尾:线的结束点
- 出度/入度:
- 出度:某一顶点发出去的弧的数量;
- 入度:某一顶点射入的弧的数量
二、无向图概念:
- 邻接点:存在一条边连接两个点
- 边:无向图中的连线;
- 顶点:图中的节点;
- 连通图:对于图内每一个顶点都直接或间接存在通往其他顶点的路径;
- 完全图:图中任一顶点都存在和其他所有顶点的直接连线。边数
- 生成树:只有最小数量的边连接每一个顶点生成连通图。边数(n为顶点个数)
三、图存储结构:
1.存储结构分类
- 邻接矩阵:数组
- 邻接表:链表-有向图
- 十字链表:链表-有向图
- 邻接多重表:链表-无向图
1.1.邻接矩阵-数组存储
- 无向图的邻接矩阵是对称矩阵
- 算法实现
struct Node
{
顶点索引;
顶点数据;一维数组
}
struct Map
{
顶点数组;//存储所有顶点信息
邻接矩阵;//存储顶点间连接关系。二维数组
}
1.2.邻接表-
- 邻接表:
顶点表示方法:
顶点索引 | 出弧链表头指针 | 顶点数据 |
- 弧的表示方法
弧头顶点索引 | 下一条弧指针 | 弧数据 |
- 邻接表的表示结构
struct Node
{
顶点索引
该顶点弧链表头节点;
顶点数据;
}
struct Arc
{
指向的顶点索引
指向下一条弧的指针
弧信息
}
struct Map
{
顶点数组
}
- 逆邻接表
顶点索引 | 入弧链表头指针 | 顶点数据 |
- 弧的表示方法
弧尾顶点索引 | 下一条弧指针 | 弧数据 |
1.3.十字链表
1.4.邻接多重表-
四、图的遍历:
- 深度优先搜索
- 类似树的前序遍历
- 广度优先搜索
- 一层一层的搜索
- 最小生成树
- 实现搜索最小值
- 普利姆Prim算法
- 克鲁斯卡尔Kruskal算法
五、利用邻接表实现图的深度优先搜索和广度优先搜索(其中深度优先搜索用递归方法;广度优先搜索用队列非递归方法)
PS:邻接表方法并不合适,因为当图比较大的时候,会需要很多的连续内存占用,所以应该探索别的存储方法。
//Node.h
#pragma once
#include<iostream>
using namespace std;
class Node
{
public:
Node(int d=0);
~Node();
int data;
bool is_vis;
private:
};
//Node.cpp
#include"Node.h"
Node::Node(int d)
{
data = d;
is_vis = false;
}
Node::~Node()
{
}
//Graph.h
#pragma once
#include"Node.h"
#include<vector>
class Graph
{
public:
Graph(int c);
~Graph();
bool add_node(Node *n);//增加元素,第几个增加的元素其序号就为几,这个要注意。
bool reset_node();//重置所有元素为未访问状态
void print_matrix();//输出邻接矩阵
bool insert_edge(int row, int column, int wei=1);//增加节点连接关系
int get_matrix_value(int row, int column);//获得邻接矩阵的连接
void depth_search(int index);//深度优先搜索
void width_search(int index);//广度优先搜索
private:
int capacity;
int node_count;
Node* node_array;
int* node_matrix;
};
//Graph.cpp
#include"Graph.h"
#include<queue>
Graph::Graph(int c)
{
capacity=c;
node_count=0;
node_array=new Node[c];//初始化node数组
node_matrix=new int[c*c];//n个节点
memset(node_matrix, 0, c * c * sizeof(int));//初始化
}
bool Graph::add_node(Node* n)
{
node_array[node_count].data = n->data;//添加顺序就是节点编号
cout << node_count <<" "<< n->data << endl;
node_count++;
return true;
}
void Graph::print_matrix()
{
for (int i=0;i<capacity;i++)
{
for (int j=0;j<capacity;j++)
{
cout << node_matrix[i * capacity + j]<<" ";
}
cout << endl;
}
cout<<"================================================================"<<endl;
}
bool Graph::insert_edge(int row, int column, int wei)
{
if (row < 0 || row >= capacity) { return false; }
if (column < 0 || column >= capacity) { return false; }
node_matrix[row * capacity + column] = wei;
node_matrix[column * capacity + row] = wei;
return true;
}
int Graph::get_matrix_value(int row, int column)
{
if (row < 0 || row >= capacity) { return 0; }
if (column < 0 || column >= capacity) { return 0; }
return node_matrix[row * capacity + column];
}
bool Graph::reset_node()
{
for (int i = 0; i < node_count; i++)
{
node_array[i].is_vis = false;
}
return true;
}
void Graph::depth_search(int index)
{
cout << node_array[index].data << " ";
node_array[index].is_vis = true;
for (int i = 0; i < capacity; i++)
{
if (get_matrix_value(index, i)==1)
{
if (!node_array[i].is_vis)
{
depth_search(i);
}
else
{
continue;
}
}
}
}
void Graph::width_search(int index)
{
cout << node_array[index].data << "
"<<endl;
node_array[index].is_vis = true;//第一个节点访问,并置为已访问
queue<int> q;
q.push(index);
int pre_q = 1;//统计第一层节点的个数,首节点当然个数为1;
while (!q.empty())
{
int value = 0;
int cur_q = 0;//记录下一层元素的个数
for (int i = 0; i < pre_q; i++)//注意这的截至条件是pre_q,说明每次循环都只会将上一层加入的节点取出,然后保存下一层的节点,根据先进先出原则,正好把上一层的取出完毕
{
int num = q.front();//得到先进入的第一个元素
q.pop();
for (int j = 0; j < capacity; j++)
{
value = node_matrix[num* capacity + j];
if (value)
{
if (!node_array[j].is_vis)//判断元素是否访问过。
{
cout << node_array[j].data << endl;
node_array[j].is_vis = true;
cur_q++;
q.push(j);//将下一层的节点入队
}
}
}
}
pre_q = cur_q;//将本层元素个数作为下一层循环的父节点个数
cout << endl;
}
return;
}
Graph::~Graph()
{
delete[]node_array;
delete[]node_matrix;
}
//main.cpp
#include"Graph.h"
int main()
{
Graph g(8);
Node* n1 = new Node(1);
Node* n2 = new Node(2);
Node* n3 = new Node(3);
Node* n4 = new Node(4);
Node* n5 = new Node(5);
Node* n6 = new Node(6);
Node* n7 = new Node(7);
Node* n0 = new Node(0);
g.add_node(n0);
g.add_node(n1);
g.add_node(n2);
g.add_node(n3);
g.add_node(n4);
g.add_node(n5);
g.add_node(n6);
g.add_node(n7);
//设置连接关系
g.insert_edge(0, 1);
g.insert_edge(2, 1);
g.insert_edge(0, 4);
g.insert_edge(3, 1);
g.insert_edge(2, 3);
g.insert_edge(4, 5);
g.insert_edge(4, 7);
g.insert_edge(5, 6);
g.insert_edge(6, 7);
g.depth_search(0);
g.reset_node();
cout << endl;
g.width_search(0);
//宽度遍历
return 0;
}