【技术分享】十大排序算法篇
2022-09-30 / 0 评论 / 478 阅读 / 52 点赞

【技术分享】十大排序算法篇

发光的神
2022-09-30 / 0 评论 / 478 阅读 / 正在检测是否收录...

排序算法分类

非线性时间比较类排序:
比较来绝定元素间的相对次序,时间复杂度不能突破O(nlogn)。
所以称非线性时间比较类排序。

线性时间非比较类排序:
不通过比较来决定元素间的相对次序。
可以突破基于比较排序的时间下界以线性时间运行,
所以称为线性时间非比较类排序。

排序算法概述

常见的有 快速排序、归并排序、堆排序以及冒泡排序 都属于比较类排序算法。
比较类排序是通过比较来决定元素间的相对次序,时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
在冒泡排序之类的排序中,问题规模为 n,又因为需要比较 n 次,所以平均时间复杂度为 O(n²)。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为 logn 次,所以时间复杂度平均 O(nlogn)。
比较类排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。

计数排序、基数排序、桶排序 则属于非比较类排序算法。
非比较排序不通过比较来决定元素间的相对次序,而是通过确定每个元素之前,应该有多少个元素来排序。
由于它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度 O(n)。

l8pgz2x4.png

冒泡排序 (Bubble Sort)

冒泡排序是一种简单的排序算法

算法原理:
重复地遍历待排序的序列,依次比较两个元素,
如果它们的顺序错误就把它们交换过来。
遍历序列的工作是重复地进行直到没有再需要交换为止,
此时说明该序列已经排序完成。

l8phajhq.png

# 第一种
def Bubble_sort(nums):
    for j in range(len(nums)-1,0,-1):
        for i in range(j):
            if nums[i] > nums[i + 1]:
                nums[i], nums[i+1] = nums[i +1], nums[i]
array = [5,9,1,3,0,7,6]
Bubble_sort(array)
print(array)

# 第二种
array = [3,2,5,6,4,8]
for i in range(0, len(array)-1):
    for j in range(0, len(array)-1):
        if array[j] > array[j+1]:
            array[j], array[j+1] = array[j+1], array[j]
print(array)

# 第三种
array = [3,2,5,6,4,8]
res = [
    array.pop(array.index(min(array))) 
    for i in range(len(array))
]
print(res)

快速排序(Quick Sort)

算法原理:
快速排序使用分治法(Divide and conquer)策略来把一个序列分为较小和较大的 2 个子序列,然后递回地排序两个子序列。

l8phvvla.png

def partition(li,left,right):  
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:  #从右边找比tmp小的数
            right -= 1   #继续从右往左查找
        li[left] = li[right]  #把右边的值写到左边空位上
        
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left] #把左边的值写到右边空位上
      
    li[left] = tmp #把tmp归位
    return  left
def quick_sort(li,left,right):  
    if left < right :#至少两个元素
        mid = partition(li,left,right)
        quick_sort(li,left,mid-1)
        quick_sort(li,mid+1,right)
        
li = [5,7,4,6,3,1,2,9,8]
quick_sort(li,0,len(li)-1)
print(li)

选择排序(Selection Sort)

算法原理:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第 2 步,直到所有元素均排序完毕。

l8re1aj9.png

def selection_sort(num_list):
    length = len(num_list)
    if length <= 1:
        return num_list
    for j in range(length):
        # 假设第一个元素为最小元素
        min_num_index = j
        # 遍历未排序区域元素,以此和未排序区域的第一个元素做对比
        for i in range(j+1, length):
            if num_list[i] < num_list[min_num_index]:
                min_num_index = i
        # 交换位置
        num_list[min_num_index], num_list[j] = num_list[j], num_list[min_num_index]
    return num_list

array = [1, 3, 2, 6, 4, 12, 33, 5, 25]
print(selection_sort(array))

插入排序(Insertion Sort)

算法原理:
从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中从后向前扫描;
如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后;
重复步骤 2~5。

l8redvj0.png

def insert_sort(tg):
    for i in range(1, len(tg)):
        for j in range(i, 0, -1):
            if tg[j] < tg[j-1]:
                tg[j-1], tg[j] = tg[j], tg[j-1]
            else:
                break
array = [8,7,5,4,6,3,1]
insert_sort(array)
print(array)

归并排序(Merge Sort)

算法原理:
如果输入内只有一个元素,则直接返回,否则将长度为 n 的输入序列分成两个长度为 n/2 的子列;
分别对这两个子序列进行归并排序,使子序列变为有序状态;
设定两个指针,分别指向两个已经排序子序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间(用于存放排序结果),并移动指针到下一位置;
重复步骤 3 ~4 直到某一指针达到序列尾;
将另一序列剩下的所有元素直接复制到合并序列尾

