从Java数组中删除具有重复项的值的最优雅方法是什么

时间:2012-10-04 02:20:00

标签: java arrays algorithm loops

我正在尝试取一个数组,检查是否有任何欺骗,并删除该字母的所有实例,我目前正在尝试使用的方法非常难看

实施例

In: ABBCCDE
Out: ADE

或者

In: BCACDF
Out: BADF

我目前正在使用两个for循环来查找欺骗,将该欺骗的Char []添加到OTHER数组,然后循环使用另外两个循环来从我的ErrorArray中删除字符。

7 个答案:

答案 0 :(得分:5)

这可能是一个解决方案:

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

答案 1 :(得分:3)

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

答案 2 :(得分:2)

这个问题有很多解决方案,根据输入情况,最佳解决方案会有所不同。

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。

如果我正在采访某人,我会接受任何解决方案。

答案 3 :(得分:1)

使用SET可以自动删除任何重复值。由于您使用的是数组,因此需要使用Arrays.asList(T.. a)

进行转换
SET<Character> uniqueCharacters = new HashSet<Character>(Arrays.asList(yourArray));

答案 4 :(得分:1)

您没有定义优雅,但我使用位掩码和XOR提交以删除欺骗。我认为这是优雅和非常有效的,因为它避免了导航设置,以消除欺骗。

(这仅适用于大写字母,但很容易扩展。)

这是一个关键的想法。它是一个围绕BitSet的简单包装器,用于表示当前的char,或者已经看到的字符等等:

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

看起来很冗长,但收益来自算法,这欠下了this answer on XOR for N bitsets的巨额债务。

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

答案 5 :(得分:1)

使用Guava的multiset类合理解决方案:

    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则不会。

我并不认为这是最节省内存的解决方案,因为有很多临时集合被创建。

但是,从代码的角度来看,它很简单,有人可以通过查看代码来推断出你想要做的事情。

答案 6 :(得分:0)

  • 由于缺乏支持,基于Set的解决方案并不优雅 在Java中,用于从char[]Set<Character>并返回。

  • 的转换
  • 更有效地使用上述转换所需的循环 在执行问题所需的实际处理时。

  • 我认为以下解决方案的极端简洁性使其更加优雅。

  • 它也是有效的,但代价是(某种程度上)可能是大小的数组 根据所需输入字符集的知识减少。

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