岛问题,并查集,岛问题的升级

岛问题

 

  1. 题目要求
    1.  
  2. 这道题的解法有很多种
    1. 我采用的是多路递归的方式具体来说就是时间复杂度为O(n)的方式。将已经确定的变成2,看代码
    2. package LiKou.Graph;
      
      public class IsLand {
          public static int getLand(int[][] arr) {
              int sum = 0;
              for (int i = 0; i < arr.length; i++) {
                  for (int j = 0; j < arr[i].length; j++) {
                      if (arr[i][j] == 2 || arr[i][j] == 0) {
                          continue;
                      }
                      sum++;
                      dfs(i, j, arr);
                  }
              }
              return sum;
          }
      
          private static void dfs(int i, int j, int[][] arr) {
              //如果越界,直接return
              if (i < 0 || i > arr.length - 1 || j < 0 || j > arr[0].length - 1) {
                  return;
              }
              //如果不越界,但是你=0;
              if (arr[i][j] == 0 || arr[i][j] == 2) {
                  return;
              }
              //下面是不等于0的状态,且不越界
              arr[i][j] = 2;
              dfs(i + 1, j, arr);
              dfs(i - 1, j, arr);
              dfs(i, j + 1, arr);
              dfs(i, j - 1, arr);
          }
      
          public static void main(String[] args) {
              int[][] arr = new int[4][4];
              arr[0][0] = 0;
              arr[0][1] = 1;
              arr[0][2] = 0;
              arr[0][3] = 0;
      
              arr[1][0] = 1;
              arr[1][1] = 0;
              arr[1][2] = 1;
              arr[1][3] = 1;
      
              arr[2][0] = 0;
              arr[2][1] = 0;
              arr[2][2] = 1;
              arr[2][3] = 0;
      
              arr[3][0] = 0;
              arr[3][1] = 1;
              arr[3][2] = 0;
              arr[3][3] = 1;
      
      
              int i = IsLand.getLand(arr);
              System.out.println(i);
          }
      }
      

       

  3. 问题来了:我现在有一个非常大的二维数组,我想设计一个并行程序(也就是多个cpu),让运行时间成倍的往下降。

引出并查集

  1. 并查集是一个支持集合合并,又非常快速的一个数据结构。(需要提前给定一组数据为初始化)
  2. 并查集的三个方法:(注意:三个方法的时间复杂度都是O(1),参考文献,算法导论23章,证明问什么是O(1)的时间复杂度,先辈用了25年,用的时候知道时间复杂度是O(1)即可)。
    1. isSame(T t1,T  t2):判断两个元素是否属于同一个集合
    2. getFather(T t):寻找该节点的最祖宗节点
      1. 存在一个优化过程,当寻找父节点的同时,将途径节点押入栈中,当找到最祖宗节点,将途径节点的父节点变为最祖宗节点
      2. 优化的目的在于;将下一次寻找父节点的时间复杂度变为O(1);
      3. 当getFather的运行次数越趋紧于N,时间复杂度就越趋近于O(1);
      4. 单独各自空间,因此空间的尺寸不用改变
    3. union(T t1, T  t2):将两个集合合并,合并的过程也是O(1);
      1. 需要将两个集合合并,因此空间需要累加。
      2. 关键节点需要进行父子串联
  3. 看代码
    1. package LiKou.UninoAndFind;
      
      import java.util.HashMap;
      import java.util.List;
      import java.util.Stack;
      
      public class Code01_UnionFind {
      
      	/**
      	 * 静态内部类:用于存放节点
      	 * @param <V>
      	 */
          public static class Node<V> {
          	//确定类型后,给一个value
              V value;
      
              public Node(V v) {
                  value = v;
              }
          }
      
          //并查集类
          public static class UnionSet<V> {
              public HashMap<V, Node<V>> nodes;  //用于存放值类型,节点类型的集合
              public HashMap<Node<V>, Node<V>> parents;  //用于存放节点对应节点的父节点
              public HashMap<Node<V>, Integer> sizeMap;  //用于判断一个集合的大小
      
      		//一个类的初始化,给定一串数据
              public UnionSet(List<V> values) {
              	//进行初始化动作
                  nodes = new HashMap<>();
                  parents = new HashMap<>();
                  sizeMap = new HashMap<>();
                  //初始化赋值
                  for (V cur : values) {
                  	//根据value创建node
                      Node<V> node = new Node<>(cur);
                      //进行node的初始化(key为值,value为node)
                      nodes.put(cur, node);
                      //刚开始当前节点的祖宗节点就是当前节点
                      parents.put(node, node);
                      //存放node节点的大小,只有1个,所以就是1
                      sizeMap.put(node, 1);
                  }
              }
      
              // 寻找头节点
              public Node<V> findFather(Node<V> cur) {
              	//创建一个栈
                  Stack<Node<V>> path = new Stack<>();
                  //当本节点的父节点不等与当前节点时,直至变为其父节点
                  while (cur != parents.get(cur)) {
                  	//压栈
                      path.push(cur);
                      //然后将当前节点变为其父节点
                      cur = parents.get(cur);
                  }
      
                  //如果栈不为空
                  while (!path.isEmpty()) {
                      //变换其与祖宗节点的父子关系
                      parents.put(path.pop(), cur);
                  }
                  //返回祖宗节点
                  return cur;
              }
      
              //判断是同一个集合里
              public boolean isSameSet(V a, V b) {
              	//判断是否在大集合里
                  if (!nodes.containsKey(a) || !nodes.containsKey(b)) {
                      return false;
                  }
                  //最终判断是否在同一个爹里
                  return findFather(nodes.get(a)) == findFather(nodes.get(b));
              }
      
              //合并
              public void union(V a, V b) {
              	//前提是两个节点必须放在node里
                  if (!nodes.containsKey(a) || !nodes.containsKey(b)) {
                      return;
                  }
                  //找到a的父节点
                  Node<V> aHead = findFather(nodes.get(a));
                  //找到b的父节点
                  Node<V> bHead = findFather(nodes.get(b));
                  //如果两个节点不一样,开始合并
                  if (aHead != bHead) {
                      int aSetSize = sizeMap.get(aHead);
                      int bSetSize = sizeMap.get(bHead);
                      //找到大的那个
                      Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
                      //大的是一个,小的就是另一个
                      Node<V> small = big == aHead ? bHead : aHead;
                      //小的放到big里
                      parents.put(small, big);
                      //大的集合大小刷新
                      sizeMap.put(big, aSetSize + bSetSize);
                      //在size里删除small
                      sizeMap.remove(small);
                  }
              }
          }
      
      }

       

       

  4. 根据并查集,由此引出咋们上面提到的用多个cpu 处理一个非常大的岛问题

    1. 核心思想就是分割加+并查集的合并套路+记录感染点:(自己手写了一下,就不上电子版了,有疑惑的留言)
    2.  

  5. 好吧,今天先到这~ 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值