Search code examples
arraysvb.netsortinginfinite-loopquicksort

Endless loop in Quicksort algorithm when array has two instances of the same value


I've been trying to write up a Quicksort function in Visual Basic as practice. It was simple enough to do and it works fine for arrays that don't have the same number appearing twice. But as soon as that happens, the second iteration of the loop advancing the left pointer toward the right pointer gets stuck in an endless loop and I can't for the life of me figure out why that happens.

If anyone has an idea why it's stuck, I'd appreciate the help. Here's the code:

Public Function Partition(ByVal a As Integer(), ByVal left As Integer, ByVal right As Integer)

    Dim temp As Integer

    Do While left <> right

        Do While (a(left) < a(right)) And (left <> right)
            left = left + 1                
        Loop

        temp = a(left)
        a(left) = a(right)
        a(right) = temp

        Do While (a(left) < a(right)) And (left <> right)
            right = right - 1                
        Loop

        temp = a(left)
        a(left) = a(right)
        a(right) = temp

    Loop

    Return left
End Function

Sub QuickSort(ByVal a As Integer(), ByVal left As Integer, ByVal right As 
Integer)

    If left < right Then
        Dim position As Integer = Partition(a, left, right)
        QuickSort(a, left, position - 1)
        QuickSort(a, position + 1, right)
    End If

End Sub

Solution

  • It happens whenever a(left) and a(right) are the same. After you swap them, they are still the same so you swap again and again.
    To avoid that situation you need to

    1.Replace the loop below:

    Do While (a(left) < a(right)) And (left <> right)
                left = left + 1                
            Loop 
    

    with this:

    Do While (a(left) <= a(right)) And (left <> right)
                left = left + 1                
            Loop 
    

    in order to put the elements equal to the pivot, to its left side

    2.Add a one-step progress after the second loop (but only for the case when the iterators do not point at the same element which implies end of partitioning):

      Do While (a(left) < a(right)) And (left <> right)
                right = right - 1                
            Loop
    
            temp = a(left)
            a(left) = a(right)
            a(right) = temp
    
            If left <> right
               left = left + 1
            End if
    

    So the modified function looks like following:

    Public Function Partition(ByVal a As Integer(), ByVal left As Integer, ByVal right As Integer)
    
        Dim temp As Integer
    
        Do While left <> right
    
            Do While (a(left) <= a(right)) And (left <> right)
                left = left + 1                
            Loop
    
            temp = a(left)
            a(left) = a(right)
            a(right) = temp
    
            Do While (a(left) < a(right)) And (left <> right)
                right = right - 1                
            Loop
    
            temp = a(left)
            a(left) = a(right)
            a(right) = temp
    
            If left <> right
               left = left + 1
            End if
        Loop
    
        Return left
    End Function
    

    PS. It could be further optimized to avoid swapping equal elements or swapping the element with itself, I'll leave it for you