【问题标题】:String Radix Sort - StringIndexOutOfBoundsEception字符串基数排序 - StringIndexOutOfBoundsEception
【发布时间】:2016-07-28 08:02:21
【问题描述】:

我正在编写自己的基数排序方法来对字符串中的单词进行排序(the big black cat sat on the  beautiful brown mat 将被排序为beautiful big black brown cat mat on sat the the)。该方法接收单个单词的 List(我自己的 List 接口)并重新排序列表。

到目前为止,这是我的方法:

public static void stringRadixSort(List<String> list, int letters) {
    List<String>[] buckets = (List<String>[]) Array.newInstance(List.class, 26);

    int letterNumber = 1; //Sorts list by 1st letter of each word, then 2nd etc.
    for (int i = 0; i < letters; i++) {
        while (!list.isEmpty()) {
            String word = list.remove(list.first());
            if (word.length() > letters) throw new UnsortableException("The list contains a word that holds more letters than the given maximum number of letters."
                    + "\nMax Letters: " + letters + "\nWord: " + word);
            String letter = word.substring(letterNumber - 1, letterNumber); //EXCEPTION THROWN
            char ch = letter.charAt(0);
            int index = ch - 'a';    //gets index of each letter ('a' = buckets[0], 'z' = buckets[25]
            if (buckets[index] == null) {
                buckets[index] = new LinkedList<String>();
            }
            buckets[index].insertLast(word);
        }

        for (int j = 0; j < buckets.length; j++) {
            if (buckets[j] != null) {
                while (!buckets[j].isEmpty()) {
                    list.insertLast(buckets[j].remove(buckets[j].first()));
                }
            }
        }
        letterNumber++;
    }
}

我的方法的(唯一,我希望)问题是,当我阅读单词的每个字符时,我会创建单词的单个字母子字符串。由于外部for 循环运行letters 次(其中letters 是列表中单词的最大长度),当此循环的迭代大于当前单词的长度时,将引发异常 -即letterNumber &gt; word.length() - 因此它尝试使用大于字符串长度的字符串索引创建子字符串。

如何调整我的方法,使其仅创建每个单词的子字符串,直到 letterNumber == word.length(),然后还能够将排序算法应用于这些较短的单词 - “a”将变为“aa”之前。

【问题讨论】:

  • 列表中似乎有一个空字。如果在非单词字符上拆分并且它们位于开头或结尾,或者没有考虑到单词之间可能有多个非单词字符,则可能会发生这种情况。

标签: java string sorting substring radix-sort


【解决方案1】:

你为什么不替换

String letter = word.substring(letterNumber - 1, letterNumber);
char ch = letter.charAt(0);

char ch = word.charAt(letterNumber - 1);

直接为您提供char。但这并不能解决IndexOutOfBoundException 的问题。

您当然应该捕获异常并处理它。也许为这种情况创建一个桶是好的:当单词对于当前迭代来说太短时,它被排序到一个桶中。将列表重新合并在一起时,请先获取此存储桶的元素。

public static void stringRadixSort(List<String> list, int letters) {
    List<String>[] buckets = (List<String>[]) Array.newInstance(List.class, 27);

    int letterNumber = 1; //Sorts list by 1st letter of each word, then 2nd etc.
    for (int i = 0; i < letters; i++) {
        while (!list.isEmpty()) {
            String word = list.remove(list.first());
            if (word.length() > letters) throw new UnsortableException("The list contains a word that holds more letters than the given maximum number of letters."
                + "\nMax Letters: " + letters + "\nWord: " + word);
            int index;
            if(word.length() > letterNumber) {
                char ch = word.charAt(letterNumber - 1);
                index = ch - 'a' + 1;    //gets index of each letter ('a' = buckets[1], 'z' = buckets[26], buckets[0] is for short words
            } else {
                index = 0;
            }
            if (buckets[index] == null) {
                buckets[index] = new LinkedList<String>();
            }
            buckets[index].insertLast(word);
        }

        for (int j = 0; j < buckets.length; j++) {
            if (buckets[j] != null) {
                while (!buckets[j].isEmpty()) {
                    list.insertLast(buckets[j].remove(buckets[j].first()));
                }
            }
        }
        letterNumber++;
    }
}

【讨论】:

  • 谢谢,我不知道怎么没有想到。原来的问题仍然存在
  • 是的,我明白了。我会试着看看这个问题
  • 使用try/catch 而不是if 是一种不好的做法。由于它很容易测试,如果某个索引会针对给定的String 抛出IndexOutOfBoundsException,则应该使用if 而不是try/catch
  • @user187470 我喜欢这个解决方案,我现在无法访问我的代码,所以我稍后会实现它并让你知道它是如何工作的。谢谢
  • @fabian 我更新了我的答案,谢谢。这个好多了
