【问题标题】:Order Strings by apperance in Java在 Java 中按外观排序字符串
【发布时间】:2011-07-04 09:43:28
【问题描述】:

我有一个已经根据数值排列的字符串列表。 现在我想按以下方式对这些字符串进行分组。

假设我有一个如下所示的文本文件:

数据库
eb
光盘
edd
ab
ebc
解码器
教育局
cda

我想以这样的方式订购它:

数据库
解码器
eb
ebc
edd
教育局
光盘
光盘
acb

所以你可以看到它必须按字符串的字母分组并将它们堆叠在一起。
完成这项任务最有效的方法是什么?

更新。 如您所见,所需的顺序不是字母顺序,包括反向。正如我所提到的,目标是对字符串进行分组,并按首次出现对其进行排序。对于这个例子,我使用字母来简化我试图解决的(很多)更复杂的问题。这里要关注的是每个字符串的字母出现的顺序。按特定顺序分组,而不是排序。

【问题讨论】:

  • 字符串的顺序是否相关?或者他们可能只是按字母顺序排序,在这种情况下你也会得到分组? - 例如,您的输入以该顺序具有edd, ebc, edb,但您所需的输出将是ebc, edd, edb。你怎么得到那个订单?它既不是自然的(字母),也不是输入的顺序。
  • @joel_gil,这似乎是颠倒的顺序。您是否考虑过使用逆序?
  • 大家好,'e' 字母的顺序是:eb, edd, ebc, edb。所需的输出(对于该字母)是:eb,ebc,edd,edb。原因是eb首先出现,所以我希望其余以'eb *'开头的顺序出现,因为已经“加权”了,我必须保持这个顺序。不是倒序,顺序与字母无关,而是先出现。
  • 定义自己的Comparator,然后使用Collections.sort()
  • @joel_gil:如果你只是向我们展示你真正的问题而不是用字母“简化”,这个问题会更清楚。

标签: java algorithm string grouping


【解决方案1】:

你需要的是一个前缀树(trie)。这是一棵树,其中每个级别对应于字符串中的一个位置(根 = 0,级别 1 = 第一个字母等),每个节点对应一个字母。 该节点还包含一个布尔值(例如isWord),指定是否有一个以该结尾的单词,在您的情况下,您需要另一个整数,例如index,以指定初始排序中单词的索引(以防万一isWord == true)。

您也可以使用最初包含所有单词的 Set。

现在只需开始遍历原始列表,对每个未使用的单词执行以下操作:

  1. 在 trie 中找到它,将其添加到新列表中,并将其标记为已使用。
  2. 获取其子树中所有未获取的单词(即所有以相同前缀开头的单词),按索引排序并将它们标记为已获取,并将它们添加到新列表中。
  3. 向上移动一级到当前节点的父节点,然后执行相同操作,直到到达根节点。

希望对您有所帮助。

【讨论】:

  • 虽然我也建议这样做,但这不会产生 OP 似乎想要的奇怪排序
  • 在这一点上我也想弄清楚:-),但这肯定不是字母排序
  • 大家好,肖恩是正确的,排序不是按字母顺序排列的。如前所述,字符串已经按数值排序。我用字母来表示它们每个值的关系。换句话说,排序取决于出现的顺序。更多的是由字符串匹配定义的分组,但不是按字母顺序排列的(这是有道理的)。
  • @fiver,我再次提到。订购已经完成。每个字符串都附加了一个数值,您看到的初始顺序与该值一致。所以现在我想做的是按字符串出现的顺序对它们进行分组。如您所见,第一个是“dbc”,所以如果在初始列表的末尾有一个“dbca”,它将成为所需列表中的第二个值。希望第二个例子有助于澄清。
  • @fiver 我也打算建议使用 trie 方法 (+1),但我发现它非常复杂(首先因为我知道的任何标准库中都没有 trie 实现)。
【解决方案2】:

好的,这看起来很复杂。如果我理解正确,您需要一个两步解决方案:

  1. 您需要将多个字符串映射到一个公共前缀

    public static String mapKey(String value){
        // or whatever your mapping to a common key would be
        return value.substring(0, 2);
    }
    
  2. 您需要按此前缀排序,按出现顺序

    我建议您使用Guava Multimap,特别是LinkedHashMultimap

    Multimap<String, String> map = LinkedHashMultimap.create();
    // write data to map
    for(String word: yourData){
        map.put(mapKey(word), word);
    }
    // read items from map, grouped by prefix
    for (String value : map.values()) {
        System.out.println(value);
    }
    

    解释:LinkedHashMultimap 按其键的创建顺序迭代其条目。由于您有多个具有公共键的条目(由mapKey 定义),它们将作为组返回。

