用swift创建一个quick sort 的演示动画

前言

visualgo这个网站启发,于是想要实现一个iOS版的排序动画演示版,本篇主要介绍如何用swift3写一个带有演示效果快速排序。同时希望以这种方式帮助大家趣味性、具体性的在脑海里形成回路来学习算法
你也可以在我的GitHub中下载源码github Example
如有错误欢迎指正,同时欢迎在GitHub上提issue

QuickSort.gif

提供自定义配置

需修改请参考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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容