java 树 最短路径代码_算法系列笔记8(有关图的算法二—最短路径问题)

结果如下:

a3e32e1b0d0b943907b364b4755b866d.png

时间复杂度为O(VE).

注意:这里有个应用就是差分约束系统。其实线性规划的一种特殊情况,即A矩阵每行只有一个1和-1,其它的都为0,这样可以构造约束图,求得最短路径就是差分约束系统的解。这里不详细介绍了。

DAG图的单源最短路径

算法的基本思想:求DAG图的拓扑排序,然后按照拓扑排序顶点的顺序对其每条边进行松弛操作即如果d[v] > d[u]+w[u,v],则d[v] = d[u]+w[u,v]。其时间复杂度为O(V+E).

二:全对最短路径问题

求所有的两两顶点之间的最短路径,最基本的思想就是对所有的顶点都运用单源最短路径问题。这样当无权值时,时间复杂度为O(V^2+VE);权值非负为O(V^2lgV+VE);一般情况为O(V^2*E),此时对于稠密图E=O(V^2),要达到O(V^4)了,效率会非常差,因此需要其他算法来改进时间复杂度。主要有三种方法,第一种为动态规划方法(结合矩阵乘法);第二种为Floyd-Warshall算法;第三种为Johnson算法,对于稀疏图要优于Floy-Warshall算法。

动态规划方法

45db6d1e6e587d3ce786c643dd1da76b.png

其中dijm表示从i到j的最短路径,其中最多经过m条边。此时当m=V-1即为我们要求的最短路径(对于无负权环)。D(1)=W为权重矩阵。即求D(V-1).

此外将(1)式看做某种新运算*,那么每次都是求D(m) = D(m-1) * W。

代码如下:

// 用动态规划求全最短路径

void Graph::dpAllDistance(){

int Adj[maxSize][maxSize];

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

minAllDistance[i][j] = MAXPRICE;

if(i == j) minAllDistance[i][j] = 0;

AllPath[i][j] = intToStr(i);

Adj[i][j] = minAllDistance[i][j];

}

}

for(int i = 0; i < vertexNum; i++){

GNode *p = edges[i].next;

while(p != NULL){

minAllDistance[i][p->val] = p->weight; // 经过了1条边 即为权重

Adj[i][p->val] = p->weight;

AllPath[i][p->val] = AllPath[i][p->val] + intToStr(p->val);

p = p->next;

}

}

for(int m = 2; m < vertexNum; m++){ // 至多经过m条边

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

for(int k = 0; k < vertexNum; k++){

if(minAllDistance[i][j] > minAllDistance[i][k] + Adj[k][j]){

minAllDistance[i][j] = minAllDistance[i][k] + Adj[k][j];

AllPath[i][j] = AllPath[i][k] + intToStr(j);

}

}

}

}

}

// 检测是否存在负权环

for(int i = 0; i < vertexNum; i++){

if(minAllDistance[i][i] < 0){

cout << "该图存在负权环" << endl;

return;

}

}

// 将全对最短路径输出

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

cout << "从" <

时间复杂度:此时时间复杂度仍然为O(V^4),但是采用D(m) = D(m/2)*D(m/2)来计算,时间复杂度即为O(V^3*lgV).

a48b070670274c0a57b52d00e56dd62b.png

Floyd-Warshall算法

定义dijk表示从i到j考虑了从中间节点{1,2,3…k}经过的所有情况的最小值。当k=n时,即考虑了所有顶点,此时即为最短路径。

代码如下:

// floyd-warshall算法求最短路径问题

void Graph::floydWarshall(){

int Adj[maxSize][maxSize];

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

minAllDistance[i][j] = MAXPRICE;

if(i == j) minAllDistance[i][j] = 0;

AllPath[i][j] = intToStr(i);

Adj[i][j] = minAllDistance[i][j];

}

}

for(int i = 0; i < vertexNum; i++){

GNode *p = edges[i].next;

while(p != NULL){

minAllDistance[i][p->val] = p->weight; // 经过了1条边 即为权重

Adj[i][p->val] = p->weight;

AllPath[i][p->val] = AllPath[i][p->val] + intToStr(p->val);

p = p->next;

}

}

for(int k = 0; k < vertexNum; k++){

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

if(minAllDistance[i][j] > minAllDistance[i][k] + minAllDistance[k][j]){

minAllDistance[i][j] = minAllDistance[i][k] + minAllDistance[k][j];

AllPath[i][j] = AllPath[i][k] + AllPath[k][j];

}

}

}

}

// 检测是否存在负权环

