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

前言

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

用swift创建一个quick sort 的演示动画_第1张图片
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)
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) {
        self.clearView()
        count=array.count
//计算item之间的间隙
        gap=(self.frame.width-CGFloat(count)*itemWidth)/CGFloat(count+1)
        //for 循环渲染 item
        for i in 0.. 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来存储,
//          每次里面只有一个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=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 的演示动画)