算法从简单开始 - 排序算法

排序算法

说起排序算法,估计所有小伙伴参加面试都或多或少接触过。我们先来了解几个概念。

  • 时间复杂度:算法完成排序的总的操作次数;
  • 空间复杂度:算法在运行时所需存储空间大小;
  • 稳定的算法:一个序列中,如果 a 原本在 b 前面,且 a = b,排序之后在新的有序序列中 a 仍然在 b 的前面。
  • 不稳定的算法:一个序列中,如果 a 原本在b的前面,且 a = b,排序之后在新的有序序列中 a 可能会出现在 b 的后面。

排序算法对比

排序方法时间复杂度(平均)时间复杂度(最差)时间复杂度(最好)空间复杂度稳定性
冒泡排序O(n2)O(n2)O(n)O(1)稳定
插入排序O(n2)O(n2)O(n)O(1)稳定
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定
选择排序O(n2)O(n2)O(n2)O(1)不稳定
快速排序O(nlog2n)O(n2)O(nlog2n)O(nlog2n)不稳定
希尔排序O(n1.3)O(n2)O(n)O(1)不稳定
堆排序O(nlog2n)O(nlog2n) )O(nlog2n)O(1)不稳定
计数排序O(n+k)O(n+k)O(n+k)O(n+k)稳定
桶排序O(n+k)O(n2) )O(n)O(n+k)稳定
基数排序O(n*k)O(n*k) )O(n*k)O(n+k)稳定

排序算法的文章很多,这里给大家推荐个链接:"如果天空不死"的博客

简单介绍五种排序方法

冒泡排序

无序序列 Rn 中排序一趟只确认一个排序位置,最多经过 n 趟排序。
冒泡排序在一趟中每次比较两个相邻的元素,如果不符合排序要求,则交换两个元素,直到一趟排序结束。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

# 冒泡排序
# 递增排序
def bubble_inc(arr: list):
    print("source array = " + str(arr))
    arr_len = len(arr)
    if arr_len == 0:
        return
    for i in range(0, arr_len):
        for j in range(0, arr_len - i - 1):
            if arr[j] > arr[j + 1]:
                swap(arr, j, j + 1)
    print("bubble increase array = " + str(arr))

# 冒泡排序
# 递排序
def bubble_dec(arr: list):
    print("source array = " + str(arr))
    arr_len = len(arr)
    if arr_len == 0:
        return
    for i in range(0, arr_len):
        for j in range(0, arr_len - i - 1):
            if arr[j] < arr[j + 1]:
                swap(arr, j, j + 1)
    print("bubble decrease array = " + str(arr))

array = [3, 2, 6, 1, 4]

bubble_inc(array)
bubble_dec(array)

运行结果

source array = [2, 1, 6, 4, 3]
bubble increase array = [1, 2, 3, 4, 6]
source array = [1, 2, 3, 4, 6]
bubble decrease array = [6, 4, 3, 2, 1]

插入排序

简单来说,插入排序就是将未排序的元素插入到已排序的序列中。

序列 Rn,1…k 的元素有序,k+1…n 的元素无序,将 k+1 位置的元素插入到前 k 个有序序列中,直到所有无序元素排序完成。
在这里插入图片描述

# 插入排序
# 递增序列
def insert_inc(arr: list):
    print("source array = " + str(arr))
    arr_len = len(arr)
    for i in range(1, arr_len):
        pre_idx = i - 1
        tmp = arr[i]
        while pre_idx >= 0 and arr[pre_idx] > tmp:
            arr[pre_idx + 1] = arr[pre_idx]
            pre_idx -= 1
        arr[pre_idx + 1] = tmp
    print("insert increase array = " + str(arr))