【解决方案2】:

只需将短于字符串长度的元素分组到一个附加组中。您还需要首先对最不重要(相关)的字符进行排序。以下代码使用 java 集合而不是您使用的任何数据结构:

public static void stringRadixSort(List<String> list, int letters) {
    if (list.size() <= 1) {
        return;
    }

    List<String>[] buckets = new List[27];
    for (int i = 0; i < buckets.length; i++) {
        buckets[i] = new LinkedList<>();
    }
    int largestLength = -1;
    int secondLargestLength = 0;
    for (String s : list) {
        int length = s.length();
        if (length >= largestLength) {
            secondLargestLength = largestLength;
            largestLength = length;
        } else if (secondLargestLength < length) {
            secondLargestLength = length;
        }
    }

    if (largestLength > letters) {
        throw new IllegalArgumentException("one of the strings is too long");
    }

    for (int i = secondLargestLength == largestLength ? secondLargestLength-1 : secondLargestLength; i >= 0; i--) {
        for (String word : list) {
            int index = (word.length() <= i) ? 0 : word.charAt(i) - ('a' - 1);
            buckets[index].add(word);
        }

        list.clear();

        for (List<String> lst : buckets) {
            if (lst != null) {
                list.addAll(lst);
                lst.clear();
            }
        }
    }
}

【讨论】:

  • 我喜欢这个解决方案,其中buckets[0] 包含较短的单词。如果buckets[0] 中的列表包含多个单词,它们还会被排序吗?抱歉,我现在没有时间全面分析您的解决方案,但稍后我会告诉您我的进展情况。
  • @KOB:是的。如果您使用('a'-1) 填充Strings,这将产生相同的订单。因此,它只是更喜欢较短的字符串而不是较长的字符串,如果它们具有相同的前缀......请注意,该算法以 least Significant 字符开头,并使用桶中的元素保持相同的事实订购它们之前在列表中。在循环的每次迭代之后,列表将按从索引 i 开始的子字符串排序,其中索引太大的子字符串被认为是空的。
  • 不幸的是,我的代码使用我自己的 List 接口有点安静,因此我无法更改此类以使用 Java Utils List。我已经编辑了您的解决方案以改用我的列表-据我所知,它根本不会改变算法的功能,只是更改了用于编辑列表的列表方法。 Here is my edited version。这是将10 : the big black cat sat on the beautiful brown mat 排序为8 : cat beautiful big the mat on sat the,其中108 是每个列表的大小,在我的toString 方法中添加。
【解决方案3】:

在我所有的尝试中,我一直按照最重要的字母(每个单词的第一个字母)对单词进行排序,然后是下一个重要的字母,依此类推。当然,基数排序依赖于对最低有效位/字母(数字/单词的最后一位/字母)进行排序。因此,我没有从关注letterNumber = 1 开始迭代我的外部for 循环并在每次迭代后递增它,而是从letterNumber = maxWordLength 开始,然后在每次迭代后递减它,以便每次迭代比较下一个最重要的字母。

@SuppressWarnings("unchecked")
public static void stringRadixSort(List<String> list) {
    List<String>[] buckets = (List<String>[]) Array.newInstance(List.class, 27);

    //Find longest word in list
    int maxWordLength = 0;
    for (String word : list) {
        if (word.length() > maxWordLength) {
            maxWordLength = word.length();
        }
    }

    //Sorts list based on least significant letter (last letter of word) to most significant
    int letterNumber = maxWordLength;
    for (int i = 0; i < maxWordLength; i++) {
        while (!list.isEmpty()) {
            String word = list.remove(list.first());
            int index = 0;
            if(word.length() >= letterNumber) {
                char ch = word.charAt(letterNumber - 1);
                index = ch - 'a' + 1;    //gets index of each letter ('a' = buckets[1], 'z' = buckets[26], buckets[0] is for words shorter than 'letterNumber')
            }
            if (buckets[index] == null) {
                buckets[index] = new LinkedList<String>();
            }
            buckets[index].insertLast(word);
        }

        for (int j = 0; j < buckets.length; j++) {
            if (buckets[j] != null) {
                while (!buckets[j].isEmpty()) {
                    list.insertLast(buckets[j].remove(buckets[j].first()));
                }
            }
        }
        letterNumber--;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-31
    • 1970-01-01
    相关资源
    最近更新 更多