美文网首页iOS Developer程序员
用swift创建一个quick sort 的演示动画

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

作者: JarvanZhang | 来源:发表于2017-03-23 16:54 被阅读0次

    前言

    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

    相关文章

      网友评论

        本文标题:用swift创建一个quick sort 的演示动画

        本文链接:https://www.haomeiwen.com/subject/wniznttx.html