Merge sort to count split inversions in Python

妖精的绣舞 提交于 2019-11-30 15:40:32

问题


I am trying to use mergesort--which I get--to count the number of split inversions in a list (that is, where an element in the first half of the unsorted list should appear after a given element in the second half of the unsorted list; for example [3 2 1 4] would contain the split inversion (3, 1), but not (3, 2) as 3 and 2 are both in the first half). When I get to the final print statement, I am getting the answer I expect--in this case 9--but the return value is all wonky since it's returning the split value through the recursion. I've tried all sorts of combinations of indexing to no avail. Any help? (using Python 2.7)

(For the record, this is a Coursera homework problem, but I'm just learning for fun--no one's grading this other than me.)

def mergesort(lst):
    '''Recursively divides list in halves to be sorted'''
    if len(lst) is 1:
        return lst
    middle = int(len(lst)/2)
    left  = mergesort(lst[:middle])
    right = mergesort(lst[middle:])
    sortedlist = merge(left, right)
    return sortedlist

def merge(left, right):
    '''Subroutine of mergesort to sort split lists.  Also returns number
    of split inversions (i.e., each occurence of a number from the sorted second
    half of the list appearing before a number from the sorted first half)'''
    i, j = 0, 0
    splits = 0
    result = []
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
            splits += len(left[i:])
    result += left[i:]
    result += right[j:]
    print result, splits
    return result, splits


print mergesort([7,2,6,4,5,1,3,8])

回答1:


Modify your mergesort function to disregard the intermediate splits.

def mergesort(lst):
    '''Recursively divides list in halves to be sorted'''
    if len(lst) == 1:
        return lst, 0
    middle = len(lst)/2
    left = mergesort(lst[:middle])[0]  # Ignore intermediate splits
    right = mergesort(lst[middle:])[0]  # Ignore intermediate splits
    sortedlist, splits = merge(left, right)
    return sortedlist, splits



回答2:


There's a few things wrong in your code:

  • Don't int() the result of len() / 2. If you are using Python3, I'd directly use integer division with the // operator.
  • The comparison in the first line of mergesort() is wrong. Firstly, do not use is to compare for equality. The is operator is only intended for identity. If you have two different integers which have the same value, they are equal but not identical. For small integers, I believe that at least some Python dialects will intern the value, hence your comparison works, but you are relying on something that isn't guaranteed. Still, using == also doesn't work, because you forgot the case of the empty list.
  • I guess your actual problem ("wonky return value") is caused by the fact that you return two values that you store (as a tuple) under a single name and which you then pass as parameters to recursive merge() calls.



回答3:


Since, in your code merge returns a pair, mergesort must also return a pair. Enable to get the total split inversion in the list you must add the left half, right half and merge split inversions.

Here is the modification I have made in your code.

def mergesort(lst):
    '''Recursively divides list in halves to be sorted'''
    if len(lst) == 1:
        return lst, 0
    middle = len(lst)/2
    left, s1 = mergesort(lst[:middle])[0]  # Ignore intermediate splits
    right, s2 = mergesort(lst[middle:])[0]  # Ignore intermediate splits
    sortedlist, s3 = merge(left, right)
    return sortedlist, (s1+s2+s3)`


来源:https://stackoverflow.com/questions/14306088/merge-sort-to-count-split-inversions-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!