l8regr6i.png

def merge(left, right):
    # 合并两个有序列表
    res = []
    while len(left) > 0 and len(right) > 0:
        if left[0] < right[0]:
            res.append(left.pop(0))
        else:
            res.append(right.pop(0))
    if left:
        res.extend(left)
    if right:
        res.extend(right)
    return res

def mergeSort(arr):
    # 归并函数
    n = len(arr)
    if n < 2:
        return arr
    middle = n // 2
    left = arr[:middle] # 取序列左边部分
    right = arr[middle:]# 取序列右边部分
    # 对左边部分序列递归调用归并函数
    left_sort = mergeSort(left) 
    # 对右边部分序列递归调用归并函数
    right_sort = mergeSort(right)
    # 
    return merge(left_sort, right_sort)

array = [8,7,5,4,6,3,1]
print(mergeSort(array))

计数排序(Counting Sort)

算法原理:
找出数组中的最大值 max、最小值 min;
创建一个新数组 C,其长度是 max-min+1,其元素默认值都为 0;
遍历原数组 A 中的元素 A[i],以 A[i]-min 作为 C 数组的索引,以 A[i] 的值在 A 中元素出
现次数作为 C[A[i]-min] 的值;
对 C 数组变形,新元素的值是该元素与前一个元素值的和,即当 i>1 时 C[i] = C[i] + C[i-1];
创建结果数组 R,长度和原始数组一样。
从后向前遍历原始数组 A 中的元素 A[i],使用 A[i] 减去最小值 min 作为索引,在计数数组
C 中找到对应的值 C[A[i]-min],C[A[i]-min]-1 就是 A[i] 在结果数组 R 中的位置,做完上
述这些操作,将 count[A[i]-min] 减小 1。

l8reryg2.png

def count_sort(nums):
    # 最大值-最小值+1的数组,初始值为0
    bucket = [0] * (max(nums) - min(nums) + 1)
    # 统计原数组中每个元素出现的个数,存储在新开辟的数组中
    for num in nums:
        bucket[num - min(nums)] += 1
    # nums的下标
    i = 0
    # 根据每个元素出现的次数,按照新开辟数组的元素从小到大依次填充到原来的数组中
    for j in range(len(bucket)):
        while bucket[j] > 0:
            nums[i] = j + min(nums)
            bucket[j] -= 1
            i += 1
    return nums

array = [8,7,5,4,6,3,1]
print(count_sort(array))

桶排序(Bucket Sort)

设置一个 BucketSize,作为每个桶所能放置多少个不同数值;
遍历输入数据,并且把数据依次映射到对应的桶里去;
对每个非空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;
从非空桶里把排好序的数据拼接起来。

def bucketSort(nums):
    # 选择一个最大的数
    max_num = max(nums)
    # 创建一个元素全是0的列表, 当做桶
    bucket = [0] * (max_num + 1)
    # 把所有元素放入桶中, 即把对应元素个数加一
    for i in nums:
        bucket[i] += 1
    # 存储排序好的元素
    sort_nums = []
    # 取出桶中的元素
    for j in range(len(bucket)):
        if bucket[j] != 0:
            for y in range(bucket[j]):
                sort_nums.append(j)
    return sort_nums

array = [8,7,5,4,6,3,1]
print(bucketSort(array))

基数排序(Radix Sort)

算法原理:
取得数组中的最大数,并取得位数,即为迭代次数 N(例如:数组中最大数值为 1000,则 N=4);
A 为原始数组,从最低位开始取每个位组成 radix 数组;
对 radix 进行计数排序(利用计数排序适用于小范围数的特点);
将 radix 依次赋值给原数组;
重复 2~4 步骤 N 次

l8rf14dw.png

def radix_sort(s):
    i = 0 # 记录当前正在排拿一位,最低位为1
    max_num = max(s)  # 最大值
    j = len(str(max_num))  # 记录最大值的位数
    while i < j:
        bucket_list =[[] for _ in range(10)] #初始化桶数组
        for x in s:
            bucket_list[int(x / (10**i)) % 10].append(x) # 找到位置放入桶数组
        s.clear()
        for x in bucket_list:   # 放回原序列
            for y in x:
                s.append(y)
        i += 1

array = [8,7,5,4,6,3,1]
radix_sort(array)
print(array)

希尔排序(Shell Sort)

算法原理:
选择一个增量序列 {t1, t2, …, tk},其中 (ti>tj, i<j, tk=1);
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 t,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行
直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

