最近在研究一个问题,自己尝试些写了一个算法:

问题描述:给出一段字符,比如[a,b,c,d……],输出任意长度大于n的字符组合

  1. 分析:首先确立数学模型。这个问题的本质是排列问题,即:AL2 + AL3 + …… + ALL。既然是排列问题,就应该按照排列的思维来进行处理这个问题。首先不去分析具体的实现细节,遵循着从整体到局部的思想,先确立程序,然后在确立算法与数据结构。从上面的描述中,可以看出,排列是分层级的,比如长度为2的层级的排列,长度为3的层级的排列……程序与数学模型的一个区别就是,程序要实现具体细节,而数学模型是一种抽象。所以说,上面的排列公式,除了分析出层级之外,重要的是要考虑层级之间的关联。在实现具体细节的时候,发现,每一次层级的组合结果都是下一个层级要组合的原始数据。层级与层级之间的组合,对于程序来讲,要用递归算法来实现,这一点是毋庸置疑的。从整个过程来看,必须要有第一次组合产生的结果,作为整个组合的原始数据,以便进行后面层级的组合。
  2. 确立程序;
  3. 确立数据结果与算法;
  4. 代码:
  5. package com.txq.letter.combine;

    import java.lang.ref.SoftReference;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Queue;
    import java.util.Set;
    import java.util.concurrent.ConcurrentLinkedDeque;

    /**
    * 输出长度>len的字符串的任意组合,比如String s = "abc",输出为"ab","ba","ac","ca"……字符的排列
    *
    * @author TongXueQiang
    * @date 2016/03/01
    * @since JDK 1.7
    */
    public class LetterCombation {
    // 存放字符的原始队列
    private static Queue<Character> queue = new ConcurrentLinkedDeque<Character>();
    // 组合过程中产生的第一个结果集
    private static Queue<List<String>> firstResult = new ConcurrentLinkedDeque<List<String>>();
    // 最终结果集
    private static Set<String> finalResult = new HashSet<String>();
    // 组合的层级数,从2开始
    private static int level = 2;

    /**
    * 任意字母组合
    *
    * @param word
    * @param len
    * @return
    */
    public Set<String> outputLetterCombina(String word, int len) {
    if (word == null || word.equals("")) {
    return null;
    }
    // 1.把word加入到原始队列中
    init(word);
    // 2.产生第一次结果集
    firstResult = outputFirstCombination();
    // 3.循环进行下一层级的组合,并得到最终结果集
    finalResult = outputCombination(firstResult, level, word);
    // 4.去除不符合期望的字符组合
    return removeUnexpectedLetterComb(finalResult, len);
    }

    /**
    * 去除不符合期望的字符串
    * @param finalResult2
    * @return
    */
    private Set<String> removeUnexpectedLetterComb(Set<String> result, int len) {
    List<String> deleteList = new ArrayList<String>();
    for (String s : result) {
    if (s.length() <= len) {
    deleteList.add(s);
    }
    }
    result.removeAll(deleteList);
    return result;
    }

    /**
    * 产生原始队列
    *
    * @param word
    */
    public Queue<Character> init(String word) {
    if (word == null || word.equals("")) {
    return null;
    }

    for (int i = 0;i < word.length();i++) {
    Character c = Character.valueOf(word.charAt(i));
    if (c.equals(' ') || c.equals('\n') || c.equals('\t') || c.equals('\r')) {
    continue;
    }
    queue.add(c);
    }

    return queue;
    }

    /**
    * 倒置字符串
    *
    * @param word
    * @return
    */
    public String reverse(String word) {
    StringBuffer result = new StringBuffer();
    for (int i = word.length() - 1; i >= 0; i--) {
    result.append(word.charAt(i));
    }
    return result.toString();
    }

    /**
    * 倒置字符串,比如abc,倒置后为cab,abcd,倒置后为dabc……
    *
    * @param word
    * @return
    */
    public String reverseCombination(String word) {
    char s[] = word.toCharArray();
    List<String> ss = new ArrayList<String>();

    StringBuffer sb = new StringBuffer();
    SoftReference<StringBuffer> srf = new SoftReference<StringBuffer>(sb);

    for (int i = 0; i < s.length - 1; i++) {
    sb.append(s[i]);
    }
    // 把除最后一个字符意外的全部字符串加载到list中
    ss.add(sb.toString());
    sb = null;

    sb = new StringBuffer();
    sb.append(s[s.length - 1]);
    // 把最后一个字符加载到list中
    ss.add(sb.toString());

    Collections.reverse(ss);// 倒置处理
    sb = null;

    sb = new StringBuffer();
    for (String s0 : ss) {
    sb.append(s0);
    }

    // 输出最后结果
    return sb.toString();
    }

    /**
    * 输出长度为2的字母组合,作为第一个结果集,以备后续的组合使用
    * @return
    */
    public Queue<List<String>> outputFirstCombination() {
    StringBuffer sb = null;
    List<String> cell = null;

    SoftReference<List<String>> srf = new SoftReference<List<String>>(cell);
    SoftReference<StringBuffer> srf0 = new SoftReference<StringBuffer>(sb);

    // 1.依次取出第一个字符,与剩下的字符组合
    char ch = queue.poll();

    for (char cha : queue) {
    cell = new ArrayList<String>();
    sb = new StringBuffer();
    sb.append(ch).append(cha);

    // 加入到cell中
    cell.add(sb.toString());
    cell.add(reverse(sb.toString()));

    // 把cell加入到首个结果集中
    firstResult.add(cell);

    sb = null;
    cell = null;
    }

    // 递归终止条件
    if (queue.size() != 1) {
    outputFirstCombination();
    }

    return firstResult;
    }

    /**
    * 输出组合,循环对输入的结果集中的每个cell处理,产生新的cell,然后把新的cell加载到中间结果集中,最后返回最后结果
    *
    * @param handleResult
    * @param level
    * @return
    */
    public Set<String> outputCombination(Queue<List<String>> inputResult, int level, String word) {
    // 定义新的中间结果集
    Queue<List<String>> middleResult = new ConcurrentLinkedDeque<List<String>>();
    SoftReference<Queue<List<String>>> srf = new SoftReference<Queue<List<String>>>(middleResult);

    StringBuffer sb = null;

    // 1.把handleResult加入到最终结果集中
    finalResult = addToFinalResult(inputResult);

    // 2.清空队列
    queue.clear();

    // 3.对输入的结果集进行处理,进行下一层级的组合
    List<String> cell = inputResult.poll();

    while (cell != null) {
    // 新的cell
    List<String> newCell = null;

    // ①.初始化队列
    queue = init(word);

    // ②.从cell中取出第一个字符串,然后去除原始队列中与之匹配的字符串
    removeStrFromOriginalQueue(cell);

    // ③.cell与原始队列中剩下的字符串进行组合,产生新的cell
    originalQueueToCellCombination(newCell, cell, middleResult, sb);

    // ④.清空队列
    queue.clear();

    // ⑤.下一个单元
    cell = inputResult.poll();
    }

    inputResult = null;
    ++ level;// 4.层级叠加

    // 5.递归终止条件
    if (level != word.length()) {
    outputCombination(middleResult, level, word);
    }

    // 6.处理最后的中间结果集
    addToFinalResult(middleResult);
    middleResult = null;

    return finalResult;
    }

    /**
    * cell与原始队列中剩下的字符串进行组合,产生新的cell
    *
    * @param newCell
    * @param cell
    * @param middleResult
    * @param sb
    */
    private void originalQueueToCellCombination(List<String> newCell, List<String> cell,
    Queue<List<String>> middleResult, StringBuffer sb) {
    SoftReference<List<String>> srf = new SoftReference<List<String>>(newCell);
    SoftReference<StringBuffer> srf0 = new SoftReference<StringBuffer>(sb);

    for (char c : queue) {
    newCell = new ArrayList<String>();
    for (String s : cell) {
    sb = new StringBuffer();
    sb.append(s).append(c);
    newCell.add(sb.toString());
    newCell.add(reverseCombination(sb.toString()));
    sb = null;// 不用的对象
    }
    // 把newCell加载到中间结果集中
    middleResult.add(newCell);
    newCell = null;
    }
    }

    /**
    * 从cell中取出第一个字符串,在原始队列中移除与之匹配的字符串
    *
    * @param cell
    */
    private void removeStrFromOriginalQueue(List<String> cell) {
    String firstWord = cell.get(0);
    for (int i = 0; i < firstWord.length(); i++) {
    queue.remove(firstWord.charAt(i));
    }
    }

    /**
    * 输出到最终结果集中
    *
    * @param middleResult
    */
    public Set<String> addToFinalResult(Queue<List<String>> middleResult) {
      for (List<String> cell : middleResult) {
    finalResult.addAll(cell);
    }
    return finalResult;
    }
    }

    程序在运行时,原始数据[abcdefgh]产生的排列,输出有109592个字符串,运行有点缓慢,再加一个字符i,输出结果通过计算预计有100多万个字符串,程序几乎无法运行。运行时,输入以下参数:-Xms1700m -Xmx1700m -Xmn900m  -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5  -XX:CMSInitiatingOccupacyFraction=85 -Xloggc:E:/gc.log。通过GC日志以及VisualVM监视,看到,当数据量非常大的时候,程序几乎都浪费在GC和FullGC上面。原因是内存不足导致,内存不足的原因是程序本身的问题。 一个好的算法会考虑时间复杂度和空间复杂度。在程序运行时,占用太多的内存,导致内存不足,会导致过多的GC和Full GC,也就是说,当数据量达到一定程度的时候,程序的绝大部分时间都浪费在GC上,性能十分低下。在写算法的时候,应该极力避免在循环体中频繁初始化对象,尤其是比较大的对象。这是经验问题。改变数据结构,减少内存占有量,减少GC时间,这就是优化的方向!

  6. 目前在中文分词领域里面,IK分词的性能是比较低下的,IK分词采用的数据结构是:当子节点小于3的用对象数组来存储,当大于3的时候,用HashMap来存储,以节约内存。但是,当字典中的汉字非常多的时候,维护太多的HashMap也不是一件很好的事情。采用三叉树这种数据结构,有效地解决了内存溢出问题。目前solr的搜索推荐系统的核心思想就是基于三叉树的前缀匹配算法,比如TSTLookup,JaSPellLookup。

