簡體   English   中英

插入已排序的列表

[英]Insert into an already-sorted list

使用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:弄清楚我做錯了什么。 我應該設置並更改我正在檢查的區域的邊界,而不是使用先前的軸點,並基於此創建新的軸。

為此使用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版本中工作。

正如已經指出的那樣,沒有理由自己實現這個,簡單的代碼示例:



    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();

(基於如何在TreeSet中查找元素的索引?

您應該使用已經具有秩序感的PriorityQueue之類的東西。 插入具有順序感的集合將自動以最短的時間(通常為log(n)或更少)將元素放置在正確的位置。

如果你想在沒有這個的情況下進行任意插入,那么我建議使用一個不必使用或完全復制的LinkedList來插入一個像你當前擁有的ArrayList這樣的項目。 雖然為LinkedList找到正確的插入位置將花費O(n)時間,但實際上它仍然比使用log(n)搜索ArrayList中的正確位置更快,但隨后需要復制或排序它然后。

用於在鏈表中查找插入位置的代碼也更加簡單。

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

使用的其他數據結構可以使用而不是像樹,優先級隊列等列表。

我認為問題出在這段代碼中:

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;
}

您正在執行相同的操作,進入測試<進入或測試>進入。 當您搜索的項目位於數組的開頭時,這將導致線性搜索。

我更喜歡使用(低和高)喜歡

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

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

制作一個自己的列表實現,並在你的add方法中有這些行:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM