基数排序

526 阅读3分钟

算法思想

  • 基于LSD方法的链式基数排序的基本思想
    “多关键字排序”的思想实现“单关键字排序”。对数字型或字符型的单关键字,可以看作由多个数位或多个字符构成的多关键字,此时可以采用“分配-收集”的方法进行排序,这一过程称作基数排序法,其中每个数字或字符可能的取值个数称为基数。比如,扑克牌的花色基数为4,面值基数为13。在整理扑克牌时,既可以先按花色整理,也可以先按面值整理。按花色整理时,先按红、黑、方、花的顺序分成4摞(分配),再按此顺序再叠放在一起(收集),然后按面值的顺序分成13摞(分配),再按此顺序叠放在一起(收集),如此进行二次分配和收集即可将扑克牌排列有序。

基数排序实现步骤

* 判断数据在各位的大小,排列数据
* 根据上一步的结果,判断数据在十分位的大小,排列数据。如果数据在这个位置的余数相同,那么数据之间的顺序根据上一轮的排列顺序确定
* 依次类推,继续判断数据在百分位、千分位......上面的数据重新排序,直到所有的数据在某一分位上数据都为0 
  • 基数排序实现步骤实例
    假设有欲排数据序列如下所示:
    73 22 93 43 55 14 28 65 39 81
    首先根据个位数的数值,在遍历数据时将它们各自分配到编号0至9的桶(个位数值与桶号一一对应)中。

    分配结束后。接下来将所有桶中所盛数据按照桶号由小到大(桶中由顶至底)依次重新收集串起来,得到如下仍然无序的数据序列:
    81 22 73 93 43 14 55 65 28 39
    接着,再进行一次分配,这次根据十位数值来分配(原理同上)

    分配结束后。接下来再将所有桶中所盛的数据(原理同上)依次重新收集串接起来,得到如下的数据序列:
    14 22 28 39 43 55 65 73 81 93
    观察可以看到,此时原无序数据序列已经排序完毕。如果排序的数据序列有三位数以上的数据,则重复进行以上的动作直至最高位数为止。

代码

#pragma mark --- 基数排序
/**
 基数排序
 */
import UIKit

class ViewController: UIViewController {
    var intArray:[Int] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        for _ in 0..<20 {
            intArray.append(Int(arc4random()%30))
        }
        let tempArr = radixSort(&intArray)
        for item in tempArr {
            print(item)
        }
    }

    func radixSort(_ array:inout [Int]) ->[Int] {
        //待排数组的最大数值
        let maxNumber = listMaxItem(array)
        //最大数值的数字位数
        let maxLength = numberLength(maxNumber)
        for digit in 1...maxLength {
            //创建空桶
            var bucket:[[Int]] = createBucket()
            // 入桶
            for item in array {
                //确定item 归属哪个桶 以digit位数为基数
                let baseNumber = fetchBaseNumber(item, digit)
                var mutArray = bucket[baseNumber]
                mutArray.append(item)
                bucket[baseNumber] = mutArray
            }
            var index = 0
            //出桶
            for i in 0..<bucket.count {
                var arrayTemp = bucket[i]
                //将桶的数据放回待排数组中
                while arrayTemp.count != 0 {
                    let number = arrayTemp[0]
                    array[index] = number
                    arrayTemp.removeFirst()
                    index += 1
                }
            }
        }
        return array
    }

    //MARK:创建空桶
    func createBucket() ->[[Int]] {
        var bucket:[[Int]] = []
        for _ in 0..<10 {
            let tempArray:[Int] = []
            bucket.append(tempArray)
        }
        return bucket
    }
    //MARK:数据最大值
    func listMaxItem(_ array:[Int]) ->Int {
        var maxNumber = array[0]
        for ele in array {
            if ele > maxNumber {
                maxNumber = ele
            }
        }
        return maxNumber
    }
    //MARK:数字的位数
    func numberLength(_ number:Int) ->Int {
        let numberStr = String(number)
        return numberStr.count
    }

    func fetchBaseNumber(_ number:Int,_ digit:Int) ->Int {
        let length = numberLength(number)
        //digit为基数位数
        if digit > 0 && digit <= length {
            //number的位数确定
            let string = String(number)
            let strArr:[Character] = Array(string)
            let str:Character = strArr[strArr.count - digit]
            return Int.init(String(str))!
        }
        return 0
    }
}

以上代码仅限数字排序,加入数字+字母的组合呢? 0~9:对应0~9,a~z:对应10~36
将以上代码修改为:

  • 1.创建空桶为0~36
  • 2.数据最大值改为最长字符串
  • 3.数字的位数改为字符串的长度 按以上修改即可