leetcode hard模式专杀685. Redundant Connection II

https://leetcode.com/problems/redundant-connection-ii/

 

这题做下来总觉得不够hard模式,因为其主要逻辑几乎就在一些简单的几何关系中。

画了下图,自己总结了一下所谓多连一条有向边会导致哪些情况。粗略分为两类:

1,指向的那个节点是自己的直系祖先,这样会构成环

2, 指向的那个节点不是自己的直系祖先,这样会造成简单的“双父”的问题

当然也有这两种情况兼有的情形,所以我的思路是先检查是否存在双父问题,如果存在,则找出对应哪两条边(没错,如果存在双父问题,则有且只有两条),得到双父的边集合A, 再检查有向环路问题,把构成环路的所有边列举出来,构成集合B。

上面两个集合为方便起见,其元素都用边数组的下标作为元素即可。

如果集合A和集合B都存在,则取二者的交集,在得到的交集中取最大者

如果仅有A存在,则取A中的最大者

如果仅有B存在,则取B中的最大者。

 

思路大概就是这样,至于怎么找有没有环的细节,倒是很简单的事,直接从某边开始,查其的下一条的下一条下一条。。。。直到碰到重复边或者不存在下一条边为止。

找双父也很简单,只要看每个边的第二个元素,在给的边数组中有没有重复(既在多个下标处都出现过)即可,有就说明有双父,没有就说明没有双父。

辅助的数据结构用了两个map,分别记录左顶点出现的下标集合和右顶点出现的下标集合。

 

代码如下:

package com.example.demo.leetcode;

import java.util.*;

public class RedundantConnectionII {
    public int[] findRedundantDirectedConnection(int[][] edges) {
        // turn edges into 2 maps:
        // 1: number with leftNode map
        // 2: number with rightNode map
        Map<Integer, Set<Integer>> left = new HashMap<>();
        Map<Integer, Set<Integer>> right = new HashMap<>();

        boolean doubleRight = false;
        int multiParentRval = -1;
        for(int i=0;i<edges.length;i++){
            int lval = edges[i][0];
            int rval = edges[i][1];

            if(left.get(lval)==null){
                Set<Integer> es = new HashSet<>();
                es.add(i);
                left.put(lval, es);
            }else{
                left.get(lval).add(i);
            }


            if(right.get(rval)==null){
                Set<Integer> es = new HashSet<>();
                es.add(i);
                right.put(rval, es);
            }else{
                doubleRight = true;
                multiParentRval = rval;
                right.get(rval).add(i);
            }
        }

        // if double right exist
        Set<Integer> candidateIndexFromMultiParent = new HashSet<>();
        if(doubleRight){
            candidateIndexFromMultiParent = right.get(multiParentRval);
//            return edges[candidateIndex.stream().max(Integer::compare).get()];
        }
        // if no double right, then it must be cycle, find the cycle
        Set<Integer> candidateFromCycle = findCycleUntilAllDye(edges, left, right);

        int finalIndex = -1;
        if(!candidateIndexFromMultiParent.isEmpty() && !candidateFromCycle.isEmpty()){
            //有环,又多父,取交集(理论上交集只有一个元素)
            candidateIndexFromMultiParent.retainAll(candidateFromCycle);

            finalIndex = candidateIndexFromMultiParent.stream().max(Integer::compare).get();
        }
        if(!candidateIndexFromMultiParent.isEmpty() && candidateFromCycle.isEmpty()){
            //有多父,无环,取多父的最大者
            finalIndex=candidateIndexFromMultiParent.stream().max(Integer::compare).get();
        }
        if(candidateIndexFromMultiParent.isEmpty() && !candidateFromCycle.isEmpty()){
            //有环,无多父,取index最大者
            finalIndex=candidateFromCycle.stream().max(Integer::compare).get();
        }

        return edges[finalIndex];
    }

    /**
     * return index of cycle candidates
     * @param edges
     * @param left
     * @param right
     * @return
     */
    Set<Integer> findCycleUntilAllDye(int[][] edges, Map<Integer, Set<Integer>> left, Map<Integer, Set<Integer>> right){
        Map<Integer, Boolean> dyeMem = new HashMap<>();
        for(int i=0;i<edges.length;i++){
            if(Boolean.TRUE.equals(dyeMem.get(i))){
                continue;
            }
            List<Integer> cycleIndex = cycle(edges, i, left, right, dyeMem);
            if(cycleIndex.size()>1){
                //发现环
                return new HashSet<>(cycleIndex);
            }
        }

        return new HashSet<>();
    }

    List<Integer> cycle(int[][] edges, int startIndex, Map<Integer, Set<Integer>> left, Map<Integer, Set<Integer>> right, Map<Integer, Boolean> dyeMem){
        List<Integer> ret = new ArrayList<>();

        ret.add(startIndex);
        dyeMem.put(startIndex,true);
        Integer nextEdge=startIndex;
        int maxIndex = 0;
        while((nextEdge=getNextEdge(edges, nextEdge, left, right))!=null){
            dyeMem.put(nextEdge,true);
            maxIndex = Math.max(maxIndex, nextEdge);
            if(ret.contains(nextEdge)){  //!!!!!!!!!
                return ret;
            }
            ret.add(nextEdge);
        }

        return Arrays.asList(-1);

    }

    private Integer getNextEdge(int[][] edges, int startEdgeIndex, Map<Integer, Set<Integer>> left, Map<Integer, Set<Integer>> right){
        int rightVert = edges[startEdgeIndex][1];
        Set<Integer> nextEdgesIndex = left.get(rightVert);
        if(nextEdgesIndex==null || nextEdgesIndex.size()<1){
            return null;
        }else{
            for(Iterator<Integer> it = nextEdgesIndex.iterator();it.hasNext();){
                return it.next();
            }
        }

        return null;
    }

    public static void main(String[] args) {
//        int[][] edges = {{1,2}, {1,3}, {2,3}};
//        int[][] edges = {{1,2},{2,3},{3,4},{4,1},{1,5}};
//        int[][] edges = {{2,1},{3,1},{4,2},{1,4}};
//        int[][] edges = {{1,2},{1,3},{2,3}};
        int[][] edges = {{4,1},{1,5},{4,2},{5,1},{4,3}};

        RedundantConnectionII demo = new RedundantConnectionII();
        int[] ret = demo.findRedundantDirectedConnection(edges);

        for(int i=0;i<ret.length;i++){
            System.out.print(ret[i]+" ");
        }
    }


}

 

 

反思:写代码中出错的几个地方包括:

一开始没想到两种情况可能同时出现(既有双父又有环),也没有想到二者的交集可能还不止一个元素,这里的几何证明结论过于想当然。

 

另外不科学的一点是,上面的代码其实我仍然觉得有点问题,就是找环路的方法不对,可是不科学的是,OJ竟然过了,这尼玛怎么回事?我举个例子来说明我找环路的代码不对吧: [[1,2],[1,3],[2,4],[2,5],[3,6],[5,7],[5,8]], 这个图用我的代码跑会报错的,可是这图应该也是合乎题干要求的一个图啊

 

anyway, 一颗多叉树要找唯一环用回溯法做一下也不是什么难事,基于我对回溯法已经比较熟了,这里就不改了,读者有兴趣可以自己改完善些

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值