转载于:https://www.cnblogs.com/txq157/articles/5238079.html

一个经典的字母排列算法相关推荐

  1. java实现apriori算法_七大经典、常用排序算法的原理、Java 实现以及算法分析

    0. 前言 大家好,我是多选参数的程序员,一个正再 neng 操作系统.学数据结构和算法以及 Java 的硬核菜鸡.数据结构和算法是我准备新开的坑,主要是因为自己再这块确实很弱,需要大补(残废了一般) ...

  2. php 冒泡查找 降序 随机数 封装,又一个PHP实现的冒泡排序算法分享

    又一个PHP实现的冒泡排序算法分享 经典的冒泡排序法一直是许多程序沿用的其中一种排序法,话说冒泡排序法在效率上比PHP系统函数sort更高效.本章不讨论性能,所以就不拿它来跟系统性能做对比了. 冒泡排 ...

  3. 经典十大排序算法(含升序降序,基数排序含负数排序)【Java版完整代码】【建议收藏系列】

    经典十大排序算法[Java版完整代码] 写在前面的话 十大排序算法对比 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 插入排序 希尔排序 计数排序 桶排序 基数排序 完整测试类 写在前面的话   ...

  4. 经典的K-means聚类算法

    原理部分主要来自大牛zouxy09和trnadomeet两个人的博客:后面的代码详细讲解为自己精心编写 一.概述          非监督学习的一般流程是:先从一组无标签数据中学习特征,然后用学习到的 ...

  5. 一个经典编程面试题的“隐退”

    [转] 一个经典编程面试题的"隐退" 作者:童燕群 | 发布日期:三月 22, 2014 本文由 伯乐在线 – 王伯 翻译自 The Noisy Channel. 面试程序员很困难 ...

  6. 用c语言实现字母排列组合,C语言字母排列组合的实现.pdf

    C语言字母排列组合的实现 曹玉坤 2011-6-21 目录 概述3 需求3 规律3 实现算法5 难点6 代码6 概述 本文档概述字母排列组合的实现算法和分析过程,着重强调在 解决问题前,对问题的思考方 ...

  7. 用c语言实现字母排列组合,C语言字母排列组合的实现.doc

    C语言字母排列组合的实现.doc C语言字母排列组合的实现曹玉坤2011-6-21目录概述3需求3规律3实现算法5难点6代码6概述 本文档概述字母排列组合的实现算法和分析过程,着重强调在解决问题前,对 ...

  8. 从一个实例中学习DTW算法

     基于动态时间规整算法(DTW)的相似度计算 Killer 发表于(2015-10-063) 本文标签:大数据 机器学习 算法 浏览量:193次 喜欢收藏 在孤立词语音识别中,最为简单有效的方法是 ...

  9. 算法 经典的八大排序算法详解和代码实现

    算法 经典的八大排序算法详解和代码实现 排序算法的介绍 排序的分类 算法的时间复杂度 时间频度 示例 图表理解时间复杂度的特点 时间复杂度 常见的时间复杂度 空间复杂度 排序算法的时间复杂度 冒泡排序 ...

