【算法】【python】排序算法:插排、快排、归并排序

排序算法

一、 插入排序(insertion sort)

1.1定义

在插入排序中,从前到后一次处理未排好序的元素,对于每个元素,我们将它与之前排好序的元素进行比骄傲,找到对应的位置后插入。

1.2 步骤

  1. 从第二个蒜素(第一个要被排序的新元素)开始,从后向前扫描之前的元素序列
  2. 如果当前扫描的元素大于新元素,将扫描元素移动到下一位
  3. 重复步骤2,知道找到一个小于或者等于新元素的位置
  4. 将新元素插入到该位置
  5. 对于之后的元素重复步骤1-4
import numpy as np

def insertion_sort(array):
    n=len(array)    
    for i in range(1,n):
        cur=array[i]
        j=i-1            
        while (j>=0)&(array[j]>cur) :    #向前扫描的条件
                array[j+1]=array[j]      #j位置的元素大则向后移动一格
                j-=1
        #退出循环时:当前j的位置<=cur,j的位置保留,j+1的位置插入cur
        array[j+1]=cur

1.3 复杂度分析

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

二、快排(quicksort)

2.1 定义

快排时一种 分治(Divide and conquer)算法,在这章算法中,我们把大问题变成小问题,然后将小问题逐个解决,当小问题解决时,大问题也解决了。

2.2 步骤

  1. 对于当前的数组,任意取一个元素当作 基准数(pivot)
  2. 将所有比基准数小的蒜素排到基准数之前,比基准数大的排在基准数之后
  3. 当基准数被放到准确的位置后,根据基准数的位置将元素切分为前后两个子数组
  4. 对子数组采用步骤1-4的递归操作,知道子数组的长度小于等于1为止
#快速排序#在这里选择第一个元素
def quicksort(array):
    if len(array)<2:        #基线条件,数组为空或1个元素
        return array
    else :
        pivot=array[0]      #选择数组第一个元素作为“基准值”
        less=[i for i in array[1:] if i<=pivot]   #无序分组,小于等于基准值
        greater=[i for i in array[1:] if i>pivot] #无序分组,大于基准值
        return quicksort(less)+[pivot]+quicksort(greater)# [pivot]才是列表形式

2.3算法复杂度

  • 时间复杂度

最差情况的复杂度: O ( n 2 ) O(n^2) O(n2)

最优情况的复杂度: O ( l o g n ) O(log n) O(logn)

平均情况的复杂度: O ( n l o g n ) O(nlog n) O(nlogn)

  1. 由于排序算法不自己检查排序情况,会出现极端情况,每次分割的两个数组都为:空数组+n-1数组;因此需要调用n-1次,栈的高度为n。栈每层涉及的元素为n个,因此再最糟糕情况下时 O ( n 2 ) O(n^2) O(n2)

  2. 而在最优的情况下,每次都平均(几乎)分割,则栈的高度为 l o g n log n logn,则此时最优复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

  3. 如果每次都随机的选择一个数组元素作为基准值,则平均时间就为 O ( log ⁡ n ) O(\log n) O(logn)

  • 空间复杂度

最差情况的复杂度: O ( n ) O(n) O(n)

平均情况的复杂度: O ( log ⁡ N ) O(\log N) O(logN)

三、归并排序(merge sort)

3.1 定义

在此算法中,我们将一个数组分为两个子数组,通过递归重复将数组切分到只剩下一个元素为止。然后将每个子数组中的元素排序后合并,通过不断合并子数组,最后就会拿到一个排序好的大数组。

3.2步骤

  1. 递归切分当前数组
  2. 如果当前数组数量小于等于1,无需排序,直接返回结果
  3. 否则将当前数组分为2个子数组,递归排序这两个子数组
  4. 在子数组排序结束后,将子数组的结果归并成排好序的数组
# mergesort合并排序
def merge_sort(array):
    if len(array)==1:
        return array
    else:#分两个数组 并 递归排序
        #中间切分两个数组
        mid=len(array)//2   #整除
        left=array[:mid]
        right=array[mid:]
        #使用递归排序
        return merge(merge_sort(left),merge_sort(right))
    
def merge(left,right):
    result=[]
    while (len(left)>0) and (len(right)>0): #取数据知道两个列表中一个为空为止
        #左右两侧的数列都是按照从小到大排序的
        #比较最前面的数据,最小者将数据移除当前列表并放入result列表中
        if left[0]<=right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
   #其中一个列表为空,一个列表还有按顺序的元素
    result+=left
    result+=right
    return result

这里记录一下,python有一个模块,专门提供了归并排序的方法,叫做“heapq”模块,因此我们只要将分解后的结果导入该方法即可

from heapq import merge

def merge_sort(arr):
    if len(arr)<=1:
        return arr                  # 从递归中返回长度为1的序列
    middle=len(arr)//2
    left=merge_sort(arr[:middle])  # 通过不断递归,将原始序列拆分成n个小序列
    right=merge_sort(arr[middle:])
    return list(merge(left,right))

3.3 复杂度

时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)

空间复杂度: O ( n ) O(n) O(n);在合并子列时需要申请临时空间,而且空间大小随数列的大小而变化,所以空间复杂度为O(n)

稳定性:我们从代码中可以看到当左边的元素小于等于右边的元素就把左边的排前面,而原本左边的就是在前面,所以相同元素的相对顺序不变,故为稳定排序

记忆方法:所谓归并肯定是要先分解,再合并

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值