題幹: https://leetcode.com/problems/redundant-connection/
這題地思路不難,就是在邊中找環,取環中所有邊中index最大者即可。
問題是怎麼找環呢?我的大體思路是從任意一個頂點開始嘗試,使用回溯法,只要發現環路,立即停止並輸出該環
說是這麼說,具體怎麼做呢?親自試了下寫起來並沒有那麼容易。
首先得收集一個數據結構,每個頂點對應哪些邊,用一個Map來表示。
然後對每條邊進行嘗試時,需要做一些判斷,該邊是否有訪問過,沒 訪問過則加入選邊列表,遞歸進行,每次遞歸都要首先判斷,目前已選的邊是否已經構成環路,若是,則進行輸出賦值。還要注意每次選完邊後,你得知道當前最後跑到的節點是誰,否則你不知道洗一次選擇從哪裏分叉,所以需要寫個小的輔助函數來根據已選邊確定最後節點,方法是最後邊的兩個節點減去倒數第二條邊的兩個節點即可(差集)。
代碼如下:
public int[] findRedundantConnection(int[][] edges) { // collect edges to map of int->list of edge index Map<Integer, List<Integer>> vertexEdgeIndexMap = new HashMap<>(); for(int i=0;i<edges.length;i++){ add2map(vertexEdgeIndexMap, edges[i][0], i); add2map(vertexEdgeIndexMap, edges[i][1], i); } for(Integer vertex: vertexEdgeIndexMap.keySet()){ // if vertex in circle, return the max index edge in the circle, else return -1 int code = checkVertexInCircle(vertex, vertexEdgeIndexMap, edges); if(code==-1){ continue; }else{ return edges[code]; } } // this should never happen, because we can guarantee the input contains loop return new int[0]; } private int checkVertexInCircle(int vertex, Map<Integer, List<Integer>> vertexEdgeIndexMap, int[][] edges){ List<Integer> edgeChoices = new ArrayList<>(); List<Integer> circleEdges = new ArrayList<>(); traverse(vertex, vertexEdgeIndexMap, edges, edgeChoices, circleEdges); //可能本頂點有環,也可能無環 if(circleEdges.size()>0){ return circleEdges.stream().max(Integer::compare).get(); }else{ return -1; } } // 假設arr1長度爲2 private int exclude(int[] arr1, int[] arr2){ List<Integer> tt = Arrays.stream(arr2).boxed().collect(Collectors.toList()); return tt.contains(arr1[0])?arr1[1]:arr1[0]; } private boolean isCircle(List<Integer> edgeChoices, int[][] edges){ int startVertex = exclude(edges[edgeChoices.get(0)], edges[edgeChoices.get(1)]); int endVertex = exclude(edges[edgeChoices.get(edgeChoices.size()-1)], edges[edgeChoices.get(edgeChoices.size()-2)]); if(startVertex==endVertex){ return true; } return false; } private void traverse(int vertex, Map<Integer, List<Integer>> vertexEdgeIndexMap, int[][] edges, List<Integer> edgeChoices, List<Integer> circleEdges){ if(edgeChoices.size()>=3){ // if edgeChoices forms a circle, set circleEdges once for all and return if(isCircle(edgeChoices, edges)){ // circleEdges = new ArrayList<>(edgeChoices); circleEdges.clear(); for(Integer e: edgeChoices){ circleEdges.add(e); } return; } } List<Integer> choices = new ArrayList<>(); if(edgeChoices.size()<1){ choices = vertexEdgeIndexMap.get(vertex); }else{ // find the final point x int x; if(edgeChoices.size()==1){ x = (edges[edgeChoices.get(0)][0]==vertex?edges[edgeChoices.get(0)][1]:edges[edgeChoices.get(0)][0]); }else{ int[] pointsInLast = edges[edgeChoices.get(edgeChoices.size()-1)]; int[] pointsInSecondLast = edges[edgeChoices.get(edgeChoices.size()-2)]; x = exclude(pointsInLast, pointsInSecondLast); } choices = vertexEdgeIndexMap.get(x); } for(Integer c: choices){ if(!edgeChoices.contains(c)){ edgeChoices.add(c); traverse(vertex, vertexEdgeIndexMap, edges, edgeChoices, circleEdges); edgeChoices.remove(edgeChoices.size()-1); } } } private void add2map(Map<Integer, List<Integer>> vertexEdgeIndexMap, Integer key, Integer val){ if(vertexEdgeIndexMap.get(key)!=null){ vertexEdgeIndexMap.get(key).add(val); }else{ List<Integer> eIndexs = new ArrayList<>(); eIndexs.add(val); vertexEdgeIndexMap.put(key, eIndexs); } }
反思:這題這題主要在輔助函數上寫了一大堆,花了不少時間,應該不是最優雅的寫法。