Sorting in linear time and in place

前端 未结 4 1860
感情败类
感情败类 2020-12-28 19:52

Suppose that n records have keys in the range from 1 to k.

  • Write an algorithm to sort the records in place in O(n+k) time.
  • You may us
4条回答
  •  梦谈多话
    2020-12-28 20:40

    An example for integer valued sequences. The sort is unstable. While it is not as concise as the answer provided by Mohit it is marginally faster (for the common case where k << n) by skipping elements already in their correct bins (time is asymptotically the same). In practice I prefer Mohit's sort for its tighter, simpler loop.

    def sort_inplace(seq):
        min_ = min(seq)
        max_ = max(seq)
        k = max_ - min_ + 1
        stop = [0] * k
        for i in seq:
            stop[i - min_] += 1
        for j in range(1, k):
            stop[j] += stop[j - 1]
        insert = [0] + stop[:k - 1]
        for j in range(k):
            while insert[j] < stop[j] and seq[insert[j]] == j + min_:
                insert[j] += 1
        tmp = None
        for j in range(k):
            while insert[j] < stop[j]:
                tmp, seq[insert[j]] = seq[insert[j]], tmp
                while tmp is not None:
                    bin_ = tmp - min_
                    tmp, seq[insert[bin_]] = seq[insert[bin_]], tmp
                    while insert[bin_] < stop[bin_] and seq[insert[bin_]] == bin_ + min_:
                        insert[bin_] += 1
    

    With a tighter loop but still skipping already-relocated elements:

    def dave_sort(seq):
        min_ = min(seq)
        max_ = max(seq)
        k = max_ - min_ + 1
        stop = [0] * k
    
        for i in seq:
            stop[i - min_] += 1
    
        for i in range(1, k):
            stop[i] += stop[i-1]
        insert = [0] + stop[:k - 1]
    
        for meh in range(0, k - 1):
            i = insert[meh]
            while i < stop[meh]:
                bin_ = seq[i] - min_
                if insert[bin_] > i:
                    tmp = seq[insert[bin_]]
                    seq[insert[bin_]] = seq[i]
                    seq[i] = tmp
                    insert[bin_] += 1
                else:
                    i += 1
    

    Edit: Mohit's approach in Python with extra bits to verify the effect on stability of the sort.

    from collections import namedtuple
    from random import randrange
    
    KV = namedtuple("KV", "k v")
    
    def mohit_sort(seq, key):
        f = lambda v: getattr(v, key)
        keys = map(f, seq)
        min_ = min(keys)
        max_ = max(keys)
        k = max_ - min_ + 1
        insert = [0] * k
    
        for i in keys:
            insert[i - min_] += 1
    
        insert[0] -= 1
        for i in range(1, k):
            insert[i] += insert[i-1]
    
        i = 0
        n = len(seq)
        while i < n:
            bin_ = f(seq[i])
            if insert[bin_] > i:
                seq[i], seq[insert[bin_]] = seq[insert[bin_]], seq[i]
                i -= 1
            insert[bin_] -= 1
            i += 1
    
    
    def test(n, k):
        seq = []
        vals = [0] * k
        for _ in range(n):
            key = randrange(k)
            seq.append(KV(key, vals[key]))
            vals[key] += 1
        print(seq)
        mohit_sort(seq, "k")
        print(seq)
    
    
    if __name__ == "__main__":
        test(20, 3)
    

提交回复
热议问题