我已经定义了以下数组 Dim myArray(10,5) as Long
并想对其进行排序。最佳方法是什么?
我将需要处理许多类似 1000 x 5 矩阵的数据。它主要包含数字和日期,需要根据某一列进行排序。
我已经定义了以下数组 Dim myArray(10,5) as Long
并想对其进行排序。最佳方法是什么?
我将需要处理许多类似 1000 x 5 矩阵的数据。它主要包含数字和日期,需要根据某一列进行排序。
QuickSortArray MyArray,,,2...将“2”作为要排序的列,并排除传递搜索域的上限和下限的可选参数。
QuickSortVector Myarray,这里也要排除可选参数。
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
' SampleUsage: 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
...还有单列数组版本:
Public Sub QuickSortVector(ByRef SortArray As Variant, Optional lngMin As Long = -1, Optional lngMax As Long = -1)
On Error Resume Next
'Sort a 1-Dimensional array
' SampleUsage: sort arrData
'
' QuickSortVector arrData
'
' Originally posted by Jim Rech 10/20/98 Excel.Programming
' Modifications, Nigel Heffernan:
' ' Escape failed comparison with an empty variant in the array
' ' Defensive coding: check inputs
Dim i As Long
Dim j As Long
Dim varMid As Variant
Dim varX As Variant
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)
End If
If lngMax = -1 Then
lngMax = UBound(SortArray)
End If
If lngMin >= lngMax Then ' no sorting required
Exit Sub
End If
i = lngMin
j = lngMax
varMid = Empty
varMid = SortArray((lngMin + lngMax) \ 2)
' 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 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) < varMid And i < lngMax
i = i + 1
Wend
While varMid < SortArray(j) And j > lngMin
j = j - 1
Wend
If i <= j Then
' Swap the item
varX = SortArray(i)
SortArray(i) = SortArray(j)
SortArray(j) = varX
i = i + 1
j = j - 1
End If
Wend
If (lngMin < j) Then Call QuickSortVector(SortArray, lngMin, j)
If (i < lngMax) Then Call QuickSortVector(SortArray, i, lngMax)
End Sub
Public Sub BubbleSort(ByRef InputArray, Optional SortColumn As Integer = 0, Optional Descending As Boolean = False) ' 对1维或2维数组进行排序。
Dim iFirstRow As Integer Dim iLastRow As Integer Dim iFirstCol As Integer Dim iLastCol As Integer Dim i As Integer Dim j As Integer Dim k As Integer Dim varTemp As Variant Dim OutputArray As Variant
Dim iDimensions As Integer iDimensions = ArrayDimensions(InputArray)
Select Case iDimensions Case 1
iFirstRow = LBound(InputArray) iLastRow = UBound(InputArray) For i = iFirstRow To iLastRow - 1 For j = i + 1 To iLastRow If InputArray(i) > InputArray(j) Then varTemp = InputArray(j) InputArray(j) = InputArray(i) InputArray(i) = varTemp End If Next j Next i Case 2
iFirstRow = LBound(InputArray, 1) iLastRow = UBound(InputArray, 1) iFirstCol = LBound(InputArray, 2) iLastCol = UBound(InputArray, 2) If SortColumn InputArray(j, SortColumn) Then For k = iFirstCol To iLastCol varTemp = InputArray(j, k) InputArray(j, k) = InputArray(i, k) InputArray(i, k) = varTemp Next k End If Next j Next i
End Select
If Descending Then OutputArray = InputArray For i = LBound(InputArray, 1) To UBound(InputArray, 1) k = 1 + UBound(InputArray, 1) - i For j = LBound(InputArray, 2) To UBound(InputArray, 2) InputArray(i, j) = OutputArray(k, j) Next j Next i Erase OutputArray End If
End Sub
这个答案可能有点晚了,无法解决您需要时的问题,但其他人在搜索类似问题的答案时会找到它。
Option Base 1
。当我对一个二维数组进行排序时,第二列(也是排序列)的一个值为零。排序完成后,第一列的值也被设置为零。我花了将近两个小时才解决这个问题。最后,在模块中设置Option Base 1
后,一切都变得正常了。 - PhilType MyType
Field1 As Variant
Field2 As Variant
Field3 As Variant
Field4 As Variant
Field5 As Variant
End Type
Sub SortMyDataByField2(ByRef Data() As MyType)
Dim FirstIdx as Long, LastIdx as Long
FirstIdx = LBound(Data)
LastIdx = UBound(Data)
Dim I as Long, J as Long, Temp As MyType
For I=FirstIdx to LastIdx-1
For J=I+1 to LastIdx
If Data(I).Field2 > Data(J).Field2 Then
Temp = Data(I)
Data(I) = Data(J)
Data(J) = Temp
End If
Next J
Next I
End Sub
这是一个很难的问题,因为它取决于许多参数,但在分析了许多算法后,我选择了这个在整体表现良好的算法。在我的不太快的机器上,我用1秒钟对有100k行的3列数组进行排序。我尝试了较少的行,只需一瞬间就可以完成,但对于一百万行,根据不同的数据(未排序数据的百分比)要花费9到26秒。
其中一个例程用于升序排序,另一个例程则用于降序排序。iCol
是第二个参数,表示数组要根据哪一列进行排序。
Public Sub MedianThreeQuickSort1_2D_Asc(ByRef pvarArray As Variant, _
ByVal iCol As Integer, _
Optional ByVal plngLeft As Long, _
Optional ByVal plngRight As Long)
'Grade A+
'NOTE: recursive routine, omit plngLeft & plngRight; they are used internally during recursion
Dim j As Integer
Dim lngFirst As Long
Dim lngLast As Long
Dim varMid As Variant
Dim lngIndex As Long
Dim varSwap As Variant
Dim a As Long
Dim b As Long
Dim c As Long
If plngRight = 0 Then
plngLeft = LBound(pvarArray, 1)
plngRight = UBound(pvarArray, 1)
End If
lngFirst = plngLeft
lngLast = plngRight
lngIndex = plngRight - plngLeft + 1
a = Int(lngIndex * Rnd) + plngLeft
b = Int(lngIndex * Rnd) + plngLeft
c = Int(lngIndex * Rnd) + plngLeft
If pvarArray(a, iCol) <= pvarArray(b, iCol) And pvarArray(b, iCol) <= pvarArray(c, iCol) Then
lngIndex = b
Else
If pvarArray(b, iCol) <= pvarArray(a, iCol) And pvarArray(a, iCol) <= pvarArray(c, iCol) Then
lngIndex = a
Else
lngIndex = c
End If
End If
varMid = pvarArray(lngIndex, iCol)
Do
Do While pvarArray(lngFirst, iCol) < varMid And lngFirst < plngRight
lngFirst = lngFirst + 1
Loop
Do While varMid < pvarArray(lngLast, iCol) And lngLast > plngLeft
lngLast = lngLast - 1
Loop
If lngFirst <= lngLast Then
For j = LBound(pvarArray, 2) To UBound(pvarArray, 2)
varSwap = pvarArray(lngLast, j)
pvarArray(lngLast, j) = pvarArray(lngFirst, j)
pvarArray(lngFirst, j) = varSwap
Next j
lngFirst = lngFirst + 1
lngLast = lngLast - 1
End If
Loop Until lngFirst > lngLast
If (lngLast - plngLeft) < (plngRight - lngFirst) Then
If plngLeft < lngLast Then MedianThreeQuickSort1_2D_Asc pvarArray, iCol, plngLeft, lngLast
If lngFirst < plngRight Then MedianThreeQuickSort1_2D_Asc pvarArray, iCol, lngFirst, plngRight
Else
If lngFirst < plngRight Then MedianThreeQuickSort1_2D_Asc pvarArray, iCol, lngFirst, plngRight
If plngLeft < lngLast Then MedianThreeQuickSort1_2D_Asc pvarArray, iCol, plngLeft, lngLast
End If
End Sub
Public Sub MedianThreeQuickSort1_2D_Desc(ByRef pvarArray As Variant, _
ByVal iCol As Integer, _
Optional ByVal plngLeft As Long, _
Optional ByVal plngRight As Long)
'Grade A+
'NOTE: recursive routine, omit plngLeft & plngRight; they are used internally during recursion
Dim j As Integer
Dim lngFirst As Long
Dim lngLast As Long
Dim varMid As Variant
Dim lngIndex As Long
Dim varSwap As Variant
Dim a As Long
Dim b As Long
Dim c As Long
If plngRight = 0 Then
plngLeft = LBound(pvarArray, 1)
plngRight = UBound(pvarArray, 1)
End If
lngFirst = plngLeft
lngLast = plngRight
lngIndex = plngRight - plngLeft + 1
a = Int(lngIndex * Rnd) + plngLeft
b = Int(lngIndex * Rnd) + plngLeft
c = Int(lngIndex * Rnd) + plngLeft
If pvarArray(a, iCol) <= pvarArray(b, iCol) And pvarArray(b, iCol) <= pvarArray(c, iCol) Then
lngIndex = b
Else
If pvarArray(b, iCol) <= pvarArray(a, iCol) And pvarArray(a, iCol) <= pvarArray(c, iCol) Then
lngIndex = a
Else
lngIndex = c
End If
End If
varMid = pvarArray(lngIndex, iCol)
Do
Do While pvarArray(lngFirst, iCol) > varMid And lngFirst < plngRight
lngFirst = lngFirst + 1
Loop
Do While varMid > pvarArray(lngLast, iCol) And lngLast > plngLeft
lngLast = lngLast - 1
Loop
If lngFirst <= lngLast Then
For j = LBound(pvarArray, 2) To UBound(pvarArray, 2)
varSwap = pvarArray(lngLast, j)
pvarArray(lngLast, j) = pvarArray(lngFirst, j)
pvarArray(lngFirst, j) = varSwap
Next j
lngFirst = lngFirst + 1
lngLast = lngLast - 1
End If
Loop Until lngFirst > lngLast
If (lngLast - plngLeft) < (plngRight - lngFirst) Then
If plngLeft < lngLast Then MedianThreeQuickSort1_2D_Desc pvarArray, iCol, plngLeft, lngLast
If lngFirst < plngRight Then MedianThreeQuickSort1_2D_Desc pvarArray, iCol, lngFirst, plngRight
Else
If lngFirst < plngRight Then MedianThreeQuickSort1_2D_Desc pvarArray, iCol, lngFirst, plngRight
If plngLeft < lngLast Then MedianThreeQuickSort1_2D_Desc pvarArray, iCol, plngLeft, lngLast
End If
End Sub
有时候最简单的答案是最好的答案。
完成。这不会赢得任何编程奖项,但可以快速完成工作。
Sub sort_2d_array(ByRef arrayin As Variant, colid As Integer)
'theWidth = LBound(arrayin, 2) - UBound(arrayin, 2)
For i = LBound(arrayin, 1) To UBound(arrayin, 1)
searchVar = arrayin(i, colid)
For ii = LBound(arrayin, 1) To UBound(arrayin, 1)
compareVar = arrayin(ii, colid)
If (CInt(searchVar) > CInt(compareVar)) Then
For jj = LBound(arrayin, 2) To UBound(arrayin, 2)
larger1 = arrayin(i, jj)
smaller1 = arrayin(ii, jj)
arrayin(i, jj) = smaller1
arrayin(ii, jj) = larger1
Next jj
i = LBound(arrayin, 1)
searchVar = arrayin(i, colid)
End If
Next ii
Next i
End Sub
就我所知(此时我无法展示代码...让我看看是否可以编辑后发布),我创建了一个自定义对象的数组(因此每个属性都随其排序的元素一起出现),用每个元素对象感兴趣的属性填充了一组单元格,然后通过vba使用excel排序函数对列进行排序。我相信可能有更有效的排序方法,而不是将其导出到单元格中,只是我还没有想出来。这实际上帮了我很多,因为当我需要添加一个维度时,我只需为数组的下一个维度添加一个let和get属性。
你可以创建一个有两列的独立数组。第一列是你要排序的内容,第二列是在另一个数组中的行数。通过按第一列排序这个数组(只有在交换时才切换两列),然后你可以使用这两个数组来处理需要的数据。但是,大型数组可能会导致内存问题。
在对多列数组进行排序时,我不会重新排列元素。相反,我通过另一个具有相同数量元素的数组S,遍历并将项目编号为1、2、3等。
然后,我使用S中的值作为要排序的列的索引,并且当需要交换元素时,我交换S中的值。
在从排序返回时,如果需要,我可以根据S中的排序顺序重新排列原始数组。很容易将快速排序适应于此。
我有一个类似的Double数组需要排序,所以我决定编写一个本地的.dll文件。
为了测试,我使用64位整数,因此您可以将其用于对Long
和ULong
数组的最后一维进行排序。
<DllImport("Arrayman.dll", EntryPoint:="SortLng")>
Sub sort(ByRef Array1stItem As Long, ByRef Indices1stItem As Integer, ByVal nItemsToSort As Long)
'Note: For sorting ULong integers, replace EntryPoint:="SortLng" with EntryPoint:="SortULng"
End Sub
在你的例子中,你会这样调用它
Dim idx(5)
sort(myArray(3,0), idx(0), idx.count)
将项目从(3, 0)
排序到(3, 5)
。
最小的数字在myArray(3, idx(0))
,最高的数字在myArray(3, idx(5))
。
ArrayMan.dll,更多信息和示例可在GitHub上找到。