Coursera普林斯顿大学算法下Week1: WordNet 字网

10 篇文章 0 订阅
9 篇文章 0 订阅

任务链接: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)  
	{
		//
	}
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值