任务链接:http://coursera.cs.princeton.edu/algs4/assignments/wordnet.html
本次任务主要有三个类需要实现,WordNet、SAP、Outcast。其中,WordNet主要对单词文件进行处理,需要读取文件内容,该部分是参考别人的:https://www.cnblogs.com/lxc1910/p/8051822.html
SAP是用来寻找最近祖先的。
在进行对两个节点寻找他们的最近祖先时,分别以节点A、节点B为根节点进行广度优先搜索,他们搜索到的集合中的共同节点即为他们的共同祖先,这些共同祖先中到节点A、节点B的路径之和最小的节点即为最近祖先节点。
在进行对节点集合A和节点集合B寻找最近祖先时,是使用暴力求解办法。即用集合A中的每一个节点与集合B中的每一个节点进行操作,搜索出这两两一对的最近祖先节点,然后再对这些最近祖先节点的路径进行比较,找到路径最短的即为集合A中节点与集合B中节点的最近祖先节点。
Outcast是用来查找词义最远的节点。也就是对集合A中每个节点进行求该节点到其他所有节点的路径之和,路径和最大的情况对应的节点即为词义最远的节点。
在本文实现的方法中,争取性测试均可通过,时间测试还未通过,还需改进。
import java.util.ArrayList;
import edu.princeton.cs.algs4.Bag;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.DirectedCycle;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.ST;
public class WordNet {
private final ST<String, Bag<Integer>> st; // 符号表,用于存放同义词集
private final ArrayList<String> idList; // id链表
private final Digraph G; // 有向图
// 根据文件名构造单词网络
public WordNet(String synsets, String hypernyms) {
if (synsets == null || hypernyms == null)
throw new java.lang.IllegalArgumentException("the file name is mull");
st = new ST<String, Bag<Integer>>();
idList = new ArrayList<String>();
int count = 0; // 节点个数
In in1 = new In(synsets); // 用于读取同义字符集
while (in1.hasNextLine()) { // 按行读取
String[] a = in1.readLine().split(","); // ,用于分割 id、synset、gloss
String[] a2 = a[1].split(" "); // 空格用于分割同义词单词,a2存放单词
for (int i = 0; i < a2.length; i++) {
if (st.contains(a2[i]))
st.get(a2[i]).add(Integer.parseInt(a[0])); // 取key(string)+同义词ID
else
{
Bag<Integer> b = new Bag<Integer>();
b.add(Integer.parseInt(a[0]));
st.put(a2[i], b);
}
}
count++;
idList.add(a[1]);
}
G = new Digraph(count);
In in2 = new In(hypernyms); // 取上位词
boolean[] isNotRoot = new boolean[count];
int rootNumber = 0;
while (in2.hasNextLine()) {
String[] a = in2.readLine().split(","); // 按逗号分割,第一个为下位词,其后为上义词
isNotRoot[Integer.parseInt(a[0])] = true; // 下一词不能为根
for (int i = 1; i < a.length; i++)
G.addEdge(Integer.parseInt(a[0]), Integer.parseInt(a[i])); // 建边
}
for (int i = 0; i < count; i++) {
if (!isNotRoot[i]) rootNumber++;
}
DirectedCycle d = new DirectedCycle(G);
if (rootNumber > 1 || d.hasCycle()) // 有环或根节点多
throw new IllegalArgumentException();
}
// 返回所有WordNet名词
public Iterable<String> nouns()
{
return st.keys();
}
// 这个词是单词网名词吗?
public boolean isNoun(String word)
{
if (word == null) {
throw new java.lang.IllegalArgumentException("the word is null");
}
return st.contains(word);
}
// 名词A与名词B之间的距离(下定义)
public int distance(String nounA, String nounB)
{
if (nounA == null || nounB == null)
{
throw new java.lang.IllegalArgumentException("the word is null");
}
if (!isNoun(nounA))
throw new java.lang.IllegalArgumentException("the String nounA is no in WordNet");
if (!isNoun(nounB))
throw new java.lang.IllegalArgumentException("the String nounB is no in WordNet");
Bag<Integer> valueA = st.get(nounA);
Bag<Integer> valueB = st.get(nounB);
SAP s = new SAP(G);
int distance = s.length(valueA, valueB);
return distance;
}
// 是名词A和名词B的最近共同祖先的一个同义词集(synsets.txt 的第二个字段)
public String sap(String nounA, String nounB)
{
if (nounA == null || nounB == null)
{
throw new java.lang.IllegalArgumentException("the word is null");
}
if (!isNoun(nounA))
throw new java.lang.IllegalArgumentException("the String nounA is no in WordNet");
if (!isNoun(nounB))
throw new java.lang.IllegalArgumentException("the String nounB is no in WordNet");
Bag<Integer> valueA = st.get(nounA);
Bag<Integer> valueB = st.get(nounB);
SAP s = new SAP(G);
int id = s.ancestor(valueA, valueB);
return idList.get(id);
}
// do unit testing of this class
public static void main(String[] args)
{
//
}
}
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Queue;
public class SAP {
private final Digraph digraph; // 有向图
private Node ancestor = null; // 两点之间最近距离祖先节点
private Node ancestorIter = null; // 两集合之间的最近距离祖先节点
// 构造函数取一个有向图(不一定是DAG)。
public SAP(Digraph G)
{
if (G == null) {
throw new java.lang.IllegalArgumentException("the Digraph is null");
}
digraph = new Digraph(G);
}
private class Node implements Comparable<Node> //公共祖先节点
{
private final int id; // 节点ID
private final int legth; // 节点距离搜索节点的距离
public Node(int id, int legth) {
this.id = id;
this.legth = legth;
}
public int getId() {
return id;
}
public int getLegth() {
return legth;
}
@Override
public int compareTo(Node thatNode) {
return Integer.compare(this.legth, thatNode.legth);
}
}
// V和W之间最短祖先路径的长度;如果没有这样的路径,则为-1
// 分别对V和W进行广度优先搜索,然后对其公共搜索节点进行距离相加,找到距离最短的元素节点
public int length(int v, int w)
{
int digraphNum = digraph.V(); // 有向图节点个数
if (v < 0 || w < 0 || v >= digraphNum || w >= digraphNum)
throw new java.lang.IllegalArgumentException();
MinPQ<Node> minPQNode = new MinPQ<>(); // 用于存放公共祖先节点
boolean []markv = new boolean[digraphNum]; // 以v节点为根节点时的搜索标志位
markv[v] = true;
boolean []markw = new boolean[digraphNum]; // 以w节点为根节点时的搜索标志位
markw[w] = true;
int []lenghv = new int[digraphNum]; // 各节点距离v节点的距离
for (int i = 0; i < lenghv.length; i++) {
lenghv[i] = -1; // 无连接时默认为-1;
}
lenghv[v] = 0;
int []lenghw = new int[digraphNum]; // 各节点距离w节点的距离
for (int i = 0; i < lenghw.length; i++) {
lenghw[i] = -1; // 无连接时默认为-1;
}
lenghw[w] = 0;
Queue<Integer> bfsQueue = new Queue<>(); // 辅助广度优先搜索的队列
// 以v节点为根节点进行广度优先搜索
bfsQueue.enqueue(v);
int nowSerchNode = -1; // 当前搜索节点
while (!bfsQueue.isEmpty()) {
nowSerchNode = bfsQueue.dequeue();
for (int next : digraph.adj(nowSerchNode)) {
if (!markv[next]) {
markv[next] = true;
bfsQueue.enqueue(next);
if (lenghv[next] == -1 || lenghv[next] > lenghv[nowSerchNode] + 1) // 更新距离
lenghv[next] = lenghv[nowSerchNode] + 1;
}
}
}
// 以w节点为根节点进行广度优先搜索
bfsQueue.enqueue(w);
nowSerchNode = -1;
while (!bfsQueue.isEmpty()) {
nowSerchNode = bfsQueue.dequeue();
if (markv[nowSerchNode]) // v、w同祖先节点
minPQNode.insert(new Node(nowSerchNode, lenghv[nowSerchNode] + lenghw[nowSerchNode]));
for (int next : digraph.adj(nowSerchNode)) {
if (!markw[next]) {
markw[next] = true;
bfsQueue.enqueue(next);
if (lenghw[next] == -1 || lenghw[next] > lenghw[nowSerchNode] + 1) // 更新距离
lenghw[next] = lenghw[nowSerchNode] + 1;
}
}
}
//计算最短距离
if (!minPQNode.isEmpty())
{
ancestor = minPQNode.min(); // 最近祖先节点
return ancestor.getLegth(); // 最短距离
}
else
{
ancestor = null;
return -1;
}
}
// 参与最短祖先路径的V和W的共同祖先;如果没有这样的路径,则为-1。
public int ancestor(int v, int w)
{
length(v, w);
if (ancestor == null) // 没有共同祖先
return -1;
else
{
int id = ancestor.getId();
return id;
}
}
// V中任何顶点与W中任意顶点之间的最短祖先路径长度;如果没有这样的路径,则为-1
public int length(Iterable<Integer> v, Iterable<Integer> w)
{
if (v == null || w == null)
throw new java.lang.IllegalArgumentException("the interable is null");
MinPQ<Node> minLengthPQ = new MinPQ<>(); // 最短节点集合
for (int nodeV : v) {
if (nodeV < 0 || nodeV >= digraph.V())
throw new IllegalArgumentException();
for (int nodeW : w) {
if (nodeW < 0 || nodeW >= digraph.V())
throw new IllegalArgumentException();
ancestor = null;
int legthvw = length(nodeV, nodeW); // 两点之间最近祖先距离
int idvw = ancestor.getId(); // 两点之间最近祖先id
ancestor = null;
// System.out.println("v="+nodeV+" w="+nodeW + " length=" +legthvw +" id="+idvw);
minLengthPQ.insert(new Node(idvw, legthvw)); // 将当前两节点最近祖先加入最小堆中
}
}
if (!minLengthPQ.isEmpty()) {
ancestorIter = minLengthPQ.min();
return ancestorIter.getLegth();
}
else {
ancestorIter = null;
return -1;
}
}
// 参与最短祖先路径的共同祖先;如果没有这样的路径,则为-1。
public int ancestor(Iterable<Integer> v, Iterable<Integer> w)
{
length(v, w);
if (ancestorIter == null)
return -1;
else
return ancestorIter.getId();
}
// do unit testing of this class
public static void main(String[] args)
{
Digraph gDigraph = new Digraph(12);
gDigraph.addEdge(6, 3);
gDigraph.addEdge(7, 3);
gDigraph.addEdge(3, 1);
gDigraph.addEdge(4, 1);
gDigraph.addEdge(5, 1);
gDigraph.addEdge(1, 0);
gDigraph.addEdge(10, 9);
gDigraph.addEdge(11, 9);
gDigraph.addEdge(8, 5);
gDigraph.addEdge(9, 5);
gDigraph.addEdge(2, 0);
SAP sap = new SAP(gDigraph);
Queue<Integer> vQueue = new Queue<>();
vQueue.enqueue(6);
vQueue.enqueue(8);
Queue<Integer> wQueue = new Queue<>();
wQueue.enqueue(2);
wQueue.enqueue(11);
System.out.println(sap.length(vQueue, wQueue));
}
}
public class Outcast {
private final WordNet wordNet;
// 构造函数取一个WordNet对象
public Outcast(WordNet wordnet)
{
this.wordNet = wordnet;
}
// 给定一组WordNet名词,返回一个被排斥者
public String outcast(String[] nouns)
{
int [] distance = new int[nouns.length]; // 用于存放节点xi与其他节点距离之和
for (int i = 0; i < distance.length; i++) {
String nouni = nouns[i];
for (int j = 0; j < distance.length; j++)
if (i != j)
distance[i] = distance[i] + wordNet.distance(nouni, nouns[j]);
}
int maxDistance = distance[0];
int maxID = 0;
for (int i = 0; i < distance.length; i++) { // 寻找最大距离的节点
if (maxDistance < distance[i]) {
maxDistance = distance[i];
maxID = i;
}
}
return nouns[maxID];
}
// see test client below
public static void main(String[] args)
{
//
}
}