def ShellSort(nums):
    step = len(nums)//2 #初始化增量为数组长度的一半
    while step > 0: #增量必须是大于0的整数
        for i in range(step,len(nums)): #遍历需要进行插入排序的数
            ind = i
            while ind >= step and nums[ind] < nums[ind-step]: #对每组进行插入排序
                nums[ind],nums[ind-step] = nums[ind-step],nums[ind]
                ind -= step
        step //= 2 #增量缩小一半
    return nums

array = [8,7,5,4,6,3,1]
ShellSort(array)
print(array)

堆排序(Heapsort)

创建一个堆 H[0……n-1];
把堆首(最大值)和堆尾互换;
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
重复步骤 2,直到堆的尺寸为 1。

import math

def buildMaxHeap(arr):
    for i in range(math.floor(len(arr)/2),-1,-1):
        heapify(arr,i)

def heapify(arr, i):
    left = 2*i+1
    right = 2*i+2
    largest = i
    if left < arrLen and arr[left] > arr[largest]:
        largest = left
    if right < arrLen and arr[right] > arr[largest]:
        largest = right

    if largest != i:
        swap(arr, i, largest)
        heapify(arr, largest)

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]

def heapSort(arr):
    global arrLen
    arrLen = len(arr)
    buildMaxHeap(arr)
    for i in range(len(arr)-1,0,-1):
        swap(arr,0,i)
        arrLen -=1
        heapify(arr, 0)
    return arr
  
array = [8,7,5,4,6,3,1]
print(heapSort(array))

以下排序不在内

TimSort

def binary_search(lst, item, start, end):
    if start == end:
        return start if lst[start] > item else start + 1
    if start > end:
        return start

    mid = (start + end) // 2
    if lst[mid] < item:
        return binary_search(lst, item, mid + 1, end)
    elif lst[mid] > item:
        return binary_search(lst, item, start, mid - 1)
    else:
        return mid


def insertion_sort(lst):
    length = len(lst)

    for index in range(1, length):
        value = lst[index]
        pos = binary_search(lst, value, 0, index - 1)
        lst = lst[:pos] + [value] + lst[pos:index] + lst[index + 1 :]

    return lst


def merge(left, right):
    if not left:
        return right

    if not right:
        return left

    if left[0] < right[0]:
        return [left[0]] + merge(left[1:], right)

    return [right[0]] + merge(left, right[1:])


def tim_sort(lst):
    """
    >>> tim_sort("Python")
    ['P', 'h', 'n', 'o', 't', 'y']
    >>> tim_sort((1.1, 1, 0, -1, -1.1))
    [-1.1, -1, 0, 1, 1.1]
    >>> tim_sort(list(reversed(list(range(7)))))
    [0, 1, 2, 3, 4, 5, 6]
    >>> tim_sort([3, 2, 1]) == insertion_sort([3, 2, 1])
    True
    >>> tim_sort([3, 2, 1]) == sorted([3, 2, 1])
    True
    """
    length = len(lst)
    runs, sorted_runs = [], []
    new_run = [lst[0]]
    sorted_array = []
    i = 1
    while i < length:
        if lst[i] < lst[i - 1]:
            runs.append(new_run)
            new_run = [lst[i]]
        else:
            new_run.append(lst[i])
        i += 1
    runs.append(new_run)

    for run in runs:
        sorted_runs.append(insertion_sort(run))
    for run in sorted_runs:
        sorted_array = merge(sorted_array, run)

    return sorted_array


def main():
    lst = [5, 9, 10, 3, -4, 5, 178, 92, 46, -18, 0, 7]
    sorted_lst = tim_sort(lst)
    print(sorted_lst)


if __name__ == "__main__":
    main()

Stoogesort

def stooge_sort(arr):
    """
    Examples:
    >>> stooge_sort([18.1, 0, -7.1, -1, 2, 2])
    [-7.1, -1, 0, 2, 2, 18.1]

    >>> stooge_sort([])
    []
    """
    stooge(arr, 0, len(arr) - 1)
    return arr


def stooge(arr, i, h):

    if i >= h:
        return

    # If first element is smaller than the last then swap them
    if arr[i] > arr[h]:
        arr[i], arr[h] = arr[h], arr[i]

    # If there are more than 2 elements in the array
    if h - i + 1 > 2:
        t = (int)((h - i + 1) / 3)

        # Recursively sort first 2/3 elements
        stooge(arr, i, (h - t))

        # Recursively sort last 2/3 elements
        stooge(arr, i + t, (h))

        # Recursively sort first 2/3 elements
        stooge(arr, i, (h - t))


if __name__ == "__main__":
    user_input = input("Enter numbers separated by a comma:\n").strip()
    unsorted = [int(item) for item in user_input.split(",")]
    print(stooge_sort(unsorted))
52

评论 (0)

取消
0:00