Excel·VBA数组冒泡排序函数

76 篇文章 27 订阅
8 篇文章 0 订阅

1,一维数组冒泡排序函数

Function bubble_sort(ByVal arr, Optional mode$ = "+")
    '函数定义bubble_sort(数组,排序模式)对一维数组数据进行排序,返回一个有序一维数组
    '2种排序模式,"+"即升序、"-"即降序
    Dim i&, j&, sorted As Boolean, temp, last_index&, sort_border&
    sort_border = UBound(arr) - 1  '排序边界,之后为有序,减少循环
    If mode = "+" Then
        For i = LBound(arr) To UBound(arr)
            sorted = True    '初始为有序,避免中途有序后的无效循环
            For j = LBound(arr) To sort_border
                If arr(j) > arr(j + 1) Then  '交换数据
                    temp = arr(j): arr(j) = arr(j + 1): arr(j + 1) = temp
                    sorted = False: last_index = j  '最后排序的序号
                End If
            Next
            sort_border = last_index ': Debug.Print "sort_border", sort_border
            If sorted Then Exit For   '如果为有序,则退出循环
        Next
    ElseIf mode = "-" Then
        For i = LBound(arr) To UBound(arr)
            sorted = True
            For j = LBound(arr) To sort_border
                If arr(j) < arr(j + 1) Then
                    temp = arr(j): arr(j) = arr(j + 1): arr(j + 1) = temp
                    sorted = False: last_index = j
                End If
            Next
            sort_border = last_index ': Debug.Print "sort_border", sort_border
            If sorted Then Exit For
        Next
    End If
    bubble_sort = arr
End Function

2,二维数组冒泡排序函数

单列排序版

注意:仅适用对二维数组的单列值进行排序,且在数字文字混合且数字位数不同的情况下,排序错误(不推荐使用)

Function bubble_sort_arr(ByVal arr, col&, Optional mode$ = "+")
    '函数定义bubble_sort_arr(数组,排序列,排序模式)对二维数组数据的指定列进行排序,返回一个有序二维数组
    '2种排序模式,"+"即升序、"-"即降序
    Dim i&, j&, t&, sorted As Boolean, temp, last_index&, sort_border&
    sort_border = UBound(arr) - 1  '排序边界,之后为有序,减少循环
    If mode = "+" Then
        For i = LBound(arr) To UBound(arr)
            sorted = True    '初始为有序,避免中途有序后的无效循环
            For j = LBound(arr) To sort_border
                If arr(j, col) > arr(j + 1, col) Then
                    For t = LBound(arr, 2) To UBound(arr, 2)  '交换数据,数组整行
                        temp = arr(j, t): arr(j, t) = arr(j + 1, t): arr(j + 1, t) = temp
                    Next
                    sorted = False: last_index = j  '最后排序的序号
                End If
            Next
            sort_border = last_index ': Debug.Print "sort_border", sort_border
            If sorted Then Exit For  '如果为有序,则退出循环
        Next
    ElseIf mode = "-" Then
        For i = LBound(arr) To UBound(arr)
            sorted = True
            For j = LBound(arr) To sort_border
                If arr(j, col) < arr(j + 1, col) Then
                    For t = LBound(arr, 2) To UBound(arr, 2)
                        temp = arr(j, t): arr(j, t) = arr(j + 1, t): arr(j + 1, t) = temp
                    Next
                    sorted = False: last_index = j
                End If
            Next
            sort_border = last_index ': Debug.Print "sort_border", sort_border
            If sorted Then Exit For
        Next
    End If
    bubble_sort_arr = arr
End Function
举例

《excel吧提问-按数字大小排序》,由于数据不规范、数字序号的位数不同,因此需要先对数据进行分割,然后调用函数排序
考虑到实际应用中可能存在不同年度,因此先对“执”字之前的内容排序,再分别对“执”字之前同样内容的“执”字之后的内容排序

Sub 排序测试()
    tm = Now()
    Dim arr, temp, brr, crr, result, i, j, k, first, last, write_col, write_row
