c++在数组中添加元素_化繁为简:Swift剔除数组中重复元素的几种姿势

4789b5edbcb38d90fb8c052173fcfd68.png

本文向大家介绍稍许算法的实现:关于如何去除数组中的重复元素,并比较了三种算法之间的效率.全部代码在Xcode的Playground中实现,直观明了,适合Swift学习入门童鞋观赏.

有个前提

如题,很多童鞋立即给出解决方法,无外乎是利用Swift内置的集合(Set)或字典(Dict)的一个特性:过滤重复元素.

但由于集合和字典中元素的顺序是无法保证的,所以这建立在一个前提基础之上:结果数组元素顺序和原数组可以不同!

但本文的算法要求:在剔除重复元素之后,元素顺序和原数组必须相同,这正是数组核心特点:有序性的一种体现.

第一种实现

如果想要简单,就必须要多些限制.

如果数组元素能满足Hashable协议,我们利用内置集合也未尝不可:

extension Array where Element:Hashable{ /// 返回剔除重复元素后的数组,其元素顺序不变 public var noRepetitionUseSet:[Element]{ var set = Set(self) var resultAry = [Element]()  for item in self{ if set.contains(item){ //只会保留第一个重复元素!!! resultAry.append(item) set.remove(item) } } return resultAry }}

如上,我们首先用集合过滤所有重复的元素,然后遍历数组,只保留第一个重复的元素.这样原有数组的顺序即得以保持不变.

数组操作是麻烦之源

如果不能保证数组元素满足Hashable协议,至少它们要满足Equatable协议.

这里略微复杂的地方在于,动态删除数组的元素需要考虑到它们删除的顺序.

删除后的黑洞立即会被后续所填满,仿佛根本不存在一样. — 鲁迅

334e6cf37e6fd8874a2f88f256592410.png

还有一个稍微棘手的问题在于:如何计算多个中间数组(它们只是原始数组的部分拷贝)元素在原始数组中对应的索引位置.

所以,这里不如让我们另辟蹊径,利用Swift的另一个特性: Optional!!!

有种类型叫:可有可无!

如果可以解决下面问题,就会使算法简单很多:

  • 不用直接删除重复元素
  • 简化定位元素的位置

这时Optional前来拯救我们了.

其核心思想是:不删除重复元素而是将其设置为nil!!!

extension Array where Element:Equatable{ public var noRepetition:[Element]{ var copy:[Element?] = self //下面只有非nil值才有比较的必要. for (i,x) in copy.enumerated(){ if x == nil {continue} for (j,y) in copy[(i+1)...].enumerated(){ if y == nil {continue} if x! == y! { //将原数组中对应的重复元素设置为nil copy[j+i+1] = nil } } } //剔除非空值,保持元素顺序 return copy.compactMap {$0} }}

如上,算法比较复杂,理解起来略微费劲!

永无止境

这个世界永远有更好的解决方法,就看你能不能发现了.

其实在不使用集合和字典的前提下,我们还可以这么写:

public var noRepetition2:[Element]{ var result = [Element]() for item in self{ if !result.contains(item) { result.append(item) } } return result}

我们只需要将每一个非重复的元素,放到一个新数组中,就完美的绕开了上面所有这些麻烦的东东…

问题来了:谁更快?快多少?

那么上面3种方法到底谁更快呢???

cc2d5ff4dc34702e4a59d63b0cce9b6c.png

因为是在Playground中运行,所以很快能写一个性能计算器来得到答案:

struct Timing{ var start:Date!  mutating func timingStart(){ start = Date() }  func timingStop()->Double{ return Date().timeIntervalSince(start) }  mutating func timing(count:Int,working:()->Void){ var totalTiming:Double = 0.0 for _ in 0..

因为系统的复杂性,我们不可能只用一两次执行来统计准确的运行时间.

测试代码如下:

let a = [1,1,1,2,3,4,1,3,4,5,6,2,1,1,1,1,17,7,7,8,9,2,1,0,1]var t = Timing()t.timing(count: 1000){ _ = a.noRepetitionUseSet}print(a.noRepetitionUseSet)t.timing(count: 1000){ _ = a.noRepetition}print(a.noRepetition)t.timing(count: 1000){ _ = a.noRepetition2}print(a.noRepetition2)

结果如下:

1000次平均执行时间 : 0.002724636197090149

[1, 2, 3, 4, 5, 6, 8, 17, 7, 9, 0]

1000次平均执行时间 : 0.004712764143943787

[1, 2, 3, 4, 5, 6, 8, 17, 7, 9, 0]

1000次平均执行时间 : 0.002008106589317322

[1, 2, 3, 4, 5, 6, 8, 17, 7, 9, 0]

首先可以看到三者返回剔除重复元素后的结果是完全一样的(这是必须的),另外它们的速度是第二种比第一种慢大约2ms左右,而最后一种比第一种还快一点点.我想日常的使用还是可以接受的.

(更正:只是对于[1,1,1,2,3,4,1,3,4,5,6,2,1,1,1,1,17,7,7,8,9,2,1,0,1]这个数组来说,第二种比第一种慢2ms,但随着数组元素个数的增加,后者会比前者慢更多!)

结尾

最后总结一下:

第一种算法利用内置集合类,对数组元素类型有限制,但速度快,逻辑简单.第二种算法依赖更少,但速度稍慢,逻辑略复杂.第三种算法速度最快,依赖少,逻辑最简单,可以作为首选.

虽然第一种方法利用了内部Swift库的优化,但创建集合的时间抵消了优化的时间,所以最后一种方法胜出!!!

上面我们实现了3种剔除重复元素的算法,并且比较了它们的优劣.

大家可以自由选择,如果你有更好的算法,欢迎讨论

这就是所有的内容了.

(PS:最后还有一种很简单的实现:

public var noRepetition3:[Element]{ var curIdx = -1 return self.filter { curIdx += 1 return self.index(of: $0) == curIdx }}

至于原理和速度大家可以遐想一下)

早睡早起,感谢观赏

ed33a64f7c35b6ab2058dfbd3e49b73b.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值