VBA array sort function?

后端 未结 13 1945
北荒
北荒 2020-11-22 05:28

I\'m looking for a decent sort implementation for arrays in VBA. A Quicksort would be preferred. Or any other sort algorithm other than bubble or merge would suffice.

<
相关标签:
13条回答
  • 2020-11-22 05:51

    Heapsort implementation. An O(n log(n)) (both average and worst case), in place, unstable sorting algorithm.

    Use with: Call HeapSort(A), where A is a one dimensional array of variants, with Option Base 1.

    Sub SiftUp(A() As Variant, I As Long)
        Dim K As Long, P As Long, S As Variant
        K = I
        While K > 1
            P = K \ 2
            If A(K) > A(P) Then
                S = A(P): A(P) = A(K): A(K) = S
                K = P
            Else
                Exit Sub
            End If
        Wend
    End Sub
    
    Sub SiftDown(A() As Variant, I As Long)
        Dim K As Long, L As Long, S As Variant
        K = 1
        Do
            L = K + K
            If L > I Then Exit Sub
            If L + 1 <= I Then
                If A(L + 1) > A(L) Then L = L + 1
            End If
            If A(K) < A(L) Then
                S = A(K): A(K) = A(L): A(L) = S
                K = L
            Else
                Exit Sub
            End If
        Loop
    End Sub
    
    Sub HeapSort(A() As Variant)
        Dim N As Long, I As Long, S As Variant
        N = UBound(A)
        For I = 2 To N
            Call SiftUp(A, I)
        Next I
        For I = N To 2 Step -1
            S = A(I): A(I) = A(1): A(1) = S
            Call SiftDown(A, I - 1)
        Next
    End Sub
    
    0 讨论(0)
  • 2020-11-22 05:53

    I wonder what would you say about this array sorting code. It's quick for implementation and does the job ... haven't tested for large arrays yet. It works for one-dimensional arrays, for multidimensional additional values re-location matrix would need to be build (with one less dimension that the initial array).

           For AR1 = LBound(eArray, 1) To UBound(eArray, 1)
                eValue = eArray(AR1)
                For AR2 = LBound(eArray, 1) To UBound(eArray, 1)
                    If eArray(AR2) < eValue Then
                        eArray(AR1) = eArray(AR2)
                        eArray(AR2) = eValue
                        eValue = eArray(AR1)
                    End If
                Next AR2
            Next AR1
    
    0 讨论(0)
  • 2020-11-22 05:55

    Take a look here:
    Edit: The referenced source (allexperts.com) has since closed, but here are the relevant author comments:

    There are many algorithms available on the web for sorting. The most versatile and usually the quickest is the Quicksort algorithm. Below is a function for it.

    Call it simply by passing an array of values (string or numeric; it doesn't matter) with the Lower Array Boundary (usually 0) and the Upper Array Boundary (i.e. UBound(myArray).)

    Example: Call QuickSort(myArray, 0, UBound(myArray))

    When it's done, myArray will be sorted and you can do what you want with it.
    (Source: archive.org)

    Public Sub QuickSort(vArray As Variant, inLow As Long, inHi As Long)
      Dim pivot   As Variant
      Dim tmpSwap As Variant
      Dim tmpLow  As Long
      Dim tmpHi   As Long
    
      tmpLow = inLow
      tmpHi = inHi
    
      pivot = vArray((inLow + inHi) \ 2)
    
      While (tmpLow <= tmpHi)
         While (vArray(tmpLow) < pivot And tmpLow < inHi)
            tmpLow = tmpLow + 1
         Wend
    
         While (pivot < vArray(tmpHi) And tmpHi > inLow)
            tmpHi = tmpHi - 1
         Wend
    
         If (tmpLow <= tmpHi) Then
            tmpSwap = vArray(tmpLow)
            vArray(tmpLow) = vArray(tmpHi)
            vArray(tmpHi) = tmpSwap
            tmpLow = tmpLow + 1
            tmpHi = tmpHi - 1
         End If
      Wend
    
      If (inLow < tmpHi) Then QuickSort vArray, inLow, tmpHi
      If (tmpLow < inHi) Then QuickSort vArray, tmpLow, inHi
    End Sub
    

    Note that this only works with single-dimensional (aka "normal"?) arrays. (There's a working multi-dimensional array QuickSort here.)

    0 讨论(0)
  • 2020-11-22 05:55

    I think my code (tested) is more "educated", assuming the simpler the better.

    Option Base 1
    
    'Function to sort an array decscending
    Function SORT(Rango As Range) As Variant
        Dim check As Boolean
        check = True
        If IsNull(Rango) Then
            check = False
        End If
        If check Then
            Application.Volatile
            Dim x() As Variant, n As Double, m As Double, i As Double, j As Double, k As Double
            n = Rango.Rows.Count: m = Rango.Columns.Count: k = n * m
            ReDim x(n, m)
            For i = 1 To n Step 1
                For j = 1 To m Step 1
                    x(i, j) = Application.Large(Rango, k)
                    k = k - 1
                Next j
            Next i
            SORT = x
        Else
            Exit Function
        End If
    End Function
    
    0 讨论(0)
  • 2020-11-22 05:55

    Somewhat related, but I was also looking for a native excel VBA solution since advanced data structures (Dictionaries, etc.) aren't working in my environment. The following implements sorting via a binary tree in VBA:

    • Assumes array is populated one by one
    • Removes duplicates
    • Returns a separated string ("0|2|3|4|9") which can then be split.

    I used it for returning a raw sorted enumeration of rows selected for an arbitrarily selected range

    Private Enum LeafType: tEMPTY: tTree: tValue: End Enum
    Private Left As Variant, Right As Variant, Center As Variant
    Private LeftType As LeafType, RightType As LeafType, CenterType As LeafType
    Public Sub Add(x As Variant)
        If CenterType = tEMPTY Then
            Center = x
            CenterType = tValue
        ElseIf x > Center Then
            If RightType = tEMPTY Then
                Right = x
                RightType = tValue
            ElseIf RightType = tTree Then
                Right.Add x
            ElseIf x <> Right Then
                curLeaf = Right
                Set Right = New TreeList
                Right.Add curLeaf
                Right.Add x
                RightType = tTree
            End If
        ElseIf x < Center Then
            If LeftType = tEMPTY Then
                Left = x
                LeftType = tValue
            ElseIf LeftType = tTree Then
                Left.Add x
            ElseIf x <> Left Then
                curLeaf = Left
                Set Left = New TreeList
                Left.Add curLeaf
                Left.Add x
                LeftType = tTree
            End If
        End If
    End Sub
    Public Function GetList$()
        Const sep$ = "|"
        If LeftType = tValue Then
            LeftList$ = Left & sep
        ElseIf LeftType = tTree Then
            LeftList = Left.GetList & sep
        End If
        If RightType = tValue Then
            RightList$ = sep & Right
        ElseIf RightType = tTree Then
            RightList = sep & Right.GetList
        End If
        GetList = LeftList & Center & RightList
    End Function
    
    'Sample code
    Dim Tree As new TreeList
    Tree.Add("0")
    Tree.Add("2")
    Tree.Add("2")
    Tree.Add("-1")
    Debug.Print Tree.GetList() 'prints "-1|0|2"
    sortedList = Split(Tree.GetList(),"|")
    
    0 讨论(0)
  • 2020-11-22 06:02

    I posted some code in answer to a related question on StackOverflow:

    Sorting a multidimensionnal array in VBA

    The code samples in that thread include:

    1. A vector array Quicksort;
    2. A multi-column array QuickSort;
    3. A BubbleSort.

    Alain's optimised Quicksort is very shiny: I just did a basic split-and-recurse, but the code sample above has a 'gating' function that cuts down on redundant comparisons of duplicated values. On the other hand, I code for Excel, and there's a bit more in the way of defensive coding - be warned, you'll need it if your array contains the pernicious 'Empty()' variant, which will break your While... Wend comparison operators and trap your code in an infinite loop.

    Note that quicksort algorthms - and any recursive algorithm - can fill the stack and crash Excel. If your array has fewer than 1024 members, I'd use a rudimentary BubbleSort.

    Public Sub QuickSortArray(ByRef SortArray As Variant, _
                                    Optional lngMin As Long = -1, _ 
                                    Optional lngMax As Long = -1, _ 
                                    Optional lngColumn As Long = 0)
    On Error Resume Next
    'Sort a 2-Dimensional array
    ' Sample Usage: sort arrData by the contents of column 3 ' ' QuickSortArray arrData, , , 3
    ' 'Posted by Jim Rech 10/20/98 Excel.Programming
    'Modifications, Nigel Heffernan:
    ' ' Escape failed comparison with empty variant ' ' Defensive coding: check inputs
    Dim i As Long Dim j As Long Dim varMid As Variant Dim arrRowTemp As Variant Dim lngColTemp As Long

    If IsEmpty(SortArray) Then Exit Sub End If
    If InStr(TypeName(SortArray), "()") < 1 Then 'IsArray() is somewhat broken: Look for brackets in the type name Exit Sub End If
    If lngMin = -1 Then lngMin = LBound(SortArray, 1) End If
    If lngMax = -1 Then lngMax = UBound(SortArray, 1) End If
    If lngMin >= lngMax Then ' no sorting required Exit Sub End If

    i = lngMin j = lngMax
    varMid = Empty varMid = SortArray((lngMin + lngMax) \ 2, lngColumn)
    ' We send 'Empty' and invalid data items to the end of the list: If IsObject(varMid) Then ' note that we don't check isObject(SortArray(n)) - varMid might pick up a valid default member or property i = lngMax j = lngMin ElseIf IsEmpty(varMid) Then i = lngMax j = lngMin ElseIf IsNull(varMid) Then i = lngMax j = lngMin ElseIf varMid = "" Then i = lngMax j = lngMin ElseIf varType(varMid) = vbError Then i = lngMax j = lngMin ElseIf varType(varMid) > 17 Then i = lngMax j = lngMin End If

    While i <= j
    While SortArray(i, lngColumn) < varMid And i < lngMax i = i + 1 Wend
    While varMid < SortArray(j, lngColumn) And j > lngMin j = j - 1 Wend

    If i <= j Then
    ' Swap the rows ReDim arrRowTemp(LBound(SortArray, 2) To UBound(SortArray, 2)) For lngColTemp = LBound(SortArray, 2) To UBound(SortArray, 2) arrRowTemp(lngColTemp) = SortArray(i, lngColTemp) SortArray(i, lngColTemp) = SortArray(j, lngColTemp) SortArray(j, lngColTemp) = arrRowTemp(lngColTemp) Next lngColTemp Erase arrRowTemp
    i = i + 1 j = j - 1
    End If

    Wend
    If (lngMin < j) Then Call QuickSortArray(SortArray, lngMin, j, lngColumn) If (i < lngMax) Then Call QuickSortArray(SortArray, i, lngMax, lngColumn)

    End Sub

    0 讨论(0)
提交回复
热议问题