美文网首页
算法小结(三):列表排序2

算法小结(三):列表排序2

作者: ShowMeCoding | 来源:发表于2021-04-14 22:25 被阅读0次

    一、希尔排序(Shell Sort)

    1、基本思路

    (1)首先取一个整数d1=n/2,将元素分为d1个组,每组相邻元素距离为d1,在各组内进行直接插入排序
    (2)取第二个整数d2=d1/2,重复上述分组排序过程,直到d1=1,即所有元素在同一组内进行直接插入排序
    (3)希尔排序每趟并不使所有元素都有序,而是整体数据越来越接近有序;最后一趟排序使所有数据有序

    2、代码实现

    插入排序+希尔排序

    def insert_sort_gap(li, gap):
        for i in range(gap, len(li)): #i 表示摸到的牌的下标
            tmp = li[i]
            j = i - gap #j指的是手里的牌的下标
            while j >= 0 and li[j] > tmp:
                li[j+gap] = li[j]
                j -= gap
            li[j+gap] = tmp
    
    def shell_sort(li):
        d = len(li) // 2
        while d >= 1:
            insert_sort_gap(li, d)
            d //= 2
        return li
    
    import random
    li = [i for i in range(16)]
    random.shuffle(li)
    print(li)
    print(shell_sort(li))
    >> [5, 0, 13, 2, 6, 14, 7, 12, 15, 3, 8, 9, 10, 11, 1, 4]
    >> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    

    整体实现算法

    def shell_sort(li):
        gap = len(li) // 2
        while gap > 0:
            for i in range(gap, len(li)):  # i表示摸到的牌的下标
                tmp = li[i]
                j = i - gap   # j 指的是手里的牌的下标
                while j >= 0 and tmp < li[j]:
                    li[j+gap] = li[j]
                    j -= gap
                li[j+gap] = tmp
            gap //= 2
        return li
    
    import random
    li = [i for i in range(16)]
    random.shuffle(li)
    print(li)
    print(shell_sort(li))
    >> [13, 1, 3, 6, 10, 2, 15, 12, 11, 0, 8, 9, 5, 7, 4, 14]
    >> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    
    • 时间复杂度:O(nlogn)
    • 稳定性:虽然插入排序是稳定的,但是由于希尔排序是跳跃性插入的,有可能破坏稳定性,因此不稳定。

    二、计数排序(Count Sort)

    1、基本思路

    对列表进行排序,已知列表中的数的范围都在0到100之间,设计时间复杂度为O(n)的算法。
    (1)通过遍历统计每个数出现的次数
    (2)按照统计结果依次添加到列表中

    2、代码实现

    def count_sort(li, max_count):
        count = [0 for _ in range(max_count + 1)]
        for val in li:
            count[val] += 1   # 实现计数,索引为原列表的值
        li.clear()
        for ind, val in enumerate(count):  # 此处的val为原列表值的个数
            for i in range(val):
                li.append(ind)   # 按个数添加列表的值
        return li
    
    import random
    li = [i for i in range(16)]
    random.shuffle(li)
    print(li)
    print(count_sort(li, 15))   # 列表里面的最大值为15
    [0, 4, 6, 11, 3, 1, 13, 5, 9, 8, 15, 14, 10, 2, 7, 12]
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    

    3、计数排序的主要问题

    当最大数很大时(1亿),需要占用的空间太多

    三、桶排序(Bucket Sort)

    1、基本思路

    (1)在计数排序中,如果元素范围比较大(比如在1到1亿之间,如何改善算法?)
    (2)首先将元素进行分桶,然后对每个桶中的元素进行排序
    (3)之后依次在桶中取数

    2、代码实现

    def bucket_sort(li, n, max_num):
        buckets = [[] for _ in range(n)]    # 分成 n 个桶
        for var in li:
            i = min(var//(max_num//n), n-1)  # i 表示把var放到几号桶中
            buckets[i].append(var)          # 把 var 放到对应的桶中
            # 保持桶内的顺序,使用冒泡排序
            for j in range(len(buckets[i])-1, 0, -1):
                if buckets[i][j] < buckets[i][j-1]:
                    buckets[i][j] , buckets[i][j-1] = buckets[i][j-1], buckets[i][j]
                else:
                    break
        # 按桶装入排序
        sorted_li = []
        for buc in buckets:
            sorted_li.extend(buc)
        return sorted_li    
    
    import random
    li = [random.randint(0,5) for i in range(20)]
    print(li)
    li = bucket_sort(li, 5, 20)
    print(li)
    >>[2, 2, 5, 1, 3, 4, 0, 3, 3, 4, 4, 4, 1, 0, 5, 3, 0, 3, 2, 0]
    >>[0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5]
    

    3、复杂度讨论

    (1)桶排序的表现取决于数据的分布,也就是需要对不同数据排序时采取不同的分桶策略
    (2)平均情况复杂度(O(n+k));最欢情况时间复杂度(O(n^2*k))
    (3)空间复杂度:O(k)

    四、基数排序(Radix Sort)

    1、基本思路

    (1)多关键字排序:一个员工表,要求按照年龄进行排序,年龄相同的员工按照薪资进行排序
    (2)对32,13,94,52,17,54,93进行排序,个位数和十位数

    2、代码实现

    def radix_sort(li):
        max_num = max(li)   # 最大值 9->1, 99->2, 888->3, 10000->5
        it = 0
        while 10**it <= max_num:   # 从低位到高位依次进行比较
            buckets = [[] for _ in range(10)]  # 分桶
            for var in li:
                # 当it=1时, 987//10 =98 ; 98 % 10 = 8
                # 当it=2时,987//100=9;   9 % 10=9
                digit = (var//10**it)%10
                buckets[digit].append(var)
            # 分桶完成
            li.clear()
            for buc in buckets:
                li.extend(buc)
            # 把数重新写回li
            it += 1
        return li
    
    import random
    li = [random.randint(0,5) for i in range(20)]
    print(li)
    li = radix_sort(li)
    print(li)
    >>[0, 2, 1, 4, 4, 4, 4, 5, 5, 5, 5, 1, 1, 3, 0, 5, 2, 1, 4, 1]
    >>[0, 0, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5]
    

    3、复杂度讨论

    (1)时间复杂度:O(kn)
    (2)空间复杂度:O(k+n)
    (3)k表示数字位数

    五、例题实战

    1、给两个字符串s和t,判断t是否是s重新排列之后组成的单词

    s = "anagram", t = "nagaram", return true
    s = "rat", t = "car", return false
    代码1:

    def isAnagram(s, t):
        return sorted(list(s)) == sorted(list(t))
    print(isAnagram("car", "tar"))
    print(isAnagram("arrand", "raarnd"))
    >>False
    >>True
    

    代码2:

    def isAnagram(s, t):
        dict1 = {}   # {'a':1, 'b':2}
        dict2 = {}  
        for ch in s:
            dict1[ch] = dict1.get(ch, 0) + 1
        for ch in t:
            dict2[ch] = dict2.get(ch, 0) + 1
        return dict1 == dict2
    print(isAnagram("car", "tar"))
    print(isAnagram("arrand", "raarnd"))
    >> True
    

    2、给定一个m*n的二维列表,查找一个元素是否都存在

    每一行的列表从左到右已经排序好
    每一行第一个数比上一行最后一个数大
    方法1:

    def search(matrix, target):
        for line in matrix:
            print(line)
            if target in line:
                return True
        return False
    matrix = [
        [1,2,3],
        [5,7,8],
        [10,12,14]]
    print(search(matrix, 5))
    >>[1, 2, 3]
    >>[5, 7, 8]
    >>True
    

    方法2:有序列表的二分查找

    def search(matrix, target):
        h = len(matrix)     # 行数
        w = len(matrix[0])  # 列数
        left = 0
        right = w*h - 1
        while left <= right:
            mid = (left + right)//2
            '''
            [
            [0, 1, 2,  3],
            [4, 5, 6,  7],    5-->(1, 1)
            [8, 9, 10 ,11]]
            '''
            i = mid // w     # 元素的行下标
            j = mid % w      # 元素的列下标
            if matrix[i][j] == target:
                return True
            elif matrix[i][j] > target:
                right = mid - 1
            else:
                left = mid + 1
        else:
            return False
    
    matrix = [
        [1,2,3],
        [5,7,8],
        [10,12,14]]
    print(search(matrix, 5))
    >> True
    

    3、给定一个列表和一个整数,设计算法找到两个数的下标,使得两个数的和为给定的整数。保证肯定仅有一个结果

    例如,列表[1,2,5,4]与目标整数3,1+2=3,结果为(0,1)
    方法1:

    def search(li, target):
        for i in range(len(li)-1):
            for j in range(i+1, len(li)):
                if li[i] + li[j] == target:
                    return (i,j)
    
    li = [1,2,5,4]
    print(search(li, 3))
    >>(0, 1)
    

    方法2:当列表是有序的

    def binary_search(li, left, right, val):
        while left <= right:
            mid = (left+right)//2
            if li[mid] == val:
                return mid
            elif li[mid] > val:
                right = mid-1
            else:
                left = mid+1
        else:
            return None
    
    def search(nums, target):
        for i in range(len(nums)):
            a = nums[i]
            b = target - a
            if b >= a:
                j = binary_search(nums, i+1, len(nums)-1, b)
            else:
                j = binary_search(nums, 0, i-1, b)
            if j:
                break
        return (i+1, j+1)
    
    nums = [1,2,3,4,5,8,10]
    print(search(nums, 9))
    >> (1, 6)
    

    方法3:当列表是无序的,先进行排序之后再二分查找

    def binary_search(li, left, right, val):
        while left <= right:
            mid = (left+right)//2
            if li[mid][0] == val:
                return mid
            elif li[mid][0] > val:
                right = mid-1
            else:
                left = mid+1
        else:
            return None
    
    def search(nums, target):
        new_nums = [[num, i] for i, num in enumerate(nums)]  # 将nums的下标和数放在同一个列表中
        new_nums.sort(key=lambda x: x[0])   # 按照值进行排序
        for i in range(len(new_nums)):
            a = new_nums[i][0]
            b = target - a
            if b >= a:
                j = binary_search(new_nums, i+1, len(new_nums)-1, b)
            else:
                j = binary_search(new_nums, 0, i-1, b)
            if j:
                break
        return sorted([new_nums[i][1], new_nums[j][1]])
    
    nums = [1,2,3,4,12,14,5,8,10]
    print(search(nums, 9))
    >> [0, 7]
    

    几种排序方法的比较

    相关文章

      网友评论

          本文标题:算法小结(三):列表排序2

          本文链接:https://www.haomeiwen.com/subject/vmmllltx.html