【问题标题】:Insert into an already-sorted list插入已排序的列表
【发布时间】:2013-05-21 18:27:24
【问题描述】:

在 Java 中,我有一个名为 TestClass 的类,它有一个名为 Name 的成员,它是一个字符串。我也有一个这种类型的 ArrayList,它已经按名称按字母顺序排序。我想要做的是找到放置TestClass 的新实例的最佳索引。到目前为止,我能想到的最好方法是:

public static int findBestIndex(char entry, ArrayList<TestClass> list){
    int desiredIndex = -1;
    int oldPivot = list.size();
    int pivot = list.size()/2;
    do
    {
        char test = list.get(pivot).Name.charAt(0);
        if (test == entry)
        {
            desiredIndex = pivot;
        }
        else if (Math.abs(oldPivot - pivot) <= 1)
        {
            if (test < entry)
            {
                desiredIndex = pivot + 1;
            }
            else
            {
                desiredIndex = pivot - 1;
            }
        }
        else if (test < entry)
        {
            int tempPiv = pivot;
            pivot = oldPivot - (oldPivot - pivot)/2;
            oldPivot = tempPiv;
        }
        else
        {
            int tempPiv = pivot;
            pivot = pivot - (oldPivot - pivot)/2;
            oldPivot = tempPiv;
        }

    } while (desiredIndex < 0);

    return desiredIndex;
}

基本上,将数组分成两半,检查您的值是在该点之前、之后还是在那一点。如果在之后,请检查数组的前半部分。否则,请检查下半场。然后,重复。我知道这种方法只测试第一个字符,但这很容易解决,与我的主要问题无关。对于某些场景,这种方法效果很好。对于大多数人来说,它的效果很糟糕。我假设它没有正确找到新的枢轴点,如果是这种情况,我将如何解决它?

编辑:为澄清起见,我将其用于库存系统,因此我不确定 LinkedList 是否合适。我使用 ArrayList 是因为它们对我来说更熟悉,因此在需要时更容易翻译成另一种语言(目前可能会转移到 C#)。出于这个原因,我试图避免使用 Comparable 之类的东西,因为如果 C# 缺少它,我必须完全重新编写它。

编辑部分 Duex:弄清楚我做错了什么。我应该设置和更改我正在检查的区域的边界,而不是使用之前的枢轴点,并以此为基础创建新的枢轴点。

【问题讨论】:

  • 为什么不直接插入所有元素,然后使用Collections.sort()
  • 也许我看的不对,但是如果您确定该值在 选定(测试)点之后,您不会检查 数组的后半部分
  • @fge,每次插入后都采取措施可能是最慢的方法,尽管速度不是我最关心的问题,因为不太可能有很多对象被快速移入和移出。跨度>
  • @mbratch,这就是我想要做的,但我这样做的尝试很可能是完全错误的,这就是我遇到问题的地方。
  • @user2423158 这取决于访问此排序列表的频率。如果不是那么频繁,您可以在需要时重新构建该列表。

标签: java arraylist alphabetical sorted


【解决方案1】:

为此使用 SortedSet(例如 TreeSet)可能不是一个好主意,因为 Set 不允许重复元素。如果您有重复的元素(即具有相同名称的 TestClass 实例),则应使用 List。将元素插入到已经排序的列表中很简单:

void insert(List<TestClass> list, TestClass element) {
    int index = Collections.binarySearch(list, element, Comparator.comparing(TestClass::getName));
    if (index < 0) {
        index = -index - 1;
    }
    list.add(index, element);
}

此代码需要 Java 8 或更高版本,但也可以重写以在较旧的 Java 版本中工作。

【讨论】:

  • 这与树木无关。 Set 不允许重复元素
  • 感谢您指出这一点。我相应地更改了措辞。
【解决方案2】:

正如已经指出的,没有理由自己实现这个,简单的代码示例:

class FooBar implements Comparable<FooBar> { String name; @Override public int compareTo(FooBar other) { return name.compareTo(other.name); } } TreeSet<FooBar> foobarSet = new TreeSet<>(); FooBar f1; foobarSet.add(new FooBar("2")); foobarSet.add(f1 = new FooBar("1")); int index = foobarSet.headSet(f1).size();

(基于How to find the index of an element in a TreeSet?

【讨论】:

  • 我也认为我们应该使用可排序列表。在答案中,我们使用 Comparable。另一种选择是使用 Comparator。
【解决方案3】:

你应该使用像 PriorityQueue 这样已经有秩序感的东西。插入到具有顺序感的集合中会自动将元素放置在正确的位置,时间最短(通常为 log(n) 或更短)。

如果您想在没有此功能的情况下进行任意插入,那么我建议您使用不需要使用或完全复制的 LinkedList 来插入单个项目,例如您当前拥有的 ArrayList。虽然为 LinkedList 找到正确的插入位置最多需要 O(n) 时间,但实际上它仍然比使用 log(n) 搜索 ArrayList 中的正确位置要快,但随后需要对其进行复制或排序然后。

此外,在链表中查找插入位置的代码要简单得多。

if (next != null && next.compareTo(insertElement) > 0){
    // You have the right location
}

【讨论】:

  • 我不确定 PriorityQueue 或 LinkedList 是否最适合我使用它的用途(我真的应该在原始帖子中提到,现在已经修复)。
  • 好吧,我不得不说你不想使用 LinkedList b/c 的原因是不正确的。两者都同样容易翻译(我什至会说 LinkedList 更容易)。最重要的是,您不能在不移动所有元素的情况下将元素插入 ArrayList 的中间。这涉及复制 ArrayList 的那部分,比 LinkedList 插入要慢得多。基本上,如果您正在寻找效率,那么您使用的是错误的数据结构。
【解决方案4】:

还有其他数据结构可以用来代替列表,如树、优先级队列等。

【讨论】:

  • 我使用 Arraylist 是因为熟悉,并且可以轻松访问任何给定元素。然而,另一种数据结构可能是个好主意,尽管我必须先做一些研究。
【解决方案5】:

我认为问题出在这段代码中:

else if (test < entry)
{
    int tempPiv = pivot;
    pivot = oldPivot - (oldPivot - pivot)/2;
    oldPivot = tempPiv;
}
else
{
    int tempPiv = pivot;
    pivot = pivot - (oldPivot - pivot)/2;
    oldPivot = tempPiv;
}

无论是 test entry,您都在执行相同的操作。当您要搜索的项目位于数组的开头时,这将导致线性搜索。

我更喜欢使用(低和高)like

high = list.size();
low = 0;

do {
   pivot = (high + low) / 2;
   if (test < entry) {
      low = pivot;
   } else if (test > entry) {
      high = pivot
   } else {
      ....
   }
} while ...;

【讨论】:

  • 为什么不接受这个答案(如果@user2423158 想接受一个......)?它具有在数组上以 O(log(n)) 工作的关键。谢谢!
  • @Matthieu 感谢您的支持,我怀疑用户希望他的问题得到回答,之后就没有兴趣了
【解决方案6】:

制作您自己的列表实现,并在您的 add 方法中包含以下几行:

wrappedList.add(object);
Collections.sort(wrappedList);

【讨论】:

    猜你喜欢
    • 2023-04-07
    • 1970-01-01
    • 2018-04-02
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多