写一个图的算法(一)
删除图中的节点
删除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计算从那个结点过来的,用于输出路径