写一个图的算法(一)
删除图中的节点
删除6结点
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class DeleteNode{
/* static List<LinkedList<Integer>> templist; */
static List<LinkedList<Integer>> totallist;
static List<LinkedList<Integer>> Normallist;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int M = sc.nextInt();
totallist = new ArrayList<LinkedList<Integer>>();
Normallist = new ArrayList<LinkedList<Integer>>();
for(int i=0;i<N;i++) {
totallist.add(new LinkedList<Integer>());
Normallist.add(new LinkedList<Integer>());
}
for(int i=0;i<M;i++) {
int s = sc.nextInt();
int e = sc.nextInt();
/* if(hasEdge(s-1,e-1))continue; */
totallist.get(s-1).add(e-1);
totallist.get(e-1).add(s-1);
Normallist.get(s-1).add(e-1);
Normallist.get(e-1).add(s-1);
}
System.out.println(" ");
List<LinkedList<Integer>> deleteNode = deleteNode(6);
System.out.println(" ");
}
private static boolean hasEdge(int i, int j) { // TODO Auto-generated method
for(Integer m:totallist.get(i)) { if(m==j) return true; } return false;
}
private static List<LinkedList<Integer>> deleteNode(int i) {
List<LinkedList<Integer>> templist = new ArrayList<LinkedList<Integer>>(totallist);
templist.remove(i);
templist.add(i,new LinkedList<Integer>());
for(int j=0;j<templist.size();j++) {
LinkedList<Integer> temp = templist.get(j);
Iterator<Integer> it = temp.iterator();
while(it.hasNext()) {
Integer intNum = it.next();
if(i==intNum.intValue()) { //引用变量Integer超出255范围后==比较为false,拆箱
it.remove(); //迭代器进行删除
}
}
}
totallist.clear();
totallist.addAll(Normallist);
return templist;
//去除集合中的重复元素
/* templist.remove(i); */
/* for(LinkedList item:templist) {
HashSet<Integer> hashSet = new HashSet<>(item);
item.clear();
item.addAll(hashSet);
}*/
}
}
测试用例:
10 8
1 5
3 9
9 7
7 4
6 7
10 5
10 2
8 5
输出:


并查集算法
并查集判断两点是否相连与路径压缩
并查集常用来解决连通性的问题,即将一个图中连通的部分划分出来。当我们判断图中两个点之间是否存在路径时,就可以根据判断他们是否在一个连通区域。
并查集的思想就是,同一个连通区域内的所有点的根节点是同一个。将每个点映射成一个数字。先假设每个点的根节点就是他们自己,然后我们以此输入连通的点对,然后将其中一个点的根节点赋成另一个节点的根节点,这样这两个点所在连通区域又相互连通了。 并查集的主要操作有:
- find(int m):这是并查集的基本操作,查找m的根节点。
- isConnected(int m,int n):判断m,n两个点是否在一个连通区域。
- union(int m,int n):合并m,n两个点所在的连通区域。
class UnionFind {
    int[] parents;
    public UnionFind(int totalNodes) {
        parents = new int[totalNodes];
        for (int i = 0; i < totalNodes; i++) {
            parents[i] = i;
        }
    }
        // 合并连通区域是通过find来操作的, 即看这两个节点是不是在一个连通区域内.
    void union(int node1, int node2) {
        int root1 = find(node1);
        int root2 = find(node2);
        if (root1 != root2) {
            parents[root2] = root1;
        }
    }
    int find(int node) {
        while (parents[node] != node) {
            // 当前节点的父节点 指向父节点的父节点.
            // 保证一个连通区域最终的parents只有一个.
            parents[node] = parents[parents[node]];
            node = parents[node];
        }
        return node;
    }
    boolean isConnected(int node1, int node2) {
        return find(node1) == find(node2);
    }
}
典型案例:
判断没有与0相连结点的个数

