簡體   English   中英

從Java數組中刪除重復的值的最優雅方法是什么

[英]What is the most elegant way to remove values that have duplicates from a Java array

我正在嘗試獲取一個數組,檢查是否有重復,並刪除該字母的所有實例,我目前正在嘗試使用的方法非常難看

例;

In: ABBCCDE
Out: ADE

要么

In: BCACDF
Out: BADF

我目前正在使用兩個for循環查找重復對象,將用於該重復對象的Char []添加到OTHER數組中,然后再循環使用2個for循環從我的ErrorArray中刪除字符。

這可能是一個解決方案:

public static void main(String[] args) {
    char[] arr = { 'A', 'B', 'B', 'C', 'C', 'D', 'E' };
    Set<Character> in = new HashSet<>();
    Set<Character> dupe = new HashSet<>();
    for (char c : arr) {
        if (!dupe.contains(c)) {
            if (in.contains(c)) {
                dupe.add(c);
                in.remove(c);
            } else {
                in.add(c);
            }
        }
    }
    char[] arrR = new char[in.size()];
    int i = 0;
    for (char c : in) {
        arrR[i++] = c;
    }
    for (char c : arrR) {
        System.out.println(c);
    }
}
public static String removeDuplicateChars (String sText)
{
    String sResult = "";
    char[] caText = sText.toCharArray();
    int[] iaAsciiIndex = new int[128];

    for (int i=0 ; i<caText.length; i++)
    {
        iaAsciiIndex[caText[i]] += 1;
    }

    for (int i=0 ; i<iaAsciiIndex.length ; i++)
    {
        if (iaAsciiIndex[i] == 1)
            sResult += (char)i;
    }

    return sResult;
}

有很多解決此問題的方法,並且最佳解決方案取決於輸入內容。

romedius在他的答案中提出的解決方案很好,就像Alex在對Makoto的答案的評論中提出的解決方案一樣。

如果您認為HashSet / HashMap的操作為O(1),則它們為O(n)。 但是,現實情況很少出現這種情況,這取決於散列函數的適當程度以及鏈接列表數組的大小(或內部使用的任何結構-Java默認使用LL)。

因此,例如:Java的HashMaps和HashSets在最壞的情況下會插入O(n),因為它們會驗證重復項並因此遍歷鏈接列表,而不僅僅是添加其尾部。 僅當碰撞次數很高時才會發生這種情況。

如果您知道輸入的大小,則最好為其設置HashSet / HashMap的大小:HashMap(int initialCapacity)構造函數將執行此操作。 這樣,您就可以避免調整結構的大小,而這可能會嚴重影響性能。

如果不這樣做,它將使用默認容量。 然后,您僅取決於哈希函數的性能。

一個可靠的解決方案O(n log n)是對輸入進行排序,然后僅迭代一次,檢查數組的上一個或下一個位置是否等於所選的位置,如果有,則不添加它。 第二部分是O(n)。 保證排序為O(n logn),如果您使用的是Java 7,它將使用timsort,這非常快。

如果我正在面試某人,我會接受其中任何一種解決方案。

使用SET可以自動刪除所有重復值。 由於使用的是數組,因此需要使用Arrays.asList(T.. a)進行轉換Arrays.asList(T.. a)

SET<Character> uniqueCharacters = new HashSet<Character>(Arrays.asList(yourArray));

你沒有定義優雅 ,但我提交使用位掩碼和XOR刪除受騙者。 我認為這是優雅且極其有效的,因為它消除了用於刪除重復對象的導航集。

(這僅適用於大寫字母,但易於擴展。)

這是該概念的關鍵。 這是一個圍繞BitSet的簡單包裝,用於表示當前字符或已看到哪些字符,等等:

class Bitmask {
    private static final int NUM_BITS = 26;
    private static final int OFFSET = 65;
    // e.g. {A,C,D} == [1,0,1,1,0, ...]
    BitSet bitset = new BitSet(NUM_BITS);

