日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一个经典的字母排列算法

發(fā)布時(shí)間:2025/7/14 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个经典的字母排列算法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最近在研究一個(gè)問題,自己嘗試些寫了一個(gè)算法:

問題描述:給出一段字符,比如[a,b,c,d……],輸出任意長(zhǎng)度大于n的字符組合

  • 分析:首先確立數(shù)學(xué)模型。這個(gè)問題的本質(zhì)是排列問題,即:AL2?+?AL3?+ …… +?ALL。既然是排列問題,就應(yīng)該按照排列的思維來進(jìn)行處理這個(gè)問題。首先不去分析具體的實(shí)現(xiàn)細(xì)節(jié),遵循著從整體到局部的思想,先確立程序,然后在確立算法與數(shù)據(jù)結(jié)構(gòu)。從上面的描述中,可以看出,排列是分層級(jí)的,比如長(zhǎng)度為2的層級(jí)的排列,長(zhǎng)度為3的層級(jí)的排列……程序與數(shù)學(xué)模型的一個(gè)區(qū)別就是,程序要實(shí)現(xiàn)具體細(xì)節(jié),而數(shù)學(xué)模型是一種抽象。所以說,上面的排列公式,除了分析出層級(jí)之外,重要的是要考慮層級(jí)之間的關(guān)聯(lián)。在實(shí)現(xiàn)具體細(xì)節(jié)的時(shí)候,發(fā)現(xiàn),每一次層級(jí)的組合結(jié)果都是下一個(gè)層級(jí)要組合的原始數(shù)據(jù)。層級(jí)與層級(jí)之間的組合,對(duì)于程序來講,要用遞歸算法來實(shí)現(xiàn),這一點(diǎn)是毋庸置疑的。從整個(gè)過程來看,必須要有第一次組合產(chǎn)生的結(jié)果,作為整個(gè)組合的原始數(shù)據(jù),以便進(jìn)行后面層級(jí)的組合。
  • 確立程序;
  • 確立數(shù)據(jù)結(jié)果與算法;
  • 代碼:
  • 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;

    /**
    * 輸出長(zhǎng)度>len的字符串的任意組合,比如String s = "abc",輸出為"ab","ba","ac","ca"……字符的排列
    *
    * @author TongXueQiang
    * @date 2016/03/01
    * @since JDK 1.7
    */
    public class LetterCombation {
    // 存放字符的原始隊(duì)列
    private static Queue<Character> queue = new ConcurrentLinkedDeque<Character>();
    // 組合過程中產(chǎn)生的第一個(gè)結(jié)果集
    private static Queue<List<String>> firstResult = new ConcurrentLinkedDeque<List<String>>();
    // 最終結(jié)果集
    private static Set<String> finalResult = new HashSet<String>();
    // 組合的層級(jí)數(shù),從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加入到原始隊(duì)列中
    init(word);
    // 2.產(chǎn)生第一次結(jié)果集
    firstResult = outputFirstCombination();
    // 3.循環(huán)進(jìn)行下一層級(jí)的組合,并得到最終結(jié)果集
    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;
    }

    /**
    * 產(chǎn)生原始隊(duì)列
    *
    * @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]);
    }
    // 把除最后一個(gè)字符意外的全部字符串加載到list中
    ss.add(sb.toString());
    sb = null;

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

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

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

    // 輸出最后結(jié)果
    return sb.toString();
    }

    /**
    * 輸出長(zhǎng)度為2的字母組合,作為第一個(gè)結(jié)果集,以備后續(xù)的組合使用
    * @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.依次取出第一個(gè)字符,與剩下的字符組合
    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加入到首個(gè)結(jié)果集中
    firstResult.add(cell);

    sb = null;
    cell = null;
    }

    // 遞歸終止條件
    if (queue.size() != 1) {
    outputFirstCombination();
    }

    return firstResult;
    }

    /**
    * 輸出組合,循環(huán)對(duì)輸入的結(jié)果集中的每個(gè)cell處理,產(chǎn)生新的cell,然后把新的cell加載到中間結(jié)果集中,最后返回最后結(jié)果
    *
    * @param handleResult
    * @param level
    * @return
    */
    public Set<String> outputCombination(Queue<List<String>> inputResult, int level, String word) {
    // 定義新的中間結(jié)果集
    Queue<List<String>> middleResult = new ConcurrentLinkedDeque<List<String>>();
    SoftReference<Queue<List<String>>> srf = new SoftReference<Queue<List<String>>>(middleResult);

    StringBuffer sb = null;

    // 1.把handleResult加入到最終結(jié)果集中
    finalResult = addToFinalResult(inputResult);

    // 2.清空隊(duì)列
    queue.clear();

    // 3.對(duì)輸入的結(jié)果集進(jìn)行處理,進(jìn)行下一層級(jí)的組合
    List<String> cell = inputResult.poll();

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

    // ①.初始化隊(duì)列
    queue = init(word);

    // ②.從cell中取出第一個(gè)字符串,然后去除原始隊(duì)列中與之匹配的字符串
    removeStrFromOriginalQueue(cell);

    // ③.cell與原始隊(duì)列中剩下的字符串進(jìn)行組合,產(chǎn)生新的cell
    originalQueueToCellCombination(newCell, cell, middleResult, sb);

    // ④.清空隊(duì)列
    queue.clear();

    // ⑤.下一個(gè)單元
    cell = inputResult.poll();
    }

    inputResult = null;
    ++ level;// 4.層級(jí)疊加

    // 5.遞歸終止條件
    if (level != word.length()) {
    outputCombination(middleResult, level, word);
    }

    // 6.處理最后的中間結(jié)果集
    addToFinalResult(middleResult);
    middleResult = null;

    return finalResult;
    }

    /**
    * cell與原始隊(duì)列中剩下的字符串進(jìn)行組合,產(chǎn)生新的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;// 不用的對(duì)象
    }
    // 把newCell加載到中間結(jié)果集中
    middleResult.add(newCell);
    newCell = null;
    }
    }

    /**
    * 從cell中取出第一個(gè)字符串,在原始隊(duì)列中移除與之匹配的字符串
    *
    * @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));
    }
    }

    /**
    * 輸出到最終結(jié)果集中
    *
    * @param middleResult
    */
    public Set<String> addToFinalResult(Queue<List<String>> middleResult) {
      for (List<String> cell : middleResult) {
    finalResult.addAll(cell);
    }
    return finalResult;
    }
    }

    程序在運(yùn)行時(shí),原始數(shù)據(jù)[abcdefgh]產(chǎn)生的排列,輸出有109592個(gè)字符串,運(yùn)行有點(diǎn)緩慢,再加一個(gè)字符i,輸出結(jié)果通過計(jì)算預(yù)計(jì)有100多萬個(gè)字符串,程序幾乎無法運(yùn)行。運(yùn)行時(shí),輸入以下參數(shù):-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監(jiān)視,看到,當(dāng)數(shù)據(jù)量非常大的時(shí)候,程序幾乎都浪費(fèi)在GC和FullGC上面。原因是內(nèi)存不足導(dǎo)致,內(nèi)存不足的原因是程序本身的問題。?一個(gè)好的算法會(huì)考慮時(shí)間復(fù)雜度和空間復(fù)雜度。在程序運(yùn)行時(shí),占用太多的內(nèi)存,導(dǎo)致內(nèi)存不足,會(huì)導(dǎo)致過多的GC和Full GC,也就是說,當(dāng)數(shù)據(jù)量達(dá)到一定程度的時(shí)候,程序的絕大部分時(shí)間都浪費(fèi)在GC上,性能十分低下。在寫算法的時(shí)候,應(yīng)該極力避免在循環(huán)體中頻繁初始化對(duì)象,尤其是比較大的對(duì)象。這是經(jīng)驗(yàn)問題。改變數(shù)據(jù)結(jié)構(gòu),減少內(nèi)存占有量,減少GC時(shí)間,這就是優(yōu)化的方向!
  • 目前在中文分詞領(lǐng)域里面,IK分詞的性能是比較低下的,IK分詞采用的數(shù)據(jù)結(jié)構(gòu)是:當(dāng)子節(jié)點(diǎn)小于3的用對(duì)象數(shù)組來存儲(chǔ),當(dāng)大于3的時(shí)候,用HashMap來存儲(chǔ),以節(jié)約內(nèi)存。但是,當(dāng)字典中的漢字非常多的時(shí)候,維護(hù)太多的HashMap也不是一件很好的事情。采用三叉樹這種數(shù)據(jù)結(jié)構(gòu),有效地解決了內(nèi)存溢出問題。目前solr的搜索推薦系統(tǒng)的核心思想就是基于三叉樹的前綴匹配算法,比如TSTLookup,JaSPellLookup。 ? ? ? ? ? ?
  • 轉(zhuǎn)載于:https://www.cnblogs.com/txq157/articles/5238079.html

    《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的一个经典的字母排列算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。