for(int i = 0; i < vertexNum; i++){

if(minAllDistance[i][i] < 0){

cout << "该图存在负权环" << endl;

return;

}

}

// 将全对最短路径输出

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

cout << "从" <

结果如下:

ff4409131a41f248a9f32b29e1a4c975.png

时间复杂度为O(V^3).

Johnson算法

Johnson算法采用了重赋权重方法。重赋权重方法不会改变最短路径,此外通过重赋权重产生的都是非负权重。

伪代码:

34c64ff554e76f056965705e55c14ec8.png

时间复杂度为O(V)*Dijkstra算法。

算法全部代码:

graph.h

#ifndef GRAPH_H

#define GRAPH_H

#include #include #include #include #include using namespace std;

#define maxSize 10

#define MAXPRICE 0x10000000 // 将此值设为权值的最大值

struct GNode{

int val;

int weight;

GNode *next;

};

class Graph{

public:

void createGraph(int n, int e);

void destroyGraph(GNode *p);

~Graph(){

for(int i = 0; i < vertexNum; i++){

destroyGraph(edges[i].next);

//cout << "析构:" << i << endl;

}

}

void showGraph();

void bfsTravel(); // 广度遍历

void dfsTravel(); // 深度遍历

void showTopSort(); // 输出拓扑序列

void componentSC(); // 建立图g的强连通分量

void prim();

void dijkstra();

void bellmanFord();

// 动态规划求全最短路径

void dpAllDistance();

void floydWarshall();

private:

int vertex[maxSize]; // 存放顶点

GNode edges[maxSize]; // 存放邻接表

int vertexNum; //顶点个数

int edgesNum; //边条数

//bfs and dfs 遍历

int visited[maxSize];

void bfs(int s);

void dfs(int s);

int beginTime[maxSize]; // 深度开始访问x的时间

int endTime[maxSize]; // 结束访问x的时间

static int time;

vectortopSort; // topSort的逆序为有向无回路的拓扑排序

void buildTransGraph(Graph &g); // 建立图g的转置

// prim

mapmPriceQueue; // 保存最小生成树和dijkstra算法的造价

pairgetMinPriceVet();

int preNode[maxSize]; // 用于保存最小生成树的边 值为该结点的前驱

// 保存dijkstra 路径

string path[maxSize]; // 保存最终的最短路径

int minDistance[maxSize]; // 保存最终的最小距离

string intToStr(int i);

// 保存所有路劲

int minAllDistance[maxSize][maxSize];

string AllPath[maxSize][maxSize];

};

#endifgraph.cpp

#include #include "graph.h"

#include #include #include using namespace std;

int Graph::time = 0;

void Graph::createGraph(int n, int e){

vertexNum = n;

edgesNum = e;

for(int i = 0; i < n; i++){

vertex[i] = i;

edges[i].val = i;

edges[i].weight = 0;

edges[i].next = NULL;

}

for(int i = 0; i < e; i++){

int source, dest, wei;

cin >> source >> dest >> wei;

GNode *newNode = new GNode();

newNode->val = dest;

newNode->weight = wei;

newNode ->next = NULL;

GNode *p = &edges[source];

while(p->next != NULL) p = p->next;

p->next = newNode;

// 无向图 有向图就将这段删除掉

/*GNode *newNode2 = new GNode();

newNode2->val = source;

newNode2->weight = wei;

newNode2 ->next = NULL;

GNode *p2 = &edges[dest];

while(p2->next != NULL) p2 = p2->next;

p2->next = newNode2;*/

}

}

void Graph::destroyGraph(GNode *p){

if(p == NULL) return;

else{

destroyGraph(p->next);

delete p;

}

}

void Graph::showGraph(){

for(int i = 0; i < vertexNum; i++){

int j = i;

cout << i << "->";

GNode *p = edges[j].next;

while( p != NULL) {

cout << "(" << p->val

p = p->next;

}

cout << endl;

}

}

// 广度遍历图

void Graph::bfs(int s){

queueq;

q.push(s);

visited[s] = 1;

while(!q.empty()){

int u = q.front();

q.pop();

cout << u

q.push(p->val);

visited[p->val] = 1;

}

p = p->next;

}

}

}

void Graph::bfsTravel(){

memset(visited, 0, sizeof(int)*vertexNum);

for(int i = 0; i < vertexNum; i++){

if(!visited[i]){

bfs(i);

cout << endl;

}

}

}

// 深度优先遍历