最新文章

  1. /proc/sysrq-trigger使用说明
  2. loss和accuracy的关系
  3. 跨域请求获取Solr json检索结果并高亮显示
  4. VC++学习(15):多线程
  5. 【树形区间DP】加分二叉树(ssl 1033/luogu 1040)
  6. unity中创建游戏场景_在Unity中创建Beat Em Up游戏
  7. 《python深度学习》代码中文注释
  8. 系统仿真平台SkyEye可替代国外Matlab/Sumlink等同类软件
  9. C语言,利用函数调用统计输出素数并统计素数和
  10. 避障跟随测距c语言程序,红外避障小车c语言程序.pdf
  11. Servlet Session 跟踪
  12. android float类型保留两位小数_你知道MySQL中Decimal类型和Float Double的区别吗?
  13. 城乡规划转到计算机专业行吗,哪些大学城乡规划专业有博士点
  14. endnote安装_EndNote X8 系列教程(一):软件介绍与安装
  15. 桌面计算机怎么覆盖文件,恢复被覆盖的文件_恢复被覆盖的桌面文件
  16. 12.这就是搜索引擎:核心技术详解 --- 搜索引擎发展趋势
  17. 初中信息技术考试:Python试题及答案
  18. 阿里云Maven配置方案
  19. 阅读科研文献心得分享(二)
  20. Mysql 分表分区

热门文章

  1. MAP(Mean Average Precision)
  2. 深度强化学习(Deep Reinforcement Learning)的资源
  3. 微软加入了面向Java的Cloud Foundry
  4. freeswitch呼叫流程分析
  5. Javascript网页摇一摇
  6. oracle数据库的net manager配置监听(插图)
  7. 《软件设计师》——数据结构和算法基础
  8. 距离QCon纽约还有3个礼拜:新的演讲、播客节目和研讨会
  9. Linux下使用mail命令发送邮件
  10. AppDomain,应用程序域