学习内容

LeeCodeJava并查集

778.水位上升的泳池中游泳

思路:

使用并查集.动用一个while循环,每次将水位不足某个特定值的数据进行合并.随后查询右下角的根值是否与左上角的相等,相等则跳出.

我的代码:

  1. class Solution {
  2. public int swimInWater(int[][] grid) {
  3. int time=0;
  4. int h=grid[0].length;
  5. UnionFind unionFind=new UnionFind(grid.length*h);
  6. while(true){
  7. for (int i = 0; i < grid.length; i++) {
  8. for (int j = 0; j < h; j++) {
  9. if(grid[i][j]>time){
  10. continue;
  11. }
  12. int temp=i*h+j;
  13. if(i!=0&&grid[i-1][j]<=time){
  14. unionFind.Union(temp-h,temp);
  15. }
  16. if(i!= grid.length-1&&grid[i+1][j]<=time){
  17. unionFind.Union(temp,temp+h);
  18. }
  19. if(j!=0&&grid[i][j-1]<=time){
  20. unionFind.Union(temp-1,temp);
  21. }
  22. if(j!=h-1&&grid[i][j+1]<=time){
  23. unionFind.Union(temp,temp+1);
  24. }
  25. }
  26. }
  27. if(unionFind.parent[0]==unionFind.parent[grid.length*h-1]){
  28. break;
  29. }
  30. time++;
  31. }
  32. return time;
  33. }
  34. }
  35. class UnionFind{
  36. int[] parent;
  37. //int[] size;
  38. int n;
  39. //int sizeCount;
  40. public UnionFind(int n) {
  41. this.n = n;
  42. parent=new int[n];
  43. for (int i = 0; i < parent.length; i++) {
  44. parent[i]=i;
  45. }
  46. }
  47. public int findParent(int target){
  48. if(target==parent[target]){
  49. return target;
  50. }else{
  51. return findParent(parent[target]);
  52. }
  53. }
  54. public void Union(int a,int b){
  55. a=findParent(a);
  56. b=findParent(b);
  57. if(a!=b){
  58. parent[b]=a;
  59. }
  60. }
  61. }

结果:

超过时间限制
应该是不能使用多次的find_parent.
题目当中水位必定是从0开始,且必定是在最高点结束.

优解代码

  1. public class Solution {
  2. private int N;
  3. public static final int[][] DIRECTIONS = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
  4. public int swimInWater(int[][] grid) {
  5. this.N = grid.length;
  6. int len = N * N;
  7. // 下标:方格的高度,值:对应在方格中的坐标
  8. int[] index = new int[len];
  9. for (int i = 0; i < N; i++) {
  10. for (int j = 0; j < N; j++) {
  11. index[grid[i][j]] = getIndex(i, j);
  12. //将坐标放入高度的哈希表
  13. }
  14. }
  15. UnionFind unionFind = new UnionFind(len);
  16. for (int i = 0; i < len; i++) {
  17. int x = index[i] / N;
  18. int y = index[i] % N;
  19. for (int[] direction : DIRECTIONS) {
  20. int newX = x + direction[0];
  21. int newY = y + direction[1];
  22. if (inArea(newX, newY) && grid[newX][newY] <= i) {
  23. unionFind.union(index[i], getIndex(newX, newY));
  24. }
  25. if (unionFind.isConnected(0, len - 1)) {
  26. return i;
  27. }
  28. }
  29. }
  30. return -1;
  31. }
  32. private int getIndex(int x, int y) {
  33. return x * N + y;
  34. }
  35. private boolean inArea(int x, int y) {
  36. return x >= 0 && x < N && y >= 0 && y < N;
  37. }
  38. private class UnionFind {
  39. private int[] parent;
  40. public UnionFind(int n) {
  41. this.parent = new int[n];
  42. for (int i = 0; i < n; i++) {
  43. parent[i] = i;
  44. }
  45. }
  46. public int root(int x) {
  47. while (x != parent[x]) {
  48. parent[x] = parent[parent[x]];
  49. x = parent[x];
  50. }
  51. return x;
  52. }
  53. public boolean isConnected(int x, int y) {
  54. return root(x) == root(y);
  55. }
  56. public void union(int p, int q) {
  57. if (isConnected(p, q)) {
  58. return;
  59. }
  60. parent[root(p)] = root(q);
  61. }
  62. }
  63. }

心得

1.将二维数组缓存为一维数组有利于减少遍历时的内存耗费
2.尽量减少在循环内部的运算,比较.
3.并查集的最终并不是要求内部的值为同一个,而是要求root(a)==root(b)即可
4.这个方法只需要双重循环的要点是其只关注目前最小高度的那个点,刚好可以将高度最低的几个点连接在一起.我的三重循环方法,虽然会在有重复权值的平台高度的情况下有优势,但是很可惜这里没有涉及.