[吐槽] 这道标记为中等的题目感觉还挺难的。
List[] edges
edges[i].add(new Pair(b,distance));
的方式添加从编号为i的节点出发到编号为b的节点距离为distance的这条边。class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
int nvars = 0; //统计equations中一共出现了多少个变量
Map<String, Integer> variables = new HashMap<String, Integer>();
// 将Equation里面出现的变量名称映射到数字 O(2*n),n 为eq的size
int n = equations.size();
for (int i = 0; i < n; i++) {
// 起点:equations.get(i).get(0)
if (!variables.containsKey(equations.get(i).get(0))) {
variables.put(equations.get(i).get(0), nvars++);
}
// 终点:equations.get(i).get(1))
if (!variables.containsKey(equations.get(i).get(1))) {
variables.put(equations.get(i).get(1), nvars++);
}
}
// 鉴于我们已经完成了映射,我们利用LinkedList的形式给出距离
// 对于每个点,存储其直接连接到的所有点及对应的权值
List<Pair>[] edges = new List[nvars];
// 初始化
for (int i = 0; i < nvars; i++) {
edges[i] = new ArrayList<Pair>();
}
// 图的构建
for (int i = 0; i < n; i++) {
int va = variables.get(equations.get(i).get(0)); //起点的编号,O(1),因为HashMap
int vb = variables.get(equations.get(i).get(1)); //终点的编号
edges[va].add(new Pair(vb, values[i]));
edges[vb].add(new Pair(va, 1.0 / values[i]));
}
// 下面开始处理要回答的问题
int queriesCount = queries.size(); // 问题数量
double[] ret = new double[queriesCount]; // 回答
for (int i = 0; i < queriesCount; i++) {
List<String> query = queries.get(i);
double result = -1.0; // 默认没有回答,即变量名字中没有queries的变量名
// 如果有回答
if (variables.containsKey(query.get(0)) && variables.containsKey(query.get(1))) {
int ia = variables.get(query.get(0)), ib = variables.get(query.get(1)); //获取其编号
if (ia == ib) {
result = 1.0;
}
else {
//如果起始点和终点不一样: BFS
Queue<Integer> points = new LinkedList<Integer>();
points.offer(ia);
double[] ratios = new double[nvars];
Arrays.fill(ratios, -1.0); // -1 (<0)用来作为我们unvisited的标签。
ratios[ia] = 1.0;
// 如果还有queue里面还有点,且终点还没有走到
while (!points.isEmpty() && ratios[ib] < 0) {
int x = points.poll();
// 检查x周围的每一条边
for (Pair pair : edges[x]) {
int y = pair.index;
double val = pair.value; //x到y的距离
if (ratios[y] < 0) {
ratios[y] = ratios[x] * val;
points.offer(y); //我们添加我们尚未检测邻居的点到queue中
}
}
}
result = ratios[ib];
}
}
ret[i] = result;
}
return ret;
}
}
class Pair {
int index; //点的编号
double value; //距离
Pair(int index, double value) {
this.index = index;
this.value = value;
}
}
Map variables = new HashMap();
List[] edges = new List[节点数量]
,edges[i] = new ArrayList();
edges[i].add(new Pair(b,distance));
的方式添加从编号为i的节点出发到编号为b的节点距离为distance的这条边。这里以用Integer编号的点为例子
//queue,先进先出(从而保证了BFS),用来记录尚未检测其邻居的点的编号
Queue<Integer> points = new LinkedList<Integer>();
points.offer(startpoint); //添加起始点
boolean[] visited = new boolean[num]; //默认所有的点都是没有visited
// 如果queue中还有点,且终点还没被走到
while(!points.isEmpty()&&visited[endpoint]!=true){
int x = points.poll(); //未检测其邻居的点的编号
for(PointType neighbor: edges[x]){
if(visited[neighbor.indedx]!=true){
visited[neighbor.indedx]=true;
points.offer(neighbor.indedx);
}
}
}
// 此时如果 visited[endpoint]依旧为False,则说明startpoint和endpoint之间没有通路。
class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
//第一步:给每一个变量赋予一个index,实现从变量名->index
int varNum = 0; //也是节点数量
Map<String,Integer> varMap = new HashMap<>();
for(int i=0;i<equations.size();i++){
if(!varMap.containsKey(equations.get(i).get(0)))
varMap.put(equations.get(i).get(0),varNum++);
if(!varMap.containsKey(equations.get(i).get(1)))
varMap.put(equations.get(i).get(1),varNum++);
}
//第二步:构建图
// values.length == equations.length
List<E>[] graph =new List[varNum];
// 2.1 为每一个节点初始化列表
for(int i=0;i<varNum;i++)
graph[i]=new ArrayList<E>();
// 2.2 填充每一条边
for(int i=0;i<equations.size();i++){
int startIndex=varMap.get(equations.get(i).get(0));
int endIndex=varMap.get(equations.get(i).get(1));
graph[startIndex].add(new E(endIndex,values[i])); //因为假设不会存在任何矛盾的结果。
graph[endIndex].add(new E(startIndex,1.0 /values[i]));
}
//第三步:开始回答问题
int queriesNum = queries.size();
double[] ans = new double[queriesNum];
// 一题一题慢慢回答
for(int i=0;i<queriesNum;i++){
List<String> question = queries.get(i);
double res = -1.0; //假设没有答案
if(varMap.containsKey(question.get(0)) && varMap.containsKey(question.get(1))){
int start=varMap.get(question.get(0));
int end=varMap.get(question.get(1));
if(start==end)
res=1.0;
else{
// bfs
Queue<Integer> points = new LinkedList<Integer>();
points.add(start);
double[] visited = new double[varNum];
Arrays.fill(visited,-1.0);
visited[start]=1.0;
while(!points.isEmpty() && visited[end]<0){
int pointIndex = points.poll();
for(E neighbor:graph[pointIndex]){
int neighborIndex = neighbor.index;
double neighborDist = neighbor.dist;
if(visited[neighborIndex]<0){
visited[neighborIndex] = visited[pointIndex]*neighborDist;
points.offer(neighborIndex);
}
}
}
res = visited[end];
}
}
ans[i] = res;
}
return ans;
}
}
class E{
int index; //编号
double dist; //距离,这里是ratio
public E(int index,double dist){
this.index=index;
this.dist=dist;
}
}
如果查询次数比较多,我们可以利用Floyd算法预先计算出每两个点之间的最短距离。
这里与思路一比较大的区别是,我们不再使用邻接表而使用邻接矩阵来表示这个图。其余如用编号表示字符串的思路是相同的。
class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
//第一步:给每一个变量赋予一个index,实现从变量名->index
int varNum = 0; //也是节点数量
Map<String,Integer> varMap = new HashMap<>();
for(int i=0;i<equations.size();i++){
if(!varMap.containsKey(equations.get(i).get(0)))
varMap.put(equations.get(i).get(0),varNum++);
if(!varMap.containsKey(equations.get(i).get(1)))
varMap.put(equations.get(i).get(1),varNum++);
}
// 第二步:构建图
// 这里由于题目的特征,我们用-1表示i到j没有路径,我们用邻接矩阵表示图
double[][] graph = new double[varNum][varNum];
for(int i=0;i<varNum;i++)
Arrays.fill(graph[i],-1.0);
for(int i=0;i<equations.size();i++){
int startIndex = varMap.get(equations.get(i).get(0));
int endIndex = varMap.get(equations.get(i).get(1));
graph[startIndex][endIndex] = values[i];
graph[endIndex][startIndex] = 1.0/values[i];
}
// 第三步:直接对Graph进行Floyd
// 实际上这道题中只要两者之间有通路(>0)那就可以了,因为题目假设告诉我们不存在任何矛盾的结果
for(int k=0;k<varNum;k++)
for(int i=0;i<varNum;i++)
for(int j=0;j<varNum;j++)
if(graph[i][k]>0 && graph[k][j]>0)
graph[i][j]=graph[i][k]*graph[k][j];
//第四步:开始回答问题
int queriesNum = queries.size();
double[] ans = new double[queriesNum];
for(int i=0;i<queriesNum;i++){
List<String> question = queries.get(i);
double res = -1.0;
if(varMap.containsKey(question.get(0)) && varMap.containsKey((question.get(1)))){
int start = varMap.get(question.get(0));
int end = varMap.get(question.get(1));
if(graph[start][end]>0)
res = graph[start][end];
}
ans[i]=res;
}
return ans;
}
}
【待做】