实际上,在重新阅读您的要求时,LinkedHashMultimap 也不太适合(因为各个组的项目会随机出现)。你需要一个自定义的Multimap

Multimap<String,String> map = Multimaps.newListMultimap(
        new LinkedHashMap<String, Collection<String>>(),
        (Supplier<? extends List<String>>) new Supplier<List<String>>() {
    public List<String> get() {
        return new ArrayList<String>();
    }
});

(其余代码不变)

【讨论】:

  • 很有趣,将尝试实现它并返回结果。
【解决方案3】:

维护插入顺序的trie(例如使用LinkedHashMap)就可以解决问题。

我拼凑了一个像这样的简单尝试:

import org.junit.Test;

import java.util.*;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;

public class LinkedTrieTest {

    @Test
    public void testLinkedTrie() {

        List<String> input = asList(
                "dbc",
                "eb",
                "cd",
                "edd",
                "acb",
                "ebc",
                "dac",
                "edb",
                "cda"
        );

        List<String> expected = asList(
                "dbc",
                "dac",
                "eb",
                "ebc",
                "edd",
                "edb",
                "cd",
                "cda",
                "acb"
        );

        assertEquals(expected, iterableToList(new LinkedTrie(input)));
    }

    private List<String> iterableToList(Iterable<String> t) {
        List<String> result = new ArrayList<String>();
        for (String s : t) {
            result.add(s);
        }
        return result;
    }

    private class LinkedTrie implements Iterable<String> {

        private Node root = new Node();

        private LinkedTrie() {
        }

        private LinkedTrie(Iterable<String> strings) {
            addAll(strings);
        }

        private void addAll(Iterable<String> strings) {
            for (String string : strings) {
                add(string);
            }
        }

        public void add(String s) {
            root.add(s);
        }

        @Override
        public Iterator<String> iterator() {
            return root.iterator();
        }

        private class Node {
            private Map<Character, Node> nodes = new LinkedHashMap<Character, Node>();

            private void add(String s) {
                if (s.isEmpty()) {
                    nodes.put(null,null);
                    return;
                }
                Character c = s.charAt(0);
                Node node = nodes.get(c);
                if (null == node) {
                    node = new Node();
                }
                nodes.put(c, node);
                node.add(s.substring(1));
            }

            private Iterator<String> iterator() {
                return new TrieIterator();
            }

            private class TrieIterator implements Iterator<String> {

                private Iterator<Map.Entry<Character,Node>> prefixesWithSuffixes = nodes.entrySet().iterator();
                private Character currentPrefix;
                private Iterator<String> suffixesForCurrentPrefix = Collections.<String>emptyList().iterator();

                @Override
                public boolean hasNext() {
                    return suffixesForCurrentPrefix.hasNext() || prefixesWithSuffixes.hasNext();
                }

                @Override
                public String next() {
                    if (outOfSuffixesForCurrentPrefix()) {
                        if (outOfPrefixes()) {
                            throw new NoSuchElementException();
                        }
                        Map.Entry<Character, Node> prefixWithSuffixes = prefixesWithSuffixes.next();
                        currentPrefix = prefixWithSuffixes.getKey();
                        if (null == currentPrefix) {
                            return "";
                        }
                        suffixesForCurrentPrefix = prefixWithSuffixes.getValue().iterator();
                    }
                    return currentPrefix + suffixesForCurrentPrefix.next();
                }

                private boolean outOfPrefixes() {
                    return !prefixesWithSuffixes.hasNext();
                }

                private boolean outOfSuffixesForCurrentPrefix() {
                    return !suffixesForCurrentPrefix.hasNext();
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            }
        }
    }
}

【讨论】:

    【解决方案4】:
    public static List<String> crazySort(List<String> list) {
      LinkedHashMap<Character, List<String>> map = 
        new LinkedHashMap<Character, List<String>>();
      for (String s : list) {
        List<String> group = map.get(s.charAt(0));
        if (group == null)
          map.put(s.charAt(0), new ArrayList<String>());
        group.add(s);
      }
      List<String> sorted = new ArrayList<String>(list.size());
      for (List<String> group : map.values()) {
        sorted.addAll(group);
      }
      return sorted;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-17
      • 2020-02-11
      • 1970-01-01
      • 1970-01-01
      • 2020-05-26
      • 2011-10-16
      • 2016-02-01
      • 2012-10-14
      • 2017-07-23
      相关资源
      最近更新 更多