数据结构之排序(9种)

概览

在这里插入图片描述

插入排序

直接插入排序

将数组分为两部分,前半部分有序,后半部分无序,依次从后面取数据,插入到前面指定位置。

def insertSort(nums):
    size = len(nums)

    for i in range(1, size):
        temp = nums[i] # 待排序的数据
        count = i-1  # 代表前面有count个数据
        while count >= 0 and nums[count] > temp:
            nums[count+1]= nums[count]
            count -= 1

        nums[count+1] = temp

    return nums**加粗样式**

插入排序:
时间复杂度:最好:O(n) 最差 :O(n^2)
空间复杂度:O(1)

折半插入

该方法和直接插入排序的方法有些类似,只是在搜索最佳插入位置的时候采用折半搜索,折半上搜索可以看另一篇文章:二分查找法技巧

def insert_sort_op(nums):
    size = len(nums)

    for i in range(1, size):
        start = 0
        end = i - 1
        target = nums[i]

        while start <= end:
            mid = start + (end - start) // 2
            if nums[mid] <= target:
                start = mid + 1
            else:
                end = mid - 1

        # 将 [end+1,i-1]位置的数据都往后移动一位
        for i in range(i - 1, end, -1):
            nums[i + 1] = nums[i]

        nums[end + 1] = target

    return nums

希尔排序(增量排序法)

将数据按照增量间隔分为不同的组,假如增量为k,则:
[0,k,2k,3k…]为一组,[1,k+1,2k+1,3k+1…]为一组,一共分为k组,然后分别对每一组排序。初始时,k的取值为(n/2,n/2/2…1).k每取一个值就按该分组排序一次。
图例:在这里插入图片描述

def shell_sort(nums):
    size = len(nums)

    step = size // 2

    while step > 0:  # 当step == 1时,如果step / 2 = 0.5,向下取证为0,跳出训话
        for i in range(step, size): # 每次从最后一组的低位开始排序即可
            temp = nums[i]
            index = i - step
            while index >= 0 and temp < nums[index]:
                nums[index + step] = nums[index]
                index = index - step
            nums[index + step] = temp
        step //= 2

    return nums

最佳情况:T(n) = O(nlog2 n)
最坏情况:T(n) = O(nlog2 n)
平均情况:T(n) =O(nlog2n)

交换排序

冒泡排序

这个是最常见的排序方法,有多种变体,下面写的是最快的一种。

def bubble_sort(nums):
size = len(nums)

step = size // 2

for i in range(size):
    flag = True
    for j in range(size - 2, i - 1, -1):
        if nums[j] > nums[j + 1]:
            nums[j], nums[j + 1] = nums[j + 1], nums[j]
            flag = False
    if flag:
        break
return nums

复杂度分析:
最佳情况:T(n) = O(n)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)

快速排序

基本快排(一路快排)

在这里插入图片描述
快排有个特点,每次能把一个数据放到他应该在的位置上。所以快排的三个指针中的其中一个就指在这个数据的初始位置上,另外还有两个 j 是分割点,i是遍历下标。

代码如下:

def get_partition_and_sort(nums, start, end):
    partition = start
    # for i in range(start + 1, end + 1):  # 由于使用的都是闭区间,所以要遍历到end
    #     nu = nums[i]
    #     if nums[i] < temp:
    #         # 如果
    #         nums[i], nums[partition] = nums[partition], nums[i]
    #         partition += 1
    #
    # nums[partition] = temp

    # 上面的代码是错误的
    # 因为partition如果指向的值是小于等于temp的值,如果在高位发现一个小于temp的值,则就把partition对应的值移到了高位

    for i in range(start + 1, end + 1):
        # 因为partition 指向一个小于等于temp的值,只有和partition交换后,才能保证partition+1后仍然指向小于等于 temp的值
        if nums < nums[start]: # nums[start]的值值直到最后才会改变,所以一直可以当参照值
            nums[i], nums[partition + 1] = nums[partition + 1], nums[i]
            partition += 1

    # 因为nums[partition]本来就小于temp,把它移到最左边
    nums[start], nums[partition] = nums[partition], nums[start]

    return partition

二路快排

二路快排的的主架构和一路快排一样:

def fast_sort(nums, start, end):
    if start < end:  # 这里要注意
        index = get_bin_sort_partition(nums, start, end)
        fast_sort(nums, start, index - 1)
        fast_sort(nums, index + 1, end)
    return nums

不同的是,二路快排使用两个指针:
在这里插入图片描述
代码如下:

def get_bin_sort_partition(nums, start, end):
    i = start + 1
    j = end

    # 每次循环结束 i(不包含i)的左边都是大于等于nums[start]的值
    # 每次循环结束 j(不包含j)的右边都是大于等于nums[start]的值
    while True:
        # 如果i 终止循环,则代表nums[i]的值大于nums[start]
        # 学会使用短路条件,防止数组越界
        while i <= end and nums[i] <= nums[start]:
            i += 1

        # 如果j 终止循环,则代表nums[j]的值小于nums[start]
        while j >= start + 1 and nums[j] >= nums[start]:
            j -= 1

        if i >= j:  # 碰头了就终止循环
            break

        # 交换后,左边大于等于nums[start],右边小于等于nums[start]
        nums[i], nums[j] = nums[j], nums[i]

        # while 循环在结束的时候都要对index修改,以便开启下次循环
        i += 1
        j -= 1

        # 这里一定要和j交换
        # 如果终止条件是j = i,则交换没问题
        # 如果终止条件是i > j,则i指向一个大于nums[start]的值,j指向一个小于nums[start]的值
        # 如果和 i 交换,则start位置就出现一个大于temp的值

    nums[start], nums[j] = nums[j], nums[start]
    return j

复杂度分析:
最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(nlogn)

选择排序

直接选择排序

从前往后,每次选择一个最小的,不再赘述

def select_sort(nums):
    size = len(nums)

    for i in range(size):
        mini_index = i

        for j in range(i, size):  # 必须从i以后(包括i)的值里面找到最小值
            if nums[j] < nums[mini_index]:
                mini_index = j
        nums[i], nums[mini_index] = nums[mini_index], nums[i]
    return nums

堆排序

归并排序

def merge(left, right):
    res = []
    size_left = len(left)
    size_right = len(right)
    left_index = 0
    right_index = 0

    while left_index < size_left and right_index < size_right:

        if left[left_index] < right[right_index]:
            res.append(left[left_index])
            left_index += 1
        else:
            res.append(right[right_index])
            right_index += 1

    if left_index == size_left:
        res.extend(right[right_index:])
    else:
        res.extend(left[left_index:])

    return res


def merge_sort(nums):
    size = len(nums)

    if size <= 1:
        return nums

    mid = size // 2

    left = merge_sort(nums[:mid])
    right = merge_sort(nums[mid:])
    return merge(left, right)


print(merge_sort([1, 2, 3, 2, 3, 1, 3, 4, 5, 2, 2, 2, 4]))

基数排序

发布了85 篇原创文章 · 获赞 21 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_37275456/article/details/104521178
今日推荐