在你的最后一句话之前,答案对我来说似乎很明显:如果需要保留顺序,请使用HashSet<String>
或LinkedHashSet<String>
:
HashSet<String> distinctStrings = new HashSet<String>(Arrays.asList(array));
如果您无法使用集合API,可以考虑构建自己的哈希集合......但在您给出不使用集合API的原因之前,很难给出更具体的答案,因为该原因可能会排除其他答案。
分析
让我们进行一些分析:
使用 HashSet。时间复杂度- O(n)。空间复杂度 O(n)。注意,它需要大约8 *数组大小字节(8-16字节 - 对新对象的引用)。
快速排序。时间-O(n*log n)。空间O(log n)(最坏的情况是O(n*n)和O(n))。
归并排序(二叉树/ TreeSet)。时间O(n * log n)。空间O(n)
堆排序。时间 O(n * log n)。空间O(1)。(但它比2和3慢)。
在堆排序的情况下,您可以通过实时去重来节省排序后的最后一遍。
结论
如果时间是您关心的问题,并且您不介意为HashSet分配8 *数组长度字节-则此解决方案似乎是最优的。
如果空间是一个问题-那么快速排序+一次遍历。
如果空间是一个大问题-实现一个带有实时去重的堆。 它仍然是O(n * log n),但没有额外的空间。
创建一个哈希集合来处理这个任务是非常昂贵的。事实上,他们告诉你不要使用集合API的整个原因就在于他们不想听到哈希这个词。所以只能遵循下面的代码。
请注意,你在对数组进行排序之后才提供了二分查找:这没有任何意义,这可能是你的建议被拒绝的原因。
选项1:
public static void removeDuplicates(String[] input){
Arrays.sort(input);//Use mergesort/quicksort here: n log n
for(int i=1; i<input.length; i++){
if(input[i-1] == input[i])
input[i-1]=null;
}
}
选项2:
public static String[] removeDuplicates(String[] input){
Arrays.sort(input);//Use mergesort here: n log n
int size = 1;
for(int i=1; i<input.length; i++){
if(input[i-1] != input[i])
size++;
}
System.out.println(size);
String output[] = new String[size];
output[0]=input[0];
int n=1;
for(int i=1;i<input.length;i++)
if(input[i-1]!=input[i])
output[n++]=input[i];
//final step: either return output or copy output into input;
//here I just return output
return output;
}
选项3:(由949300添加,基于选项1)。请注意,这会破坏输入数组,如果不可接受,您必须制作副本。
public static String[] removeDuplicates(String[] input){
Arrays.sort(input);//Use mergesort/quicksort here: n log n
int outputLength = 0;
for(int i=1; i<input.length; i++){
// I think equals is safer, but are nulls allowed in the input???
if(input[i-1].equals(input[i]))
input[i-1]=null;
else
outputLength++;
}
// check if there were zero duplicates
if (outputLength == input.length)
return input;
String[] output = new String[outputLength];
int idx = 0;
for ( int i=1; i<input.length; i++)
if (input[i] != null)
output[idx++] = input[i];
return output;
}
嗨,你需要把它们放到一个数组里吗?使用哈希值(如集合)的集合会更快。因为每个值都有其唯一的哈希值。
如果你把所有条目放入一个集合类型的集合中,你可以使用
HashSet(int initialCapacity)
构造函数用于在运行时防止内存扩展。
Set<T> mySet = new HashSet<T>(Arrays.asList(someArray))
如果内存不需要扩展,Arrays.asList()的运行时复杂度为O(n)。
由于这是一道面试题,我认为他们希望你自己来实现而不是使用set api。
你可以建立一个二叉树并创建一个空数组来存储结果,而不是先对其进行排序再进行比较。
数组中的第一个元素将是根节点。
如果下一个元素等于该节点,则返回。->这会删除重复元素
如果下一个元素小于该节点,则将其与左侧进行比较,否则将其与右侧进行比较。
重复以上两个步骤,直到到达树的末尾,然后您就可以创建一个新节点并知道这还没有重复。将此新节点值插入数组中。
遍历所有原始数组元素后,您将获得一个按原始顺序没有重复的新数组副本。
遍历需要O(n)时间,搜索二叉树需要O(logn)时间(插入应该只需要O(1),因为您只需将其附加而不是重新分配/平衡树),因此总计应为O(nlogn)。
如果他们想要超级快的速度,让我们尽可能多地使用字符串的哈希码。
循环遍历数组,获得每个字符串的哈希码,并将其添加到您喜欢的数据结构中。由于您不能使用 Collection,请使用 BitSet。注意,您需要两个,一个用于正数,一个用于负数,并且它们都非常庞大。
再次循环遍历数组,使用另一个 BitSet。True 表示字符串通过。如果字符串的哈希码不存在于 Bitset 中,则可以将其标记为 true。否则,将其标记为可能重复项,即 false。顺便统计一下可能重复项的数量。
将所有可能的重复项收集到一个名为 possibleDuplicates 的大型 String[] 中。将其排序。
现在,在原始数组中遍历可能的重复项,并在 possibleDuplicates 中进行二进制搜索。如果存在,那么你还是被卡住了,因为你想要包含它一次,但不是所有其他时间。所以你需要另一个数组。有点混乱,我得去吃饭了,但这是一个开始...