'------参数填写
    write_col = "e"         '写入区域,列名,附加在列尾
    Cells(1, write_col).Value = "标题"
    arr = [b2:b19].Value
    ReDim Preserve arr(1 To UBound(arr), 1 To 3)
    For i = 1 To UBound(arr)
        temp = Split(arr(i, 1), "执")
        arr(i, 2) = temp(0): arr(i, 3) = Val(temp(1))  'val()提取文字前的数字
    Next
    brr = bubble_sort_arr(arr, 2, "+")  '对"执"之前的内容排序
    first = 1
    For j = 1 To UBound(brr) - 1
        If brr(j, 2) <> brr(j + 1, 2) Then  '对"执"之前的内容相等的排序
            last = j
            ReDim crr(1 To last - first + 1, 1 To 2)
            For k = first To last    '数组截取
                crr(k - first + 1, 1) = brr(k, 1): crr(k - first + 1, 2) = brr(k, 3)
            Next
            result = bubble_sort_arr(crr, 2, "+")
            write_row = Cells(1, write_col).CurrentRegion.Rows.count + 1
            Cells(write_row, write_col).Resize(UBound(result), 1) = result  '仅返回排序后的内容
        ElseIf j = UBound(brr) - 1 Then  '最后一组数据,无论单行多行
            last = UBound(brr)
            ReDim crr(1 To last - first + 1, 1 To 2)
            For k = first To last    '数组截取
                crr(k - first + 1, 1) = brr(k, 1): crr(k - first + 1, 2) = brr(k, 3)
            Next
            result = bubble_sort_arr(crr, 2, "+")
            write_row = Cells(1, write_col).CurrentRegion.Rows.count + 1
            Cells(write_row, write_col).Resize(UBound(result), 1) = result  '仅返回排序后的内容
            Exit For  '结束循环
        End If
        first = last + 1  '重置开始行
    Next
    Debug.Print ("排序完成,累计用时" & Format(Now() - tm, "hh:mm:ss"))  '耗时
End Sub

返回结果
在这里插入图片描述

多列排序版

多列排序:对需要排序的多列依次排序,除第1列外后面需要排序的列,需要在前一列值相同的情况下进行排序

2.1,字符串拼接法

代码思路:对需要排序的n列的值提取出来,每行拼接为1个字符串,并标记所属行号,组成多行2列数组,再对拼接字符串进行排序,最后将原始数组按排序后的行号顺序写入新数组输出
单列排序版一样,在数字文字混合且数字位数不同的情况下,排序错误

Function bubble_sort2d(ByVal arr, ByVal key_col, Optional mode$ = "+")
    '函数定义bubble_sort2d(数组,列号数组,排序模式)对二维数组数据的指定列号数组进行排序,返回一个有序二维数组
    '2种排序模式,"+"即升序、"-"即降序;字符串拼接法;返回数组从1开始计数
    Dim i&, j&, m&, n&, r&, t&, sorted As Boolean, temp, last_index&, sort_border&, delimiter$, s$
    If LBound(arr) = 0 Or LBound(arr, 2) = 0 Then  '转为从1开始计数
        arr = WorksheetFunction.Transpose(WorksheetFunction.Transpose(arr))
    End If
    m = UBound(arr): n = UBound(arr, 2): sort_border = m - 1: delimiter = Chr(28) '分隔符
    ReDim result(1 To m, 1 To n): ReDim brr(1 To m, 1 To 2)  '结果、临时数组
    For i = 1 To m  'key_col列号数组对应值进行拼接、行号写入brr
        s = ""
        For Each c In key_col
            s = s & delimiter & arr(i, c)
        Next
        brr(i, 1) = Mid(s, 2): brr(i, 2) = i
    Next
    For i = 1 To m  '对brr排序,升序
        sorted = True    '初始为有序,避免中途有序后的无效循环
        For j = 1 To sort_border
            If brr(j, 1) > brr(j + 1, 1) Then
                For t = 1 To 2    '交换数据,数组整行
                    temp = brr(j, t): brr(j, t) = brr(j + 1, t): brr(j + 1, t) = temp
                Next
                sorted = False: last_index = j  '最后排序的序号
            End If
        Next
        sort_border = last_index
        If sorted Then Exit For   '如果为有序,则退出循环
    Next
    If mode = "+" Then  '写入结果数组
        For i = 1 To m
            r = brr(i, 2)  '对应brr排序行号
            For j = 1 To n
                result(i, j) = arr(r, j)
            Next
        Next
    ElseIf mode = "-" Then
        For i = 1 To m
            r = brr(m + 1 - i, 2)
            For j = 1 To n
                result(i, j) = arr(r, j)
            Next
        Next
    End If
    bubble_sort2d = result
End Function
2.2,逐步字符串拼接法,数组版

代码思路:与2.1大体一样,对需要排序的n列的值提取出来、加上一列对应所属行号,组成多行n+1列数组brr,依次对数组1-n列进行排序,且如果当前排序列之前有多列(2列以上)以排序的值,则将值拼接为字符串,写入当前排序列的前一列,最后将原始数组按排序后的行号顺序写入新数组输出
2.1不同,需要排序列中包含纯数字列,且数字位数不同的,最终排序结果正确(推荐使用)

