Quicksort in-place for list of lists

Question:

I got a task to sort data with quicksort in-place. The input is a list of lists (name, score1, score2).

The data should be sorted by score1 in descending order, then (if equal) by score2 in ascending order, then (if equal) by name in ascending order.

Example input:

5
antony 4 100
jack 6 1000
jerry 2 90
ricky 2 90
timy 4 80

Expected output:

jack
timy
antony
jerry
ricky

So far I have coded the quicksort in-place algorithm and tested it with some custom input, but I can’t figure out how to compare values in a list of lists. Or is there any other way to do this comparison?

import random

def compare(l1, l2):
    if l1[1] > l2[1]:
        return True
    elif l1[1] == l2[1] and l1[2] < l2[2]:
        return True
    elif l1[2] == l2[2] and l1[1] == l2[1] and l1[0] > l1[0]:
        return True
    else:
        return False

def quicksort(A, l=0, r=None):
    if r is None:
        r = len(A) - 1
    # end = len(array) - 1
    if l >= r:
        return
    else:
        q = random.choice(A[l:r + 1])
        i = l
        j = r
        while i <= j:
            while compare(q, A[i]):
                i += 1
            while compare(A[j], q):
                j -= 1
            if i <= j:
                A[i], A[j] = A[j], A[i]
                i += 1
                j -= 1
                quicksort(A, l, j)
                quicksort(A, i, r)
    return A

def read_input():
    k = int(input())
    values = []
    while k > 0:
        n = input().split()
        values.append(n[0])
        values.append(n[1])
        values.append(n[2])
        k -= 1
    return values
Asked By: r3tr0

||

Answers:

There are the following issues in your code:

  • read_input reads all values in a 1 dimensional list, i.e. it doesn’t create a list of triplets

  • read_input does not convert the scores to int data type, but leaves them as strings, which will have an impact on how they are compared by compare

    To solve these two issues, change the body of the loop in read_input with this:

    name, score1, score2 = input().split()
    values.append([name, int(score1), int(score2)])
    k -= 1
    
  • compare returns True when the first argument should be ordered after the second argument. When you call compare you treat True as a message not to swap. But you pass the arguments in the order as they appear in the input, which means you should swap when you get True. So here the code is wrong. Fix by passing the arguments in opposite order.

  • The recursive calls of quickSort are made at the wrong time. These calls should not be done during the loop, but after it.

  • quicksort has a return without list and a return A with list. This is not consistent. As quicksort is inplace it is more pythonic to not return the list, so change that final return. The caller should take the list that it passed as argument, which will have been sorted.

  • compare has a typo in the string comparison: it has l1[0] > l1[0] (the two operands are the same), which should be l2[0] > l1[0]

Here is your code with the above mentioned corrections:

def compare(l1, l2):
    if l1[1] > l2[1]:
        return True
    elif l1[1] == l2[1] and l1[2] < l2[2]:
        return True
    elif l1[2] == l2[2] and l1[1] == l2[1] and l2[0] > l1[0]:
        return True
    else:
        return False

def quicksort(A, l=0, r=None):
    if r is None:
        r = len(A) - 1
    if l >= r:
        return
    else:
        q = random.choice(A[l:r + 1])
        i = l
        j = r
        while i <= j:
            while compare(A[i], q):  # args swapped
                i += 1
            while compare(q, A[j]):  # args swapped
                j -= 1
            if i <= j:
                A[i], A[j] = A[j], A[i]
                i += 1
                j -= 1
        quicksort(A, l, j)  # Fixed indentation
        quicksort(A, i, r)
    return  # Don't return A

def read_input():
    k = int(input())
    values = []
    while k > 0:
        name, score1, score2 = input().split()
        # Append a triple, and convert the scores to int:
        values.append([name, int(score1), int(score2)])
        k -= 1
    return values
Answered By: trincot