zoukankan      html  css  js  c++  java
  • 原创:平衡的三叉树

    去年3月份,写了一个平衡的三叉树算法包,还写了一个基于逆向最大匹配算法的中文分词算法包。现在,将平衡的三叉树算法包上传。首先看一下包结构:

    1.chinese.utility.cfg代码:

    package chinese.utility.cfg;

    /**
     * 获得主词典、量词词典以及扩展词典和扩展停词词典的路径
     * @author TongXueQiang
     * @date 2015/11/25
     * @since 1.8
     */
    public interface Configuration {
        public abstract String getMainDictionary();//主词典路径
    }

    package chinese.utility.cfg;

    public class DefaultConfiguration implements Configuration {

        @Override
        public String getMainDictionary() {        
            return "chinese/utility/dictionary/word.dic";
        }    
    }
    2.chinese.utility.comparator代码:

    package chinese.utility.comparator;

    import java.util.Comparator;

    import net.sourceforge.pinyin4j.PinyinHelper;
    /**
     * 严格拼音比较器
     * @author TongXueQiang
     * @date 2016/03/05
     * @since JDK 1.8
     */
    public class PinyinComparator implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {
            for (int i = 0; i < o1.length() && i < o2.length(); i++) {

                int codePoint1 = o1.charAt(i);
                int codePoint2 = o2.charAt(i);

                if (Character.isSupplementaryCodePoint(codePoint1)
                        || Character.isSupplementaryCodePoint(codePoint2)) {
                    i++;
                }

                if (codePoint1 != codePoint2) {
                    if (Character.isSupplementaryCodePoint(codePoint1)
                            || Character.isSupplementaryCodePoint(codePoint2)) {
                        return codePoint1 - codePoint2;
                    }

                    String pinyin1 = pinyin((char) codePoint1);
                    String pinyin2 = pinyin((char) codePoint2);

                    if (pinyin1 != null && pinyin2 != null) { // 两个字符都是汉字
                        if (!pinyin1.equals(pinyin2)) {
                            return pinyin1.compareTo(pinyin2);
                        }
                    } else {
                        return codePoint1 - codePoint2;
                    }
                }
            }
            return o1.length() - o2.length();
        }

        /**
         * 字符的拼音,多音字就得到第一个拼音。不是汉字,就return null。
         */
        public String pinyin(char c) {
            String[] pinyins = PinyinHelper.toHanyuPinyinStringArray(c);
            if (pinyins == null) {
                return null;
            }
            return pinyins[0];
        }
    }

    3.chinese.utility.dictionary代码:

    package chinese.utility.dictionary;

    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.util.ArrayList;
    import java.util.List;
    import chinese.utility.cfg.Configuration;
    import chinese.utility.cfg.DefaultConfiguration;
    import chinese.utility.ternaryTree.SortAndCompromise;
    import chinese.utility.ternaryTree.TernaryNode;
    import chinese.utility.ternaryTree.TernaryTree;
    /**
     * 字典
     * @author TongXueQiang
     * @date 2016/03/06
     * @since JDK 1.8
     */
    public class Dictionary {
        //主字典
        private static volatile Dictionary main_dict;
        //三叉树
        private TernaryTree tree;
        private Configuration configuration;
        
        private Dictionary () throws Exception{
            configuration = new DefaultConfiguration();
        }
        
        public static Dictionary getInstance() throws Exception{
            if (main_dict == null) {
                synchronized(Dictionary.class){
                    if (main_dict == null) {
                        main_dict = new Dictionary();
                        return main_dict;
                    }
                }
            }
            return main_dict;
        }
        
        /**
         * 从字典中读取汉字
         *
         * @param originalWords
         * @return
         */    
        public List<String> addToListFromDict(List<String> originalWords) throws Exception {
            //InputStream is = new FileInputStream("C:\Users\Administrator.2013-20150325HY\Desktop\word.dic");
            InputStream is = super.getClass().getClassLoader().getResourceAsStream(configuration.getMainDictionary());
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 512);
            String word = null;
            
            try {
                do {                
                    word = br.readLine();                
                    if (word == null) {
                        break;
                    }
                    
                    if (word != null && !"".equals(word.trim())) {
                        originalWords.add(word);                    
                    }                
                } while (word != null);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (is != null) {
                        is.close();
                        is = null;
                    }                
                } catch (Exception e) {
                    e.printStackTrace();
                }            
            }
            return originalWords;
        }

        /**
         * 加载主字典
         * @throws Exception
         */
        public void loadDict() throws Exception {
            //把原来的字典替换为经过排序和折中处理的字典
            replaceDictionaryBySortAndCompromise();
            
            tree = new TernaryTree();
            InputStream is = super.getClass().getClassLoader().getResourceAsStream("chinese/utility/dictionary/word.dic");
            if (is == null) {
                throw new RuntimeException("Main Dictionary not found!!!");
            }
            
            try {            
                BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"),512);
                String newWord = null;
                do {
                    newWord = br.readLine();                
                    if (newWord != null && !"".equals(newWord.trim())) {
                        tree.insert(newWord);
                    }
                }while(newWord != null);
            }catch(Exception e){
                e.printStackTrace();
            }
            finally{
                try {
                    if (is != null) {
                        is.close();
                        is = null;
                    }
                } catch(Exception e){
                    e.printStackTrace();
                }
            }        
        }
        
        /**
         * 更新字典,对字典进行排序,然后折中处理
         * @throws Exception
         */
        private void replaceDictionaryBySortAndCompromise() throws Exception {
            SortAndCompromise sac = new SortAndCompromise();
            
            List<String> originalWords = new ArrayList<String>();
            OutputStream os = new FileOutputStream("E:/Eclipse_WorkSpace/balancedTernaryTree/src/chinese/utility/dictionary/word.dic");        
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os,"UTF-8"),512);
            //对原始字典进行排序,然后折中处理,输出到新的字典当中
            sac.sortAndCompromise(originalWords, writer);        
        }
        
        /**
         * 查找前缀匹配词语
         * @param prefix
         * @return
         */
        public List<TernaryNode> prefixCompletion(String prefix){
            return tree.prefixCompletion(prefix);
        }
    }
    word.dic为词典,保存格式为UTF-8无BOM编码格式。
    4.chinese.utility.ternaryTree包代码:

    package chinese.utility.ternaryTree;
    /**
     * 三叉树节点
     * @author TongXueQiang
     * @date 2016/03/12
     * @since JDK 1.8
     */
    public class TernaryNode {
        public char storedChar;    //节点存储的单个字符
        public String token;//最后一个节点存储的term(成词)
        public TernaryNode leftNode,centerNode,rightNode;//子节点
        
        public TernaryNode (char storedChar)  {
            this.storedChar = storedChar;
        }
    }

    package chinese.utility.ternaryTree;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;

    /**
     * 自定义三叉树
     * @author TongXueQiang
     * @date 2016/03/12
     * @since JDK 1.8
     */
    public class TernaryTree {
        // 根节点,不存储字符
        private static TernaryNode root = new TernaryNode('');

        /**
         * 向树中插入字符
         *
         * @param TernaryNode
         * @param word
         * @return
         */
        public void insert(String word) {
            root = insert(root, word, 0);
        }

        public TernaryNode insert(TernaryNode currentTernaryNode, String word, int index) {
            if (word == null || word.length() < index) {
                return currentTernaryNode;
            }
            char[] charArray = word.toCharArray();
            
            if (currentTernaryNode == null) {
                currentTernaryNode = new TernaryNode(charArray[index]);
            }
            
            if (currentTernaryNode.storedChar > charArray[index]) {
                currentTernaryNode.leftNode = insert(currentTernaryNode.leftNode, word, index);
            } else if (currentTernaryNode.storedChar < charArray[index]) {
                currentTernaryNode.rightNode = insert(currentTernaryNode.rightNode, word, index);
            } else {
                if (index != word.length() - 1) {
                    currentTernaryNode.centerNode = insert(currentTernaryNode.centerNode, word, index + 1);
                } else {
                    currentTernaryNode.token = word;
                }
            }

            return currentTernaryNode;
        }

        /**
         * 查找以指定前缀开头的所有字符串
         *
         * @param prefix
         * @return
         */
        public List<TernaryNode> prefixCompletion(String prefix) {
            // 首先查找前缀的最后一个节点
            TernaryNode TernaryNode = findNode(prefix);
            return prefixCompletion(TernaryNode);
        }
        
        public List<TernaryNode> prefixCompletion(TernaryNode p) {
            List<TernaryNode> suggest = new ArrayList<TernaryNode>();
            
            if (p == null) return suggest;
            if ((p.centerNode == null) && (p.token == null)) return suggest;
            if ((p.centerNode == null) && (p.token != null)) {
              suggest.add(p);
              return suggest;
            }
            
            if (p.token != null) {
                suggest.add(p);
            }
            
            p = p.centerNode;

            Stack<TernaryNode> s = new Stack<TernaryNode>();
            s.push(p);
            while (!s.isEmpty()) {
                TernaryNode top = s.pop();            

                if (top.token != null) {
                    suggest.add(top);
                }
                if (top.centerNode != null) {
                    s.push(top.centerNode);
                }
                if (top.leftNode != null) {
                    s.push(top.leftNode);
                }
                if (top.rightNode != null) {
                    s.push(top.rightNode);
                }
            }
            return suggest;
        }

        /**
         * 查找前缀的下一个centerTernaryNode,作为searchPrefix的开始节点
         *
         * @param prefix
         * @return
         */
        public TernaryNode findNode(String prefix) {
            return findNode(root, prefix, 0);
        }
        
        private TernaryNode findNode(TernaryNode TernaryNode, String prefix, int index) {
            if (prefix == null || prefix.equals("")) {
                return null;
            }
            if (TernaryNode == null) {
                return null;
            }
            char[] charArray = prefix.toCharArray();
            // 如果当前字符小于当前节点存储的字符,查找左节点
            if (charArray[index] < TernaryNode.storedChar) {
                return findNode(TernaryNode.leftNode, prefix, index);
            }
            // 如果当前字符大于当前节点存储的字符,查找右节点
            else if (charArray[index] > TernaryNode.storedChar) {
                return findNode(TernaryNode.rightNode, prefix, index);
            } else {// 相等
                // 递归终止条件
                if (index !=charArray.length - 1) {
                    return findNode(TernaryNode.centerNode, prefix, ++index);
                }
            }
            return TernaryNode;
        }
    }

    package chinese.utility.ternaryTree;

    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;

    import chinese.utility.comparator.PinyinComparator;
    import chinese.utility.dictionary.Dictionary;

    public class SortAndCompromise {
        /**
         * 用二分法对经过排序的字典折中处理,输出到新的文件中
         *
         * @param word
         * @param left
         * @param right
         * @param newWord
         * @throws IOException
         */
        public void outputBalanced(BufferedWriter fp, List<String> k, int offset, int n) throws IOException {
            int m;
            if (n < 1) {
                return;
            }
            
            m = n >> 1; // m=n/2

            String item = k.get(m + offset);        
            fp.write(item);        
            fp.newLine();
            fp.flush();
            
            outputBalanced(fp, k, offset, m); // 输出左半部分
            outputBalanced(fp, k, offset + m + 1, n - m - 1); // 输出右半部分

        }

        /**
         * 排序和折中处理,以便外部调用
         *
         * @param originalWord
         * @param word
         * @return
         * @throws Exception
         */
        public void sortAndCompromise(List<String> originalWords, BufferedWriter writer) throws Exception {
            // 从字典中读取汉字,加载到list中
            Dictionary dict = Dictionary.getInstance();
            originalWords = dict.addToListFromDict(originalWords);

            // 对list中的汉字排序
            Comparator<String> comparator = new PinyinComparator();
            Collections.sort(originalWords, comparator);
            System.out.println("排序后的:" + originalWords);
            // 折中处理,输出到新的文件当中
            outputBalanced(writer, originalWords, 0, originalWords.size());
        }

    }

    5.chinese.utility.test代码:

    package chinese.utility.test;

    import java.io.IOException;
    import java.util.List;

    import org.junit.Test;
    import chinese.utility.comparator.PinyinComparator;
    import chinese.utility.ternaryTree.TernaryNode;
    import chinese.utility.ternaryTree.TernaryTree;

    public class HanyuToPinyinTest {    
        PinyinComparator comparator = new PinyinComparator();
        @Test
        public void test() throws IOException {
    //         System.out.println(PinyinUtils.converterToSpell("重复").toLowerCase());
    //       System.out.println(PinyinUtils.converterToSpell("康师傅").toLowerCase());
    //         String []strs = PinyinUtils.pinyin('重');
    //         Arrays.sort(strs,comparator);
    //         for (String s : strs) {
    //            System.out.println(s);
    //         }    
            TernaryTree tree = new TernaryTree();
            tree.insert("cq");
            tree.insert("重庆");        
            
            List<TernaryNode> result = tree.prefixCompletion("cq");
            for (TernaryNode node : result) {
                System.out.println(node.token);
            }        
        }    
    }

    package chinese.utility.test;

    import java.util.Comparator;

    import org.junit.Assert;
    import org.junit.Test;

    import chinese.utility.comparator.PinyinComparator;

    public class PinyinComparatorTest {
        private Comparator<String> comparator = new PinyinComparator();
         /**
         * 常用字
         */
         @Test
         public void testCommon() {
         Assert.assertTrue(comparator.compare("孟", "宋") < 0);
         }
        
         /**
         * 不同长度
         */
         @Test
         public void testDifferentLength() {
         Assert.assertTrue(comparator.compare("他奶奶的", "他奶奶的熊") < 0);
         }

         /**
         * 和非汉字比较
         */
         @Test
         public void testNoneChinese() {
         Assert.assertTrue(comparator.compare("a", "阿") < 0);
         Assert.assertTrue(comparator.compare("1", "阿") < 0);
         Assert.assertTrue(comparator.compare("b","阿") < 0);
         }

        /**
         * 非常用字(怡)
         */
        @Test
        public void testNoneCommon() {
            Assert.assertTrue(comparator.compare("怡", "张") < 0);
        }

        /**
         * 同音字
         */
        @Test
        public void testSameSound() {
            Assert.assertTrue(comparator.compare("怕", "帕") == 0);
        }
        
        /**
         * 多音字(曾)
         */
        @Test
        public void testMultiSound() {
            Assert.assertTrue(comparator.compare("曾经", "曾迪") > 0);
        }
    }

    package chinese.utility.test;

    import java.util.List;
    import chinese.utility.dictionary.Dictionary;
    import chinese.utility.ternaryTree.TernaryNode;

    public class Test {

        public static void main(String[] args) throws Exception {        
            Dictionary dictionary = Dictionary.getInstance();        
            //加载主字典
            dictionary.loadDict();
            //查找前缀匹配的词语
            List<TernaryNode> result = dictionary.prefixCompletion("中");
            for (TernaryNode node : result) {
                System.out.print(node.token+" ");
            }        
        }
    }

    6.chinese.utility.utils代码:

    package chinese.utility.utils;

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;

    import net.sourceforge.pinyin4j.PinyinHelper;
    import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
    import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
    import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
    import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
    /**
     * 汉语转拼音
     * @author TongXueQiang
     * @date 2016/03/07
     * @since JDK 1.8
     */
    public class PinyinUtils {
        
        /**   
         * 汉字转换为汉语拼音首字母,英文字符不变   
         * @param chines 汉字   
         * @return 拼音
         */     
        public static String converterToFirstSpell(String chines){              
             String pinyinName = "";   
              
             //转化为字符
             char[] nameChar = chines.toCharArray();
              
             //汉语拼音格式输出类   
             HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
              
             //输出设置,大小写,音标方式等   
             defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);       
             defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);       
              
             for (int i = 0; i < nameChar.length; i++) {       
                 //如果是中文
                 if (nameChar[i] > 128) {
                    try {       
                         pinyinName +=
                               PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0].charAt(0);       
                     } catch (BadHanyuPinyinOutputFormatCombination e) {       
                         e.printStackTrace();       
                     }       
                 }else{//为英文字符    
                     pinyinName += nameChar[i];       
                 }       
             }       
            return pinyinName;       
         }       
             
        /**   
         * 汉字转换位汉语拼音,英文字符不变   
         * @param chines 汉字   
         * @return 拼音   
         */     
        public static String converterToSpell(String chines){               
            String pinyinName = "";       
            char[] nameChar = chines.toCharArray();
            
            //输出设置,大小写,音标方式等   
            HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();       
            defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);       
            defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
            
            for (int i = 0; i < nameChar.length; i++) {       
                if (nameChar[i] > 128) {       
                    try {       
                         pinyinName += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0];       
                     } catch (BadHanyuPinyinOutputFormatCombination e) {       
                         e.printStackTrace();       
                     }       
                 }else{       
                     pinyinName += nameChar[i];       
                 }       
             }       
            return pinyinName;       
         }  
        /**
         * 得到一个字符的多音
         * @param c
         * @return
         */
        public static String [] pinyin(char c){
            return PinyinHelper.toHanyuPinyinStringArray(c) == null ? null : PinyinHelper.toHanyuPinyinStringArray(c);
        }
        
        public static List<String> getPermutationSentence(List<List<String>> termArrays,int start) {
              if (CollectionUtils.isEmpty(termArrays))
                  return Collections.emptyList();

              int size = termArrays.size();
              
              if (start < 0 || start >= size) {
                  return Collections.emptyList();
              }

              if (start == size-1) {
                  return termArrays.get(start);
              }        

              List<String> strings = termArrays.get(start);

              List<String> permutationSentences = getPermutationSentence(termArrays, start + 1);

              if (CollectionUtils.isEmpty(strings)) {
                  return permutationSentences;
              }

              if (CollectionUtils.isEmpty(permutationSentences)) {
                  return strings;
              }

              List<String> result = new ArrayList<String>();
              for (String pre : strings) {
                  for (String suffix : permutationSentences) {
                      result.add(pre+suffix);
                  }
              }

              return result;
            }
    }

  • 相关阅读:
    vue之下拉菜单Dropdown的使用
    elinput输入框的readonly属性
    通过CollectionUtils工具类判断集合是否为空,通过StringUtils工具类判断字符串是否为空
    前端与后端之间参数的传递与接收和@RequestBody,@Requestparam,@Param三个注解的使用
    为什么数据库能查出两条id相同的数据
    js中函数调用时,对参数个数和类型没有要求
    避免创建不需要的对象,消除过期对象的引用
    避免使用终结方法
    避免使用终结方法
    快速排序
  • 原文地址:https://www.cnblogs.com/txq157/p/6366378.html
Copyright © 2011-2022 走看看