Function bubble_sort2d_v1(ByVal arr, ByVal key_col, Optional mode$ = "+")
    '函数定义bubble_sort2d_v1(数组,列号数组,排序模式)对二维数组数据的指定列号数组进行排序,返回一个有序二维数组
    '2种排序模式,"+"即升序、"-"即降序;数组字符串拼接法;返回数组从1开始计数
    Dim i&, j&, m&, n&, r&, t&, sorted As Boolean, temp, last_index&, sort_border&, delimiter$
    If LBound(arr) = 0 Or LBound(arr, 2) = 0 Then  '转为从1开始计数
        arr = WorksheetFunction.Transpose(WorksheetFunction.Transpose(arr))
    End If
    m = UBound(arr): n = UBound(arr, 2): sort_border = m - 1: delimiter = Chr(28) '分隔符
    k = UBound(key_col) - LBound(key_col) + 1
    ReDim result(1 To m, 1 To n): ReDim brr(1 To m, 1 To k + 1)  '结果、临时数组
    For i = 1 To m  'key_col列号数组对应值、行号写入brr
        For Each c In key_col
            j = j + 1: brr(i, j) = arr(i, c)
        Next
        j = 0: brr(i, k + 1) = i
    Next
    For i = 1 To m  '对brr第1列排序,升序
        sorted = True    '初始为有序,避免中途有序后的无效循环
        For j = 1 To sort_border
            If brr(j, 1) > brr(j + 1, 1) Then
                For t = 1 To UBound(brr, 2)  '交换数据,数组整行
                    temp = brr(j, t): brr(j, t) = brr(j + 1, t): brr(j + 1, t) = temp
                Next
                sorted = False: last_index = j  '最后排序的序号
            End If
        Next
        sort_border = last_index
        If sorted Then Exit For   '如果为有序,则退出循环
    Next
    Dim kk&, r_s&, r_e&, s$
    kk = 2: r_s = 1: r_e = 1: s = brr(1, 1)  '当前排序列号;开始、结束行号;开始值
    Do While k > 1 And kk <= k  'key_col有多个值,则依次按列排序,字符串拼接法
        For i = r_s + 1 To m
            If brr(i, kk - 1) = s Then r_e = i Else Exit For  '更新结束行号
        Next
        If r_s <> r_e Then  '需要排序的部分
            sort_border = r_e - 1
            For i = r_s To r_e
                sorted = True
                For j = r_s To sort_border
                    If brr(j, kk) > brr(j + 1, kk) Then
                        For t = 1 To UBound(brr, 2)
                            temp = brr(j, t): brr(j, t) = brr(j + 1, t): brr(j + 1, t) = temp
                        Next
                        sorted = False: last_index = j
                    End If
                Next
                sort_border = last_index
                If sorted Then Exit For
            Next
        End If
        r_s = r_e + 1: r_e = r_e + 1  '更新值:开始、结束行号,开始值
        If r_s >= m Then
            kk = kk + 1: r_s = 1: r_e = 1
            If kk > 2 And kk <= k Then  '当前排序列号 >2 时,前2列字符串合并至kk-1列(即kk-2和kk-1合并)
                For i = 1 To m
                    brr(i, kk - 1) = brr(i, kk - 2) & delimiter & brr(i, kk - 1)
                Next
            End If
        End If
        s = brr(r_s, kk - 1)
    Loop
    If mode = "+" Then  '写入结果数组
        For i = 1 To m
            r = brr(i, k + 1) '对应brr排序行号
            For j = 1 To n
                result(i, j) = arr(r, j)
            Next
        Next
    ElseIf mode = "-" Then
        For i = 1 To m
            r = brr(m + 1 - i, k + 1)
            For j = 1 To n
                result(i, j) = arr(r, j)
            Next
        Next
    End If
    bubble_sort2d_v1 = result
End Function
2.3,逐步字符串拼接法,字典版

代码思路:与2.2大体一样,对当前排序列前一列的值不使用循环判断相等,而是写入字典,字典键为前一列的值,字典值为数组(计数、开始行号、结束行号)。再遍历字典,对计数 >2 的数组 开始行号-结束行号 进行排序,最后将原始数组按排序后的行号顺序写入新数组输出
2.2一样,需要排序列中包含纯数字列,且数字位数不同的,最终排序结果正确

