前言
受visualgo这个网站启发,于是想要实现一个iOS版的排序动画演示版,本篇主要介绍如何用swift3写一个带有演示效果快速排序。同时希望以这种方式帮助大家趣味性、具体性的在脑海里形成回路来学习算法
你也可以在我的GitHub中下载源码github Example
如有错误欢迎指正,同时欢迎在GitHub上提issue
提供自定义配置
需修改请参考QuickSortViewController.swift文件上部进行修改
let JVMaxNumber:UInt32 = 50 //产生随机数的最大值
let sleepTime:UInt32 = 300000 //1 sec = 1000000 休眠时间可调整动画快慢
let numberCount=13 //多少个待排序的数
let normalColor:UIColor = UIColor.init(red: 172/255, green: 216/255, blue: 231/255, alpha: 1.0)
let pivotColor:UIColor = UIColor.init(red: 255/255, green: 255/255, blue: 2/255, alpha: 1.0)
let currentColor:UIColor = UIColor.init(red: 220/255, green: 20/255, blue: 60/255, alpha: 1.0)
let minColor:UIColor = UIColor.init(red: 60/255, green: 179/255, blue: 113/255, alpha: 1.0)
let maxColor:UIColor = UIColor.init(red: 153/255, green: 51/255, blue: 204/255, alpha: 1.0)
let endColor:UIColor = UIColor.init(red: 255/255, green: 165/255, blue: 0/255, alpha: 1.0)
关键代码
定义类和接口
在下手之前,我们要构思好我们要写的东西,先抽象出类、写好提供给外部调用的方法。这也是一种好习惯与君共勉
我们需要自定义一个渲染的View来渲染出图表,这里我们定义为JVgraphView。
JVgraphView需要提供一些方法给ViewController调用
func drawGraph(array:Array<Int>)
func startSort()
同时我们还需要自定义一个每一个Item的View来展示当前的数字、长短、颜色,我们把它命名为JVgraphItemView,提供如下方法
init(frame: CGRect,itemValue:Int)
func changeState(state:GIoptionState)
//表示状态的枚举,根据状态来转换颜色
enum GIoptionState:Int{
case normal,pivot,current,min,max,end
}
填充定义方法
JVgraphItemView
注意 你将在本文多次见到 ** usleep(sleepTime)** 是为了让动画演示过程中有停顿,为了让UI不被卡住,都是在次线程中停顿,再返回到主线程渲染UI
//MARK: - init 根据frame 和当前的数字 渲染每一个Item
init(frame: CGRect,itemValue:Int) {
super.init(frame: frame)
number=itemValue
oneHeight=self.frame.height/CGFloat(JVMaxNumber)
let gHeight=oneHeight*CGFloat(itemValue)
let y = self.frame.height-gHeight
ghView.frame=CGRect.init(x: 0, y: y, width: self.frame.width, height: gHeight)
self.addSubview(ghView)
//大于临界值的数字渲染在图像里面
if itemValue>critical {
valueLabel.frame=CGRect.init(x: 0, y: y, width: self.frame.width, height: labelHeight)
}else{
valueLabel.frame=CGRect.init(x: 0, y: y-labelHeight, width: self.frame.width, height: labelHeight)
}
valueLabel.text=String(itemValue)
valueLabel.textColor=UIColor.black
valueLabel.textAlignment=NSTextAlignment.center
self.addSubview(valueLabel)
self.ghView.backgroundColor=normalColor
}
//根据状态改变颜色
func changeState(state:GIoptionState) {
DispatchQueue.main.async(execute: {
switch state {
case .normal:
self.ghView.backgroundColor=normalColor
case .current:
self.ghView.backgroundColor=currentColor
case .pivot:
self.ghView.backgroundColor=pivotColor
case .min:
self.ghView.backgroundColor=minColor
case .max:
self.ghView.backgroundColor=maxColor
default:
self.ghView.backgroundColor=endColor
}
})
}
JVgraphView code
// MARK: - draw UI
func drawGraph(array:Array<Int>) {
self.clearView()
count=array.count
//计算item之间的间隙
gap=(self.frame.width-CGFloat(count)*itemWidth)/CGFloat(count+1)
//for 循环渲染 item
for i in 0..<count {
let item=array[i]
let view=JVgraphItemView.init(frame: self.getItemRect(item: CGFloat(i)),itemValue: item)
self.addSubview(view)
viewArray.append(view)
}
}
//计算每一个item的frame
func getItemRect(item:CGFloat) -> CGRect {
var x:CGFloat=0,y:CGFloat=0,w:CGFloat=0,h:CGFloat=0
x=item==0 ? gap : (item+1)*gap+item*itemWidth
y=0
w=itemWidth
h=self.frame.height
return CGRect.init(x: x, y: y, width: w, height: h)
}
在实现func startSort()的相应之前呢 我们需要实现快速排序:
func quickSort(start:Int,end:Int)
func swap(changeIdx:Int,toIdx:Int)
func swap 最简单我们先从简单的开始
func swap(changeIdx:Int,toIdx:Int){
if changeIdx==toIdx {
return
}
let view1=self.viewArray[changeIdx]
let view2=self.viewArray[toIdx]
let cRT:CGRect=view1.frame
let tRT:CGRect=view2.frame
//交换两个数据
let temporary:JVgraphItemView = self.viewArray[changeIdx]
self.viewArray[changeIdx]=self.viewArray[toIdx]
self.viewArray[toIdx]=temporary
usleep(sleepTime)
//创建信号量次线程等待主线程更新完毕之后在执行
let semaphore = DispatchSemaphore(value: 0)
//在主线程更新UI
DispatchQueue.main.sync {
UIView.animate(withDuration: 0.25, animations: {
view1.frame=tRT
view2.frame=cRT
}, completion: { (finished) in
// 执行完毕发送信号
semaphore.signal()
})
}
// 等待信号
semaphore.wait()
}
func quickSort的关键代码和注释
func quickSort(start:Int,end:Int) {
//把已经完成排序的改变endColor
if start == end{
usleep(sleepTime)
self.viewArray[start].changeState(state: .end)
}
//跳出递归
if start>=end {
return;
}
let startItem = self.viewArray[start]
usleep(sleepTime)
// 改变基准点颜色
startItem.changeState(state: .pivot)
let pivot:Int=startItem.number
var i:Int=start+1
var storeIndex:Int=i
while i<end+1 {
// 再进行排序之前需要改变之前进行过判断的item的颜色
// 这里我是把之前的信息用Dictionary<JVgraphItemView,Bool>来存储,
// 每次里面只有一个key和一个value 其他存储方式也是可以的, Bool表示之前的大小,根据大小改变对应的颜色
if lastItemDict.keys.count>0 {
for item:JVgraphItemView in (lastItemDict.keys) {
if (lastItemDict[item])!{
usleep(sleepTime)
item.changeState(state: .max)
}else{
usleep(sleepTime)
item.changeState(state: .min)
}
}
lastItemDict.removeAll()
}
// 改变当前光标颜色
let currentItem=self.viewArray[i]
usleep(sleepTime)
currentItem.changeState(state: .current)
//交换比较小的到大小相邻的位置
if currentItem.number<pivot {
self.swap(changeIdx: i, toIdx: storeIndex)
storeIndex+=1
// 存储大小信息
lastItemDict.updateValue(false, forKey: currentItem)
}else{
// 存储大小信息
lastItemDict.updateValue(true, forKey: currentItem)
}
i += 1
}
self.swap(changeIdx: start, toIdx: storeIndex-1)
usleep(sleepTime)
// 除了已经确定的基准点以外的点全部的状态改为normal
for (i,item) in self.viewArray.enumerated() {
if i>=start&&i<=end {
if item.isEqual(startItem) {
item.changeState(state: .end)
}else{
item.changeState(state: .normal)
}
lastItemDict.removeAll()
}
}
//判断特殊情况的颜色改变,只有两个item的时候全部改为end状态
if (end-start) == 1 {
usleep(sleepTime)
viewArray[end].changeState(state: .end)
}
// 递归调用
self.quickSort(start: start, end: storeIndex-2)
self.quickSort(start: storeIndex, end: end)
}
接下来只需要在func startSort里面调用就好了
// MARK: - QuckSort
func startSort(){
DispatchQueue.global().async {
self.quickSort(start: 0, end: self.viewArray.count-1)
// 提供给viewController 的回调
if self.finishAction != nil{
self.finishAction?()
}
usleep(sleepTime)
for item in self.viewArray{
item.changeState(state: .normal)
}
}
}
结束语
通过写完演示动画之后相信你一定不会在觉得算法枯燥了,无趣了吧。
后期如果有时间接着分享关于算法的趣味学习
Thank You
网友评论