测试用例:
8 7
0 1
3 0
1 2
3 7
8 6
2 5
7 2
并查集实现
public class UF {
    static int[] parents;
    static int[] ranks;//基于高度的优化
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        parents = new int[N+1];
        ranks = new int[N+1];
        for(int i = 0;i<N+1;i++) {
            parents[i] = i;
            ranks[i] = 1;
        }
        for(int i=0;i<M;i++){
            int s = scanner.nextInt();
            int e = scanner.nextInt();
            unionElement(s,e);
        }
        int count = 0;
        for(int i =0;i<N+1;i++) {
            if(!isConnected(0, i)) {
                count++;
            }
        }
        System.out.println(count);
    }
    private static void unionElement(int s, int e) {
        // TODO Auto-generated method stub
        int pRoot = find(s);
        int qRoot = find(e);
        if(pRoot==qRoot) return;
        if(ranks[pRoot]<ranks[qRoot]) {
            parents[pRoot] = qRoot; //高度小的节点指向高度比较高的节点
        }else if(ranks[qRoot]<ranks[pRoot]) {
            parents[qRoot] = pRoot;
        }else {
            parents[qRoot] = pRoot;
            ranks[pRoot]+=1;
        }
    }
    private static boolean isConnected(int x,int y) {
        return find(x) == find(y);
        }
    
    private static int find(int n) {
        if(n!=parents[n])
            parents[n] = find(parents[n]);//路径压缩
        return parents[n];
    }
}输出:
3
经典案例
给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。
找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
我们的思路是把所有边界上的O看做一个连通区域。遇到O就执行并查集合并操作,这样所有的O就会被分成两类
- 和边界上的O在一个连通区域内的。这些O我们保留。
- 不和边界上的O在一个连通区域内的。这些O就是被包围的,替换。
由于并查集我们一般用一维数组来记录,方便查找parants,所以我们将二维坐标用node函数转化为一维坐标。
实现
public class IsandsFind {
   static char[][] grid = {
           {'x','x','x','x'},
           {'x','0','0','x'},
           {'x','x','0','x'},
           {'x','0','x','x'}
       };
   static int[] parents;
   static int[] ranks;
   static int m;
   static int n;
    public static void main(String[] args) {
   m = grid.length;
   n = grid[0].length;
   parents = new int[m*n+1];
   ranks = new int[m*n+1];
   int dummyNode = m*n;
   for(int i=0;i<m*n+1;i++) {
      parents[i] = i;
      ranks[i] =1;
   }
   for(int i=0;i<m;i++) {
      for(int j=0;j<n;j++) {
         if(grid[i][j]=='0') {
            // 遇到O进行并查集操作合并
            if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
                    // 边界上的O,把它和dummyNode 合并成一个连通区域.
               unionElement(node(i, j), dummyNode);
                } else {
                    // 和上下左右合并成一个连通区域.
                    if (i > 0 && grid[i - 1][j] == '0')
                     unionElement(node(i, j), node(i - 1, j));
                    if (i < m - 1 && grid[i + 1][j] == '0')
                     unionElement(node(i, j), node(i + 1, j));
                    if (j > 0 && grid[i][j - 1] == '0')
                     unionElement(node(i, j), node(i, j - 1));
                    if (j < n - 1 && grid[i][j + 1] == '0')
                     unionElement(node(i, j), node(i, j + 1));
                }
         }
      }
   }
   
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (isConnected(node(i, j), dummyNode)) {
                // 和dummyNode 在一个连通区域的,那么就是O;
               grid[i][j] = '0';
            } else {
               grid[i][j] = 'x';
            }
        }
    }
    System.out.println(grid);
    }
   private static int node(int i,int j) {
      return i*n+j;
   }
   
   private static boolean isConnected(int x,int y) {
      return find(x)==find(y);
   }
   private static int find(int n) {
      // TODO Auto-generated method stub
      if(n!=parents[n])
         parents[n] = find(parents[n]);
      return parents[n];
   }
   private static void unionElement(int s,int e) {
      int pRoot = find(s);
      int qRoot = find(e);
      if(pRoot==qRoot) return;
      if(ranks[pRoot]<ranks[qRoot]) {
         parents[pRoot] = qRoot;
      }else if(ranks[qRoot]<ranks[pRoot]) {
         parents[qRoot] = pRoot;
      }else {
         parents[qRoot] = pRoot;
         ranks[pRoot]+=1;
      }
   }
}输出:

图的遍历-深度优先搜索
DFS获取无向图中的所有路径

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
public class Path {
private int[] form;
private int s;
private Graph graph;
private boolean[] visited;
private List<int[]> totalList;
public Path(int s, Graph graph) {
super();
this.s = s;
this.graph = graph;
visited = new boolean[graph.V()];
form = new int[graph.V()];
for(int i=0;i<graph.V();i++) {
form[i] = -1;
}
totalList = new ArrayList<>();
int toIndex = 6;
DFS(s,toIndex);
}
private boolean hasPath(int w) {
return form[w]!=-1;
//return visited[w];
}
private List<List<Integer>> path(int w){
List<List<Integer>> retlist = new ArrayList<>();
for(int[] tempForm:totalList) {
List<Integer> list = new ArrayList<>();
Deque<Integer> deque = new LinkedList<>();
int p = w;
while(p!=-1) {
deque.push(p);
p = tempForm[p];
}
while(!deque.isEmpty()) {
list.add(deque.pop());
}
retlist.add(list);
}
return retlist;
}
//showPath(6)
void showPath(int w) {
assert hasPath(w);
List<List<Integer>> path = path(w);
for(int i=0;i<path.size();i++) {
for(int j=0;j<path.get(i).size();j++) {
System.out.println(path.get(i).get(j));
if(j!=path.get(i).size()-1)
System.out.print(" -> ");
else
System.out.println();
}
}
}
private void DFS(int i,int toIndex) {
// TODO Auto-generated method stub
visited[i] = true;
if(i==toIndex) {
visited[i] = false;
return;
}
for(int item :graph.adj(i)) {
if(!visited[item]) {
form[item] = i;
if(item==toIndex) {
visited[i] = false;
int[] temp = Arrays.copyOf(form, form.length);
totalList.add(temp);
}
DFS(item,toIndex);
//form[item] = i;
/*if(item==toIndex) {
int[] temp = Arrays.copyOf(form, form.length);
totalList.add(temp);
Arrays.fill(form, -1);
}*/
}
}
visited[i] = false;
form[i] = -1;
}
}
打印输出:

DFS获取图的连通分量判断两点是否连接
深度遍历是把与i相连接的,不是相邻近的所有的节点遍历一边,没遍历的节点一定在其他的联通分量中
public class Compents {
 Graph graph;
 private boolean[] visited;
 private int count;
 private int[] ids;
public Compents(Graph graph) {
  this.graph = graph;
  int V = graph.V();
  visited = new boolean[V];
  ids = new int[V];
  count = 0;
  for(int i=0;i<V;i++) {
    ids[i] = -1;
  }
  //connect numbers 
  for(int i=0;i<V;i++) {
    if(!visited[i]) {
      DFS(i);
      count++;
    } 
  }
}
private void DFS(int i) {
  // TODO Auto-generated method stub
  visited[i] = true;
  ids[i] = count;
  for(int item :graph.adj(i)) {
    if(!visited[item])
      DFS(item);
  }
  
}
 
int count() {
  return count;
}
  
boolean isConnected(int v,int w) {
  return ids[v] == ids[w];
}
  
  
}DFS遍历有向无环有权图任意两点间最优路径

DFS(0,7); 计算0-7所有路径中权值最小的
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
/**
* 13:22:38 13:26:13 13:47:39 14:52:02 13:53:51 13:55:37 14:56:52
* 13:57:55 14:01:08 14:02:55 14:08:10 14:11;35 14:16:18 14:17:44
* 14:19:04 14:22:58 14:25:29
* @author Administrator
*
*/
public class weighDirRoute {
static List<List<int[]>> totallist;
static boolean[] visited;
static int MaxCount;
static List<Integer> compareMax;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
int M = scanner.nextInt();
totallist = new ArrayList<>();
compareMax= new ArrayList<>();
visited = new boolean[N];
for(int i=0;i<N;i++) {
totallist.add(new LinkedList<>());
}
for(int i=0;i<M;i++) {
int s = scanner.nextInt();
int e = scanner.nextInt();
int w = scanner.nextInt();
totallist.get(s).add(new int[]{s,e,w});
}
MaxCount = 0;
DFS(0,7);
Integer answer = Collections.min(compareMax);
System.out.println(answer);
}
private static void DFS(int i,int toIndex) {
// TODO Auto-generated method stub
visited[i] = true;
for(int[] items:totallist.get(i)) {
int item = items[1];
int Count = items[2];
if(!visited[item]) {
MaxCount+=Count;
if(item==toIndex) {
compareMax.add(MaxCount);
}
DFS(item,toIndex);
/*if(item==toIndex) {
compareMax.add(MaxCount);
}*/
MaxCount-=Count;
}
}
visited[i] = false;
}
}

输出:

DFS计算节点间能到达的最远距离
无向图


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
/**
* @author Administrator
* 6:52:38 6:53:51
*/
public class ND {
static int[][] Map;
static boolean[] visited;
static List<LinkedList<Integer>> totalList;
static int MaxCount;
static int Count;
static int N;
static int R;
static int C;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();//点的个数
int M = sc.nextInt();
Map = new int[N][N];
totalList = new ArrayList<LinkedList<Integer>>();
for(int j=0;j<N;j++) {
LinkedList<Integer> itemList = new LinkedList<Integer>();
totalList.add(itemList);
}
for(int i=0;i<M;i++) {
int startPoint = sc.nextInt();
int endPoint = sc.nextInt();
List<Integer> itemsList = new LinkedList<Integer>();
itemsList.add(startPoint);
itemsList.add(endPoint);
totalList.get(itemsList.get(0)-1).add(itemsList.get(1)-1);
totalList.get(itemsList.get(1)-1).add(itemsList.get(0)-1);
}
MaxCount = 0;
for(int i=0;i<N;i++) {
visited = new boolean[N];
Count = 0;
if(totalList.get(i).size()>0)
DFS(i);
}
System.out.println(MaxCount);
}
private static void DFS(int i) {
// TODO Auto-generated method stub
MaxCount = MaxCount>Count?MaxCount:Count;
visited[i] = true;
for(Integer items : totalList.get(i)) {
if((!visited[items])) {
Count++;
DFS(items);
}
}
return;
}
}
输出:

