列表L 。它包含任意类型的元素。 如何有效删除此列表中的所有重复元素? 必须保留ORDER
只需要一个算法,因此不允许导入任何外部库。
答案 0 :(得分:28)
假设订单很重要:
在Python中:
>>> L = [2, 1, 4, 3, 5, 1, 2, 1, 1, 6, 5]
>>> S = set()
>>> M = []
>>> for e in L:
... if e in S:
... continue
... S.add(e)
... M.append(e)
...
>>> M
[2, 1, 4, 3, 5, 6]
如果订单无关紧要:
M = list(set(L))
答案 1 :(得分:18)
首先,我们需要确定一些关于假设的东西,即等于和存在函数关系。这是什么意思?我的意思是对于源对象S的集合,给定作为S的元素的任何两个对象x1和x2,存在(哈希)函数F,使得:
if (x1.equals(x2)) then F(x1) == F(x2)
Java有这样的关系。这允许您将重复项检查为近O(1)操作,从而将算法简化为简单的O(n)问题。如果订单不重要,那么它就是一个简单的单线:
List result = new ArrayList(new HashSet(inputList));
如果订单很重要:
List outputList = new ArrayList();
Set set = new HashSet();
for (Object item : inputList) {
if (!set.contains(item)) {
outputList.add(item);
set.add(item);
}
}
你会注意到我说“靠近O(1)”。这是因为这样的数据结构(如Java HashMap或HashSet)依赖于一种方法,其中一部分哈希码用于在后备存储中找到一个元素(通常称为存储桶)。桶的数量是2的幂。这样,该列表中的索引很容易计算。 hashCode()返回一个int。如果您有16个桶,您可以通过将hashCode与15进行AND运算来找到要使用的桶,从而为您提供0到15之间的数字。
当您尝试在该存储桶中放置某些东西时,它可能已被占用。如果是,那么将对该桶中的所有条目进行线性比较。如果碰撞率太高或者你试图在结构中放置太多元素,那么通常会增加一倍(但总是以2的幂为单位)并且所有项目都放在他们的新桶中(基于新的)面具)。因此,调整这种结构的大小是相对昂贵的。
查找也可能很昂贵。考虑这个课程:
public class A {
private final int a;
A(int a) { this.a == a; }
public boolean equals(Object ob) {
if (ob.getClass() != getClass()) return false;
A other = (A)ob;
return other.a == a;
}
public int hashCode() { return 7; }
}
此代码完全合法,它符合equals-hashCode合约。
假设你的集合只包含A个实例,你的插入/搜索现在变成O(n)操作,将整个插入变成O(n 2 )。
显然这是一个极端的例子,但指出这样的机制还依赖于地图或集合使用的值空间中相对较好的散列分布是有用的。
最后,必须说这是一个特例。如果您使用的语言没有这种“哈希快捷方式”,那么这是一个不同的故事。
如果列表中没有排序函数,那么你就会遇到O(n 2 )每个对象与其他对象的强力比较。所以在Java中:
List result = new ArrayList();
for (Object item : inputList) {
boolean duplicate = false;
for (Object ob : result) {
if (ob.equals(item)) {
duplicate = true;
break;
}
}
if (!duplicate) {
result.add(item);
}
}
如果存在排序函数(例如,整数或字符串列表),则对列表进行排序(即O(n log n)),然后将列表中的每个元素与下一个元素进行比较( O(n))所以总算法是O(n log n)。在Java中:
Collections.sort(inputList);
List result = new ArrayList();
Object prev = null;
for (Object item : inputList) {
if (!item.equals(prev)) {
result.add(item);
}
prev = item;
}
注意:以上示例假设列表中没有空值。
答案 2 :(得分:7)
如果顺序无关紧要,您可能想尝试用Python编写的算法:
>>> array = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6]
>>> unique = set(array)
>>> list(unique)
[1, 2, 3, 4, 5, 6]
答案 3 :(得分:7)
这将由nub
和nubBy
函数
nub :: Eq a => [a] -> [a]
nub [] = []
nub (x:xs) = x : nub (filter (/= x) xs)
nubBy :: (a -> a -> Bool) -> [a] -> [a]
nubBy f [] = []
nubBy f (x:xs) = x : nub (filter (not.f x) xs)
nubBy
放宽了对Eq
类型类的依赖,而是允许你定义自己的相等函数来过滤重复项。
这些函数在一致的任意类型列表上工作(例如,haskell中不允许[1,2,"three"]
),并且它们都是保持顺序的。
为了提高效率,可以使用Data.Map(或实现平衡树)将数据收集到一个集合中(key是元素,value是原始列表的索引,以便能够获得原始排序),然后将结果收集回列表并按索引排序。我稍后会尝试实施。
import qualified Data.Map as Map
undup x = go x Map.empty
where
go [] _ = []
go (x:xs) m case Map.lookup x m of
Just _ -> go xs m
Nothing -> go xs (Map.insert x True m)
这是@FogleBird解决方案的直接翻译。不幸的是,没有导入它就无法工作。
替换Data.Map导入的一个非常基本的尝试是实现一个树,就像这样
data Tree a = Empty
| Node a (Tree a) (Tree a)
deriving (Eq, Show, Read)
insert x Empty = Node x Empty Empty
insert x (Node a left right)
| x < a = Node a (insert x left) right
| otherwise = Node a left (insert x right)
lookup x Empty = Nothing --returning maybe type to maintain compatibility with Data.Map
lookup x (Node a left right)
| x == a = Just x
| x < a = lookup x left
| otherwise = lookup x right
一项改进是通过维护深度属性使其在插入上自动平衡(使树不会降级为链表)。关于哈希表的这个好处是它只需要你的类型在类型类Ord中,这对于大多数类型来说很容易派生。
nub [] = []
nub (x:xs) | elem x xs = nub (filter (/=x) xs)
| otherwise = x : nub xs
有趣的是,你不需要过滤第二个递归情况,因为elem已经检测到没有重复。
答案 4 :(得分:4)
在Python中
>>> L = [2, 1, 4, 3, 5, 1, 2, 1, 1, 6, 5]
>>> a=[]
>>> for i in L:
... if not i in a:
... a.append(i)
...
>>> print a
[2, 1, 4, 3, 5, 6]
>>>
答案 5 :(得分:3)
在java中,它是一个单行。
Set set = new LinkedHashSet(list);
将为您提供删除重复项目的集合。
答案 6 :(得分:2)
对于Java可以使用:
private static <T> void removeDuplicates(final List<T> list)
{
final LinkedHashSet<T> set;
set = new LinkedHashSet<T>(list);
list.clear();
list.addAll(set);
}
答案 7 :(得分:2)
即我们无法使用set
(dict
)或sort
。
from itertools import islice
def del_dups2(lst):
"""O(n**2) algorithm, O(1) in memory"""
pos = 0
for item in lst:
if all(item != e for e in islice(lst, pos)):
# we haven't seen `item` yet
lst[pos] = item
pos += 1
del lst[pos:]
解决方案来自here:
def del_dups(seq):
"""O(n) algorithm, O(log(n)) in memory (in theory)."""
seen = {}
pos = 0
for item in seq:
if item not in seen:
seen[item] = True
seq[pos] = item
pos += 1
del seq[pos:]
那就是我们可以使用sort
。此解决方案不保留原始订单。
def del_dups3(lst):
"""O(n*log(n)) algorithm, O(1) memory"""
lst.sort()
it = iter(lst)
for prev in it: # get the first element
break
pos = 1 # start from the second element
for item in it:
if item != prev: # we haven't seen `item` yet
lst[pos] = prev = item
pos += 1
del lst[pos:]
答案 8 :(得分:1)
为了简单起见,项目的索引可以存储在类似std :: map
的内容中 如果我没有遗漏任何内容,看起来像是O(n * log n)
答案 9 :(得分:1)
这取决于你的意思“有效”。朴素算法是O(n ^ 2),我假设你实际意味着你想要的东西比那个低。
正如Maxim100所说,您可以通过将列表与一系列数字配对来保留订单,使用您喜欢的任何算法,然后将其余部分恢复到原始顺序。在Haskell中它看起来像这样:
superNub :: (Ord a) => [a] -> [a]
superNub xs = map snd
. sortBy (comparing fst)
. map head . groupBy ((==) `on` snd)
. sortBy (comparing snd)
. zip [1..] $ xs
当然你需要导入Data.List(sort),Data.Function(on)和Data.Ord(比较)。我只能背诵这些函数的定义,但重点是什么?
答案 10 :(得分:1)
我已经为字符串编写了一个算法。实际上,你有什么类型并不重要。
static string removeDuplicates(string str)
{
if (String.IsNullOrEmpty(str) || str.Length < 2) {
return str;
}
char[] arr = str.ToCharArray();
int len = arr.Length;
int pos = 1;
for (int i = 1; i < len; ++i) {
int j;
for (j = 0; j < pos; ++j) {
if (arr[i] == arr[j]) {
break;
}
}
if (j == pos) {
arr[pos] = arr[i];
++pos;
}
}
string finalStr = String.Empty;
foreach (char c in arr.Take(pos)) {
finalStr += c.ToString();
}
return finalStr;
}
答案 11 :(得分:0)
Python中的一行解决方案。
使用list-comprehension:
>>> L = [2, 1, 4, 3, 5, 1, 2, 1, 1, 6, 5]
>>> M = []
>>> zip(*[(e,M.append(e)) for e in L if not e in M])[0]
(2, 1, 4, 3, 5, 6)
答案 12 :(得分:0)
也许你应该考虑使用关联数组(也就是python中的dict)来避免首先出现重复的元素。
答案 13 :(得分:0)
我的Java代码:
ArrayList<Integer> list = new ArrayList<Integer>();
list.addAll({1,2,1,3,4,5,2,3,4,3});
for (int i=0; i<list.size(); i++)
{
for (int j=i+1; j<list.size(); j++)
{
if (list.get(i) == list.get(j))
{
list.remove(i);
j--;
}
}
}
或只是这样做:
SetList<Integer> unique = new SetList<Integer>();
unique.addAll(list);
两种方式都有时间= nk~O(n ^ 2)
其中n是输入列表的大小,
k是输入列表的唯一成员数
答案 14 :(得分:0)
算法delete_duplicates(a [1 .... n])
//从给定数组中删除重复项
//输入参数:a [1:n],n个元素的数组
{
temp[1:n];
// n个元素的数组
temp[i]=a[i];for i=1 to n
temp[i].value=a[i]
temp[i].key=i
* //基于'value'对数组临时排序。 *
//基于'value'删除temp中的重复元素。
//基于'key'排序数组temp.//使用temp。构建一个数组p
p[i]=temp[i].value
return p
使用'key'在输出数组中维护其他元素。考虑密钥长度为O(n),对密钥执行排序所花费的时间和值为O(nlogn)。因此,从数组中删除所有重复项所花费的时间是O(nlogn)。
答案 15 :(得分:0)
接近已接受答案的通用解决方案
k = ['apple', 'orange', 'orange', 'grapes', 'apple', 'apple', 'apple']
m = []
def remove_duplicates(k):
for i in range(len(k)):
for j in range(i, len(k)-1):
if k[i] == k[j+1]:
m.append(j+1)
l = list(dict.fromkeys(m))
l.sort(reverse=True)
for i in l:
k.pop(i)
return k
print(remove_duplicates(k))