    public Bitmask() {}

    public Bitmask(Bitmask bitmask) {
        this.bitset = (BitSet) bitmask.bitset.clone();
    }
    public void set(char c) {
        int whichBit = (int) c - OFFSET;
        bitset.set(whichBit);        
    }

    public List<Character> getAllSet() {
        List<Character> all = new ArrayList<Character>();
        for (int i = 0; i < NUM_BITS; i++) {
            if (bitset.get(i)) {
                char c = (char) (OFFSET + i);
                all.add(new Character(c));
            }
        }        
        return all;
    }

    public void xor(Bitmask bitmask) {
        this.bitset.xor(bitmask.bitset);
    }

    public void or(Bitmask bitmask) {
        this.bitset.or(bitmask.bitset);
    }

    public void and(Bitmask bitmask) {
        this.bitset.and(bitmask.bitset);
    }

    public void andNot(Bitmask bitmask) {
        this.bitset.andNot(bitmask.bitset);
    }    
}

看起來很冗長,但是收益在算法中,這對N位集的XOR的答案是很大的負擔。

char[] input = {'A', 'B', 'B', 'B', 'C', 'D', 'E'};  //expect 'ACDE'
//char[] input = {'A', 'A', 'B', 'B', 'B', 'C'};
//char[] input = {'A', 'C', 'G' };

Bitmask moreThanOnceBitmask = new Bitmask();
Bitmask onceBitmask = new Bitmask();

for(char c : input) {
    Bitmask thisBitmask = new Bitmask();
    thisBitmask.set(c);
    Bitmask tmpOnceBitmask = new Bitmask(onceBitmask);
    // we've seen 'char c' at least once
    onceBitmask.or(thisBitmask);
    // we've seen 'char c' more than once
    tmpOnceBitmask.and(thisBitmask);
    moreThanOnceBitmask.or(tmpOnceBitmask);
}

// we want 'at least once' but not 'more than once'
Bitmask finalBitmask = new Bitmask(onceBitmask);
finalBitmask.andNot(moreThanOnceBitmask);

// build list

System.out.println(finalBitmask.getAllSet().toString());

Guava的多集類的合理解決方案:

    char[] chars = new char[] { 'A', 'B', 'B', 'B', 'C', 'D', 'C', 'E' };

    Multiset<Character> set =  LinkedHashMultiset.create(Chars.asList(chars));

    for (char c : chars ) {
       int cnt = set.count(c);
       if (cnt > 1) {
           set.remove(c, cnt);
       }
    }

    char[] singles = Chars.toArray(set);

    System.out.println(new String(singles));

PS:使用LinkedHashMultiset而不是HashMultiset非常重要,因為LinkedHashMultiset版本在您進行迭代時會保留插入順序,而HashMultiset不會。

我並不是說這是內存使用效率最高的解決方案,因為會創建許多臨時集合。

但是,從代碼的角度來看,這很簡單,並且有人可以僅查看代碼即可推斷出您要執行的操作。

  • 由於Java中缺少從char[]Set<Character>以及返回的轉換的支持,因此基於Set解決方案並不完美。

  • 上述轉換所需的循環可以更有效地用於執行問題所需的實際處理。

  • 我認為以下解決方案的極度簡單使其優雅。

  • 它也是有效的,盡管是以(某種)大數組為代價的,但可以根據所需輸入字符集的知識來減小其大小。

     public class Test extends TestCase { public void testDupes() { assertEquals("ADE", noDupes("ABBCCDE".toCharArray())); assertEquals("BADF", noDupes("BCACDF".toCharArray())); } public String noDupes(char[] in) { int[] count = new int[Character.MAX_VALUE]; for (char c: in) count[c]++; StringBuffer out = new StringBuffer(); for (char c: in) if (count[c]==1) out.append(c); return out.toString(); } } 

暫無
暫無

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

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