有向图

public class DirectGraph {
    static int[][] Map;
    static boolean[] visited;
    static List<LinkedList<Integer>> totalList;
    static int MaxCount;
    static int Count;
    static int N;
    static int R;
    static int C;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
         N = sc.nextInt();//点的个数
         int M = sc.nextInt();
         Map = new int[N][N];
         totalList = new ArrayList<LinkedList<Integer>>();
             for(int j=0;j<N;j++) {
               LinkedList<Integer> itemList = new LinkedList<Integer>();
               totalList.add(itemList);
             }
             
        for(int i=0;i<M;i++) {
            int startPoint = sc.nextInt();
            int endPoint = sc.nextInt();
            /*List<Integer> itemsList = new LinkedList<Integer>();
            itemsList.add(startPoint);
            itemsList.add(endPoint);*/
            totalList.get(startPoint-1).add(endPoint-1);
            //totalList.get(itemsList.get(1)-1).add(itemsList.get(0)-1);
        }    
             
             
         MaxCount = 0;
         for(int i=0;i<N;i++) {
             visited = new boolean[N];
             Count = 0;
             if(totalList.get(i).size()>0)
             DFS(i);
         }
         
         System.out.println(MaxCount);
        
    }
    private static void DFS(int i) {
        // TODO Auto-generated method stub
        MaxCount = MaxCount>Count?MaxCount:Count;
         visited[i] = true;
         for(Integer items : totalList.get(i)) {
          if((!visited[items])) {
              Count++;
              DFS(items);
              //Count--;
          }
         }
         Count--;
         return;
    }
}输出:
2
有向图要--
图的遍历-广度优先搜索
广度优先计算无向图的最短路径
广度优先搜索算法(Breadth-First-Search),是一种图形搜索算法。简单的说,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止。BFS同样属于盲目搜索。一般用队列数据结构来辅助实现BFS算法。
算法步骤:
1. 首先将根节点放入队列中。
2. 从队列中取出第一个节点,并检验它是否为目标。
如果找到目标,则结束搜寻并回传结果。
否则将它所有尚未检验过的直接子节点加入队列中。
3. 若队列为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传"找不到目标"。
4. 重复步骤2。

广度一次将一个图的相邻节点全部遍历,然后遍历相邻节点的相邻节点,需要使用队列作为辅助的数据结构
计算0-6的最短路径

广度优先求最短路径
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class ShortestPath {
private Graph graph;
private boolean[] visited;
private int s;
private int[] form;
private int[] ords;
public ShortestPath(Graph graph, int s) {
super();
this.graph = graph;
this.s = s;
int V = graph.V();
visited = new boolean[V];
form = new int[V];
ords = new int[V];
Arrays.fill(form, -1);
Arrays.fill(ords, -1);
Queue<Integer> queue = new LinkedList<>();
queue.add(s);
ords[s] = 0;
visited[s] = true;
while(!queue.isEmpty()) {
Integer top = queue.poll();
for(int item:graph.adj(top)) {
if(!visited[item]) {
queue.add(item);
visited[item] = true;
form[item] = top;
ords[item] = ords[top]+1;
}
}
}
}
public boolean hasPath(int w) {
return visited[w];
}
public List<Integer> path(int w){
assert hasPath(w);
List<Integer> list = new ArrayList<>();
Deque<Integer> deque = new LinkedList<>();
int p = w;
while(p!=-1) {
deque.push(p);
p = form[p];
}
while(!deque.isEmpty()) {
list.add(deque.pop());
}
return list;
}
public void showPath(int w) {
assert hasPath(w);
List<Integer> path = path(w);
for(int i=0;i<path.size();i++) {
System.out.println(path.get(i));
if(i==path.size()-1) {
System.out.println();
}else {
System.out.print("->");
}
}
}
}
输出:

ords距离0结点的距离

form计算从那个结点过来的,用于输出路径











