一、有向圖概念:
- 頂點:有向圖的每一個節點
- 弧:每一條線
- 弧頭:線的起始點
- 弧尾:線的結束點
- 出度/入度:
- 出度:某一頂點發出去的弧的數量;
- 入度:某一頂點射入的弧的數量
二、無向圖概念:
- 鄰接點:存在一條邊連接兩個點
- 邊:無向圖中的連線;
- 頂點:圖中的節點;
- 連通圖:對於圖內每一個頂點都直接或間接存在通往其他頂點的路徑;
- 完全圖:圖中任一頂點都存在和其他所有頂點的直接連線。邊數
- 生成樹:只有最小數量的邊連接每一個頂點生成連通圖。邊數 (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 << "\n"<<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;
}