void Graph::dfs(int s){

visited[s] = 1;

time += 1;

beginTime[s] = time;

cout << s << "(" << beginTime[s] << " "; // shen

GNode *p = edges[s].next;

while(p != NULL){

if(!visited[p->val])

dfs(p->val);

p = p->next;

}

time += 1;

endTime[s] = time;

topSort.push_back(s);

cout << endTime[s] << ")" <::reverse_iterator iter="topSort.rbegin();" topsort.rend>

cout << *iter << " ";

cout << endl;

}

// 创建图g的转置

void Graph::buildTransGraph(Graph &g){

this->vertexNum = g.vertexNum;

this->edgesNum = g.edgesNum;

for(int i = 0; i < vertexNum; i++){

this->vertex[i] = g.vertex[i];

this->edges[i].val = g.edges[i].val;

this->edges[i].weight = g.edges[i].weight;

this->edges[i].next = NULL;

}

for(int i = 0; i < vertexNum; i++){

GNode *p = g.edges[i].next;

while(p != NULL){

GNode *newNode = new GNode();

newNode->val = i;

newNode->next = NULL;

newNode->weight = p->weight;

GNode *q = &edges[p->val];

while(q->next != NULL) q = q->next;

q->next = newNode;

p = p->next;

}

}

}

//强连通分量

void Graph::componentSC(){

//time = 0;

//dfsTravel(); // 对图g进行深度搜索得到完成x访问所需要的时间 并由此得到其拓扑排序

Graph g2;

g2.buildTransGraph(*this); // 得到图G的转置

time = 0;

memset(g2.visited, 0, sizeof(int)*vertexNum);

cout << "强连通分量: " << endl;

for(vector::reverse_iterator iter = topSort.rbegin(); iter != topSort.rend(); iter++){ // 对转置图g2进行深度搜索得到强连通分量

if(!g2.visited[*iter])

g2.dfs(*iter);

}

cout << endl;

}

// 最小生成树

/*最小生成树采用的是贪心算法:设T是图G的MST(minimum spanning tree),

A属于V, (u,v)是连接A与V-A之间的一条边且权值最小,则(u,v)边属于最小生成树

lowcost存放代价*/

pairGraph::getMinPriceVet(){

pairp;

int minPrice = MAXPRICE;

int minPriceVet = 0;

for(map::iterator iter = mPriceQueue.begin(); iter != mPriceQueue.end(); iter++){

if((*iter).second < minPrice){

minPrice = (*iter).second;

minPriceVet = (*iter).first;

p = *iter;

}

}

mPriceQueue.erase(minPriceVet); // 或者采用数组 此时就需要设置visited标记数组来判断该顶点是否已经加入S中了

return p;

}

void Graph::prim(){

mPriceQueue.clear();

mPriceQueue[0] = 0;

for(int i = 1; i < vertexNum; i++){

mPriceQueue[i] = MAXPRICE;

}

while(!mPriceQueue.empty()){

pairpMinNode = getMinPriceVet();

int minPriceVet = pMinNode.first;

GNode *p = edges[minPriceVet].next;

while(p != NULL){

if(mPriceQueue.count(p->val) && p->weight < mPriceQueue[p->val]){

mPriceQueue[p->val] = p->weight;

preNode[p->val] = minPriceVet;

}

p = p->next;

}

}

// 展示最小生成树的边

for(int i = 1; i < vertexNum; i++)

{

cout << "(" << preNode[i] << "," << i << ") ";

}

cout << endl;

}

// dijkstra 算法

string Graph::intToStr(int i){

string s;

stringstream ss;

ss << i;

ss >>s;

ss.clear();

return s;

}

void Graph::dijkstra(){

mPriceQueue.clear();

mPriceQueue[0] = 0;

path[0] = intToStr(0);

for(int i = 1; i < vertexNum; i++){

mPriceQueue[i] = MAXPRICE;

path[i] = "";

}

while(!mPriceQueue.empty()){

pairpMinNode = getMinPriceVet(); // dijkstra的贪心策略就是该点就是此时的pMinNode.second就是最短路径

int minPriceVet = pMinNode.first;

minDistance[minPriceVet] = pMinNode.second; // 保存最短路径到minDistance中

GNode *p = edges[minPriceVet].next;

while(p != NULL){

if(mPriceQueue.count(p->val) && (p->weight + pMinNode.second) < mPriceQueue[p->val]){ // 松弛操作

mPriceQueue[p->val] = p->weight + pMinNode.second;

path[p->val] = path[minPriceVet] + intToStr(p->val);

}

p = p->next;

}

}

// 输出路径

for(int i = 0; i < vertexNum; i++){

cout << "从0到" << i << "的最短路径为: " p->weight + minDistance[i]){ // 松弛每一条边

minDistance[p->val] = minDistance[i] + p->weight;

path[p->val] = path[i] + intToStr(p->val);

}

p = p->next;

}

}

}