Function bubble_sort2d_v2(ByVal arr, ByVal key_col, Optional mode$ = "+")
    '函数定义bubble_sort2d_v2(数组,列号数组,排序模式)对二维数组数据的指定列号数组进行排序,返回一个有序二维数组
    '2种排序模式,"+"即升序、"-"即降序;字典字符串拼接法;返回数组从1开始计数
    Dim i&, j&, m&, n&, r&, t&, sorted As Boolean, temp, last_index&, sort_border&, delimiter$
    If LBound(arr) = 0 Or LBound(arr, 2) = 0 Then  '转为从1开始计数
        arr = WorksheetFunction.Transpose(WorksheetFunction.Transpose(arr))
    End If
    m = UBound(arr): n = UBound(arr, 2): sort_border = m - 1: delimiter = Chr(28) '分隔符
    k = UBound(key_col) - LBound(key_col) + 1
    ReDim result(1 To m, 1 To n): ReDim brr(1 To m, 1 To k + 1)  '结果、临时数组
    For i = 1 To m  'key_col列号数组对应值、行号写入brr
        For Each c In key_col
            j = j + 1: brr(i, j) = arr(i, c)
        Next
        j = 0: brr(i, k + 1) = i
    Next
    For i = 1 To m  '对brr第1列排序,升序
        sorted = True    '初始为有序,避免中途有序后的无效循环
        For j = 1 To sort_border
            If brr(j, 1) > brr(j + 1, 1) Then
                For t = 1 To UBound(brr, 2)  '交换数据,数组整行
                    temp = brr(j, t): brr(j, t) = brr(j + 1, t): brr(j + 1, t) = temp
                Next
                sorted = False: last_index = j  '最后排序的序号
            End If
        Next
        sort_border = last_index
        If sorted Then Exit For   '如果为有序,则退出循环
    Next
    Dim kk&, dict As Object, r_s&, r_e&
    kk = 2: Set dict = CreateObject("scripting.dictionary")  '当前排序列号,字典
    Do While k > 1 And kk <= k  'key_col有多个值,则依次按列排序,字典字符串拼接法
        If kk > 2 Then  '当前排序列号 >2 时,前2列字符串合并至kk-1列(即kk-2和kk-1合并)
            For i = 1 To m
                brr(i, kk - 1) = brr(i, kk - 2) & delimiter & brr(i, kk - 1)
            Next
        End If
        For i = 1 To m  'kk-1列值写入字典
            temp = brr(i, kk - 1)
            If Not dict.Exists(temp) Then
                dict(temp) = Array(1, i, i)  '计数、开始行号、结束行号
            Else
                c = dict(temp)(0) + 1: dict(temp) = Array(c, dict(temp)(1), i)
            End If
        Next
        For Each ks In dict.keys
            If dict(ks)(0) > 1 Then  '有重复值,则需排序
                r_s = dict(ks)(1): r_e = dict(ks)(2): sort_border = r_e - 1  '开始行号、结束行号
                For i = r_s To r_e
                    sorted = True
                    For j = r_s To sort_border
                        If brr(j, kk) > brr(j + 1, kk) Then
                            For t = 1 To UBound(brr, 2)
                                temp = brr(j, t): brr(j, t) = brr(j + 1, t): brr(j + 1, t) = temp
                            Next
                            sorted = False: last_index = j
                        End If
                    Next
                    sort_border = last_index
                    If sorted Then Exit For
                Next
            End If
        Next
        kk = kk + 1: dict.RemoveAll
    Loop
    If mode = "+" Then  '写入结果数组
        For i = 1 To m
            r = brr(i, k + 1) '对应brr排序行号
            For j = 1 To n
                result(i, j) = arr(r, j)
            Next
        Next
    ElseIf mode = "-" Then
        For i = 1 To m
            r = brr(m + 1 - i, k + 1)
            For j = 1 To n
                result(i, j) = arr(r, j)
            Next
        Next
    End If
    bubble_sort2d_v2 = result
End Function
3种代码对比

单列排序版举例为例

Sub bubble_sort2d测试()
    Dim arr, brr
    arr = [a2:b19]
    brr = bubble_sort2d(arr, Array(1, 2))
    [d2].Resize(UBound(brr), UBound(brr, 2)) = brr
    brr = bubble_sort2d_v1(arr, Array(1, 2))
    [g2].Resize(UBound(brr), UBound(brr, 2)) = brr
    brr = bubble_sort2d_v2(arr, Array(1, 2))
    [j2].Resize(UBound(brr), UBound(brr, 2)) = brr
End Sub

2列都是数字文字混合的数据,3种代码排序结果相同
在这里插入图片描述
第2列为纯数字,2.1排序错误,其他2种排序正确
在这里插入图片描述
第2列统一数字位数,3种代码排序结果相同
在这里插入图片描述
3种代码运行速度,随机数排序耗时秒数,速度都挺快的
在这里插入图片描述

参考资料:《冒泡排序》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔_51

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值