# 插入排序
# 递减序列
def insert_dec(arr: list):
    print("source array = " + str(arr))
    arr_len = len(arr)
    for i in range(1, arr_len):
        pre_idx = i - 1
        tmp = arr[i]
        while pre_idx >= 0 and arr[pre_idx] < tmp:
            arr[pre_idx + 1] = arr[pre_idx]
            pre_idx -= 1
        arr[pre_idx + 1] = tmp
    print("insert decrease array = " + str(arr))

array = [3, 2, 6, 1, 4]

insert_inc(array)
insert_dec(array)

运行结果

source array = [3, 2, 6, 1, 4]
insert increase array = [1, 2, 3, 4, 6]
source array = [1, 2, 3, 4, 6]
insert decrease array = [6, 4, 3, 2, 1]

归并排序

归并排序采用的算法思想是“分治”,将一个无序序列分为 2 个子序列,再将子序列分别采用归并排序;最后合并两个有序的子序列 。

  • 长度为 n 的序列,分割为长度为 n/2 的两个子序列;
  • 对分割的两个子序列进行归并排序;
  • 将两个通过归并排序而有序的子序列合并成一个有序序列;
    在这里插入图片描述
def do_merge(left: list, right: list):
    result = []
    while len(left) > 0 and len(right) > 0:
        if left[0] <= right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
    while len(left) > 0:
        result.append(left.pop(0))
    while len(right) > 0:
        result.append(right.pop(0))
    return result

def merge(arr: list):
    arr_len = len(arr)
    if arr_len < 2:
        return arr
    mid_index = arr_len // 2
    left_arr = arr[0:mid_index]
    right_arr = arr[mid_index:]
    result = do_merge(merge(left_arr), merge(right_arr))
    return result
    
array = [3, 2, 6, 1, 4]

# 归并排序
# 递增序列
print("merge increase array = " + str(merge(array)))

运行结果

merge increase array = [1, 2, 3, 4, 6]

快速排序

快速排序也是采用“分治”的思想,通过选取无序序列的基数,分为小于基数的序列和不小于基数的序列;然后继续可以对两个子序列排序,从而得到最终的有序序列。
在这里插入图片描述
上述例子中,基数从序列位置 0 开始获取

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

def do_quick(arr: list, left: int, right: int):
    pivot = left
    start = pivot + 1
    index = start
    for i in range(start, right + 1):
        if arr[i] < arr[pivot]:
            swap(arr, i, index)
            index += 1
    swap(arr, pivot, index - 1)
    return index - 1

def quick(arr, left, right):
    if left < right:
        index = do_quick(arr, left, right)
        quick(arr, left, index - 1)
        quick(arr, index + 1, right)
    return arr


array = [3, 2, 6, 1, 4]
print("quick increase array = " + str(quick(array, 0, len(array) - 1)))

运行结果

quick increase array = [1, 2, 3, 4, 6]

基数排序

基数排序可以认为是分层递进排序,分层就是排序的优先层级,递进就是每次分层排序都是有效的。
下面的例子中无序序列中的数字先按个位大小排序,形成一个新的无序序列,再对新的无序序列按十位进行排序,依次直到所有位数的排序完成
在这里插入图片描述
按十位排序后,排序已经结束

def radix(arr: list, max_num: int):
    mod = 10
    dev = 1
    counter = [[] for i in range(0, 10)]
    for i in range(0, max_num):
        for j in range(0, len(arr)):
            idx = (arr[j] % mod) // dev
            if len(counter) <= idx:
                counter[idx] = []
            counter[idx].append(arr[j])
        pos = 0
        for j in range(0, len(counter)):
            while len(counter[j]) > 0:
                arr[pos] = counter[j].pop(0)
                pos += 1
        mod *= 10
        dev *= 10
    return arr

array = [23, 12, 36, 5, 89]
print("radix increase array = " + str(radix(array, 6)))

运行结果

radix increase array = [5, 12, 23, 36, 89]
到这里,介绍了五种常见的排序算法,其余的排序算法小伙伴们可以通过上述推荐的博文去加以了解。共勉,进步!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值