簡體   English   中英

如何修改我的方法以在 O(N) 或 O(N * log N) 中搜索然后刪除重復項?

[英]How to modify my method to search and then remove duplicates in O(N) or O(N * log N)?

我創建了一個方法來搜索重復項,然后將重復項索引存儲到另一個數組中。 然后我遍歷我的大數組並移動所有條目而不重復。

現在,我的問題是這使用了 O(N*N) 並且我正在使用額外的 memory 空間,因為我正在添加額外的數組。

這怎么可能呢? 假設我需要了解如何在不使用其他庫或 HashSet 的情況下完成此操作。

任何提示表示贊賞。

   public void dups()
   {
       int[] index = new int[100];

       int k = 0;
       int n = 0;
       int p = 0;

       for (int i = 0; i < elements; i++)
           for (int j = i + 1; j < elements; j++)
               if(a[j].equals(a[i]))
                   index[k++] = i;

       for (int m = 0; m < elements; m++)
           if (m != index[p])
               a[n++] = (T) a[m];
           else
               p++;

       elements -= k;
   }

通常,您無法在O(n)找到重復項。

但是,可以在O(n*log n) 只需對數組排序( O(n*log n) ),然后可以在O(n)掃描重復項。

另一方面,如果您可以使用哈希表(您可能不想做什么,如果您不想使用任何其他庫),則可以掃描數組並計算每個元素在數組中出現的頻率。 之后,您可以遍歷哈希表中的每個元素,並找到出現多次的那些元素。 這將花費預期O(n)運行時間,而不是確定性O(n)

最后,為什么我寫到您通常無法在O(n)中找到重復項?
可以想象幾種特殊情況,在O(n)可以找到重復項。 例如,您的數組只能包含0到99之間的數字。在這種情況下,您可以使用另一個數組(大小為100)來計算每個元素在數組中出現的頻率。 這與哈希表的工作方式相同,但其運行時間將是確定性O(n)

如果數組已經排序,則當然可以在O(n)中查找重復項的另一個示例。

使用HashSet在O(n)時間內執行此操作:

public <T> int removeDups(T[] original) {
    HashSet<T> unique = new HashSet<T>();
    for (T item: original) {
        unique.add(item);
    }

    int size = unique.size();
    int curr = 0;
    for (int i = 0; i < original.length; i += 1) {
        if (unique.remove(original[i])) {
            original[curr] = original[i];
            curr++;
        }
    }

    return size;
}

請注意,這取決於您的列表元素的hashCode方法,是否可以在HashSet的存儲桶中正確分配元素以實現O(n)。 在最壞的情況下,這是O(n * m),其中m是唯一元素的數量,因此您絕對應該對其進行測量。

此實現在適當的位置修改數組,並返回唯一元素的數量。 盡管數組可能大於此數組,但超出該點的元素應視為垃圾。

它在列表上進行了一次遍歷以將項目添加到HashSet (添加一項為O(1)),另一遍進行了更新以更新數組,因此為O(n)(同樣,假設哈希函數良好)。

array1 = [1,2,2,3,3,3,4,5,6,4,4,5,5,5,5,10,10,8,7,7,9,10]

array1.sort() # sorting is must
print(array1)

current = NONE
count = 0 

# overwriting the numbers at the frontal part of the array
for item in array1:
    if item != current:
        array1[count] = item
        count +=1
        current=item
        
       

print(array1)#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, 10, 10]

print(array1[:count])#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

最有效的方法是:

array1 = [1,2,2,3,3,3,4,5,6,4,4,5,5,5,5,10,10,8,7,7,9,10]

array1.sort()
print(array1)

print([*dict.fromkeys(array1)])#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

#OR#
aa = list(dict.fromkeys(array1))
print( aa)#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

由於哈希和等於比較,它不是O(n),它使用LinkedHashSet,它是Java標准庫的一部分,但可能足夠接近:

public void dups() {
    Set<Integer> uniques = new LinkedHashSet<>();
    for (int i = 0; i < elements.length; i++) {
        uniques.add(elements[i]);
    }
    // todo: copy the set into a list, then call toArray() to get an array.
}

HashMap的默認實現是基於數組的,並且為O(n)。 因此,如果您想進行有趣的練習,則可以篩查HashMap的實現,以准確了解其哈希處理其鍵的方式。 基本上,它使用鍵的hashCode並使用它在預定位置(hashCode和arraylength-1)對數組進行索引,並將值存儲在該索引處。 如果要重復使用概念,將值用作鍵和值,則數組中將只有唯一的條目。

但是,如果您有大量重復項,但只有唯一值,那么最終將得到一個帶有許多空插槽的數組。 填充陣列后,只需循環遍歷一次即可刪除所有空插槽。 (例如:將所有非空條目復制到列表中)

這將是O(n),但需要2次傳遞-一次填充陣列,一次移除空插槽。 它還將需要一個與現有數組長度相同的附加數組,並需要一個較小的數組(或列表)來獲得唯一值的最終列表。

暫無
暫無

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

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