有权图的最短路径要用DijKstra
lc310 树的中心
1 | Input: n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] |
关键:中心(到所有叶节点路径最小)只可能有1个或者2个
一层一层的褪去叶节点,
最后剩下的一个或两个节点就是我们要求的最小高度树的根节点
所有只有一个连接边的节点(叶节点)都存入到一个队列queue中,然后我们遍历每一个叶节点,通过图来找到和其相连的节点,并且在其相连节点的集合中将该叶节点删去,如果删完后此节点也也变成一个叶节点了,加入队列中,再下一轮删除。
当节点数小于等于2时候停止,此时剩下的一个或两个节点就是我们要求的最小高度树的根节点啦
[0]-<3> leaf
[1]-<3> leaf
[2]-<3> leaf
[3]-<0,1,2,4>
[4]-<3,5>
[5]-<4> leaf
->
[3]-<4>
[4]-<3>3>4>4>3>3>3>
279完美平方数
距离场
距离场的题不适合用dfs,递归全部完成才建立好距离场。
lt 803 建筑物之间的最短距离
1 | 盖房子,在最短的距离内到达所有的建筑物。 |
思路:
1.BFS可以求每个0到这个建筑物的距离。BFS保证每个可达的格子只访问1次肯定是最短距离。
2.用一个矩阵累加空地到所有建筑物的距离。并且计数这是累加了几个建筑物的。
3.遍历累加距离矩阵中累加了和1相等的数量的格子中最小的那个。
int[n][m][2]
每个位置(2,1)表示从一个建筑物出发,走两步到达这个格子。
从另一个建筑物出发,叠加距离变成(4,2)表示第二个建筑物走两步到达这个格子。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public int shortestDistance(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int[][][] res = new int[n][m][2];
// 城市的数量
int count = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(grid[i][j] == 1) {
count++;
bfs(grid, i, j, res);
}
// System.out.println();
}
}
int min = Integer.MAX_VALUE;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
if(res[i][j][1] == count)
min = Math.min(min, res[i][j][0]);
return min;
}
void bfs(int[][] grid, int i, int j, int[][][] res) {
int n = grid.length;
int m = grid[0].length;
Queue<int[]> q = new LinkedList<>();
q.offer(new int[]{i, j});
boolean[][] v = new boolean[n][m];
int step = 1;
while(!q.isEmpty()) {
int size = q.size();
while (size-- > 0){
int[] cur = q.poll();
for(int[] dir : dirs) {
int x = cur[0] + dir[0];
int y = cur[1] + dir[1];
// 四个方向继续找空地
if(x < 0 || x >= n || y < 0 || y >= m || grid[x][y] != 0 || v[x][y]) continue;
q.offer(new int[]{x, y});
res[x][y][0] += step;
res[x][y][1] += 1;
v[x][y] = true;
}
}
// System.out.println(Arrays.deepToString(res));
step++;
}
}
lt573 邮局建立
同上
1 | public int shortestDistance(int[][] grid) { |
542 01矩阵 变成离0距离的矩阵
1 | 0 0 0 |
还可以dp todo
还可以dfs todo
1.把0都放入队列,非零格子最大化
2.如果bfs到这个格子的距离比格子的值小就更新。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32int[][] ori = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
public int[][] updateMatrix(int[][] matrix) {
int n = matrix.length;
int m = matrix[0].length;
Queue<int[]> que = new LinkedList<>();
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j] == 0) {
que.add(new int[]{i, j});
}else{
matrix[i][j] = Integer.MAX_VALUE;
}
}
}
while (!que.isEmpty()) {
int[] top = que.poll();
int curx = top[0];
int cury = top[1];
for (int i = 0; i < ori.length; i++) {
int newx = curx + ori[i][0];
int newy = cury + ori[i][1];
if (newx < 0 || newx >= n || newy < 0 || newy >= m ||
matrix[newx][newy] <= matrix[curx][cury] +1)
continue;
matrix[newx][newy] = matrix[curx][cury] + 1;
que.add(new int[]{newx, newy});
}
}
return matrix;
}
lt 663 墙和门
您将获得一个使用这三个可能值初始化的 m×n 2D 网格。
-1 - 墙壁或障碍物。
0 - 门。
INF - Infinity是一个空房间。我们使用值 2 ^ 31 - 1 = 2147483647 来表示INF,您可以假设到门的距离小于 2147483647
在代表每个空房间的网格中填入到距离最近门的距离。
如果不可能到达门口,则应填入 INF。
给定 2D 网格:
1
2
3
4 INF -1 0 INF
INF INF INF -1
INF -1 INF -1
0 -1 INF INF
返回结果:1
2
3
43 -1 0 1
2 2 1 -1
1 -1 2 -1
0 -1 3 4
dfs 1415 ms1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
public void wallsAndGates(int[][] rooms) {
int n = rooms.length;
int m = rooms[0].length;
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
if(rooms[i][j] == 0)
dfs(rooms,i,j,0,m,n);}}}
private void dfs(int[][] rooms,int x,int y,int d,int m,int n){
if(rooms[x][y] > d || d == 0){
rooms[x][y] = d;
for(int i = 0;i< dirs.length ;i++){
int newx = x + dirs[i][0];
int newy = y + dirs[i][1];
if(newx>=n || newy>=m ||newx<0||newy<0 ||rooms[newx][newy] ==-1)continue;
dfs(rooms,newx,newy,d+1,m,n);
}}
return;
}
BFS方法同上
1 | int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; |
!!126 word Ladder 返回所有可能的路径 记录宽搜搜索路径!
77% 117ms
1.先bfs,在bfs的过程中,构建有向图邻接表map。完成一个最长路径为k的图。
2.在构建完的图中dfs,回溯找完到end的路径tmp添加到rst中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
HashSet<String> words = new HashSet<>(wordList);
words.add(beginWord);
List<List<String>> rst = new ArrayList<>();
HashMap<String,List<String>> graph = new HashMap<>();
// pair <node,step>
HashMap<String,Integer> pairs = new HashMap<>();
ArrayList<String> tmp = new ArrayList<>();
bfs(beginWord,endWord,words,graph,pairs);
dfs(beginWord,endWord,words,graph,pairs,tmp,rst);
return rst;
}
// 从set 中筛选出 步长差1的String
private List<String> getNeib(String top,Set<String> words){
List<String> rst = new ArrayList<>();
char[] chs = top.toCharArray();
// 这两个循环如果反一下 慢30ms (数量小的循环要写外面?)
for(char ch = 'a'; ch <= 'z' ; ch++ ){
for (int i = 0; i <chs.length ; i++) {
if(chs[i] == ch)continue;
char old = chs[i];
chs[i] = ch;
if(words.contains(String.valueOf(chs))){
rst.add(String.valueOf(chs));
}
chs[i] = old;
}
}
return rst;
}
// bfs可以找到所有 k步可达的顶点,并建立起链接, k是到达end的最少步数
private void bfs(String start,String end,Set<String> words,HashMap<String,List<String>> graph,HashMap<String,Integer> pairs){
for(String word : words){
graph.put(word, new ArrayList<>());
}
// 队列只放node 或者结构体把step也带着
Deque<String> que = new ArrayDeque<>();
que.add(start);
pairs.put(start, 0);
while (!que.isEmpty()){
// 这一层
int count = que.size();
boolean found = false;
for (int i = 0; i < count ; i++) {
String top = que.poll();
int step = pairs.get(top);
List<String> neib = getNeib(top, words);
for(String next : neib){
// 构建有向图
graph.get(top).add(next);
// 记录访问过了并且当前访问的步长 不用了visit set
if(!pairs.containsKey(next)){
// 注意找到了也需要先把pair放进去
pairs.put(next, step+1);
// 如果找到了
if(end.equals(next)){
found = true;
}else{
que.add(next);
}
}
}
if(found)break;
}
}
}
private void dfs(String start,String end,Set<String> words,Map<String,List<String>> graph,Map<String,Integer> pairs,List<String> tmp,List<List<String>> res){
tmp.add(start);
if(end.equals(start))
{
res.add(new ArrayList<>(tmp));
}else {
// 从start dfs找这个最长只有k步的图, dfs的条件是1 是邻边表示差1步,2是pair中记录这个邻边是下一个step
for(String next : graph.get(start)){
if(pairs.get(next) == pairs.get(start) + 1){
dfs(next,end,words,graph,pairs,tmp,res);
}
}
}
//如果这条路走不通或者走完了,一段一段删回来
tmp.remove(tmp.size()-1);
}
队列中的结构<顶点,前驱,步数>
!!127 word Ladder bfs最短单词转换路径
todo 为什么复杂度差那么多
//todo双向bfs
注意marked和dfs的不同,
单纯bfs访问wordlist里每个单词1.79% 1097ms
//list.size()*cur.length()
1 | private boolean dif(String difword,String cur){ |
构建图,用map 邻接表,两层for遍历wordList(包括start) ,如果两个字符串只差一个字符,则加到双方邻接表(map)中。
将start放进队列<顶点,步数>
,宽搜,搜到返回步数。
需要markedSet,因为一个点的父节点有多个,将一个点的邻接点放进队列,有的点早被别的父节点就放进队列过了。 AC 17.88% 570ms
1 | class Pair{ |
先改变单词cur.length()*25再查表
47% 97ms
1 | public int ladderLength(String beginWord, String endWord, List<String> wordList) { |
倒水问题 BFS ax + by = m 最大公约数 的倍数都行
有无限的水源,一个5L无刻度桶和一个7L无刻度桶,则只利用这两个无刻度桶,将不能获得()L水
正确答案: F 你的答案: E (错误)
A2
B3
C6
D8
E11
F以上均能获得
365 容量x,y的两个杯子能否量出z
Input: x = 3, y = 5, z = 4
Output: True
Input: x = 2, y = 6, z = 5
Output: False
bfs 4种情况1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public boolean canMeasureWater(int x, int y, int z) {
if(z == 0)return true;
if(x + y < z)return false;
int total = x+y;
Set<Integer> set = new HashSet<>();
Queue<Integer> q = new LinkedList<>();
q.offer(0);
while (!q.isEmpty()){
int n = q.poll();
if(n + x <= total && set.add(n + x)){
q.offer(n + x);
}
if(n + y <= total && set.add(n + y)){
q.offer(n + y);
}
if(n - x >=0 && set.add(n -x)){
q.offer(n-x);
}
if(n -y >=0 &&set.add(n - y)){
q.offer(n - y);
}
if(set.contains(z)){
return true;
}
}
return false;
}
-2*3 + 2*5 = 4
倒掉3的容器2次倒满5的容器2次
1.装满5,倒给3 -> (0,2)
2.装满5,倒给3 -> (4,3)
只要z是x,y的最大公约数的倍数就True
- 裴蜀定理:ax + by = m
有整数解,当且仅当 m是d = gcd(a,b)的倍数
d 是最小的 可以写成ax+by形式的正整数。
ax+by = 1有整数解 当且仅当 a,b互质
1 | public boolean canMeasureWater(int x, int y, int z) { |
hdu 1495 容量S 用N和M的平分
S:可乐体积 N杯子1 M杯子2 N+M=S 三个容器可以互相倒 能不能2人平分
7 4 3
4 1 3
out: 最少次数
NO
3
ax+by = (a+b)/2
第一个瓶子倒入x次第二个倒出y次
倒进倒出都是大瓶子,倒进1次之后继续用还要把小瓶子倒回大瓶子,倒出同理。
但是最后 一定是放在两个小瓶子里不用倒回大瓶子 所以操作数=(a+b)/gcd(a,b)-1
poj 3414 Pots 容量A,B的容器 量出C升水
http://poj.org/problem?id=3414
输入:3 5 4
输出
容量A3 B5 获得4升水的最短序列
6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)
Accepted 3128K 1125MS
1 | class pathNode{ |