for(int i = 0; i < vertexNum; i++){ // 检查是否存在负权回路

GNode *p = edges[i].next;

while(p != NULL){

if(minDistance[p->val] > p->weight + minDistance[i]){

cout << "该图存在负权环" << endl;

return;

}

p = p->next;

}

}

// 输出路径

for(int i = 0; i < vertexNum; i++){

cout << "从0到" << i << "的最短路径为: " weight; // 经过了1条边 即为权重

Adj[i][p->val] = p->weight;

AllPath[i][p->val] = AllPath[i][p->val] + intToStr(p->val);

p = p->next;

}

}

for(int m = 2; m < vertexNum; m++){ // 至多经过m条边

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

for(int k = 0; k < vertexNum; k++){

if(minAllDistance[i][j] > minAllDistance[i][k] + Adj[k][j]){

minAllDistance[i][j] = minAllDistance[i][k] + Adj[k][j];

AllPath[i][j] = AllPath[i][k] + intToStr(j);

}

}

}

}

}

// 检测是否存在负权环

for(int i = 0; i < vertexNum; i++){

if(minAllDistance[i][i] < 0){

cout << "该图存在负权环" << endl;

return;

}

}

// 将全对最短路径输出

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

cout << "从" weight; // 经过了1条边 即为权重

Adj[i][p->val] = p->weight;

AllPath[i][p->val] = AllPath[i][p->val] + intToStr(p->val);

p = p->next;

}

}

for(int k = 0; k < vertexNum; k++){

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

if(minAllDistance[i][j] > minAllDistance[i][k] + minAllDistance[k][j]){

minAllDistance[i][j] = minAllDistance[i][k] + minAllDistance[k][j];

AllPath[i][j] = AllPath[i][k] + AllPath[k][j];

}

}

}

}

// 检测是否存在负权环

for(int i = 0; i < vertexNum; i++){

if(minAllDistance[i][i] < 0){

cout << "该图存在负权环" << endl;

return;

}

}

// 将全对最短路径输出

for(int i = 0; i < vertexNum; i++){

for(int j = 0; j < vertexNum; j++){

cout << "从" <

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是带权最短路径算法——Dijkstra算法Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int MAX = Integer.MAX_VALUE; public static void dijkstra(int[][] graph, int src) { int n = graph.length; int[] dist = new int[n]; boolean[] visited = new boolean[n]; // 初始化dist数组和visited数组 for (int i = 0; i < n; i++) { dist[i] = MAX; visited[i] = false; } dist[src] = 0; for (int i = 0; i < n - 1; i++) { // 找到dist值最小的点 int u = minDistance(dist, visited); visited[u] = true; // 更新u的邻接点的dist值 for (int v = 0; v < n; v++) { if (!visited[v] && graph[u][v] != 0 && dist[u] != MAX && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } // 输出最短路径 printSolution(dist); } private static int minDistance(int[] dist, boolean[] visited) { int min = MAX; int minIndex = -1; for (int v = 0; v < dist.length; v++) { if (!visited[v] && dist[v] <= min) { min = dist[v]; minIndex = v; } } return minIndex; } private static void printSolution(int[] dist) { System.out.println("Vertex \t Distance from Source"); for (int i = 0; i < dist.length; i++) { System.out.println(i + " \t\t " + dist[i]); } } public static void main(String[] args) { int[][] graph = {{0, 2, 0, 6, 0}, {2, 0, 3, 8, 5}, {0, 3, 0, 0, 7}, {6, 8, 0, 0, 9}, {0, 5, 7, 9, 0}}; dijkstra(graph, 0); } } ``` 以上代码中,我们首先定义了一个`MAX`常量表示整数的最大值。然后,我们通过`dijkstra()`方法实现Dijkstra算法。在该方法中,我们首先初始化一个`dist`数组和一个`visited`数组,分别用于存储各个节点的距离和是否已经访问过。然后,我们将起始节点的距离设为0,其余节点的距离设为`MAX`。 接下来,我们在循环中找到`dist`值最小的未访问过的节点,将其标记为已访问,并更新其邻接点的距离。在循环结束后,我们将输出最短路径,即输出各个节点距离起点的距离。 最后,我们通过`main()`方法测试了上面的代码,输出的结果为: ``` Vertex Distance from Source 0 0 1 2 2 3 3 6 4 8 ``` 这说明从节点0到各个节点的最短路径分别为0、2、3、6和8。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值