青训营X豆包MarsCode技术训练营——排序算法知识笔记 | 豆包MarsCode AI刷题

159 阅读3分钟

排序算法

直接插入排序

基本思想:

每次将待排序元素插入到已经排序过的序列的合适位置

平均时间复杂度为O(n^2)、空间复杂度为O(1)的解决方案

def insert_sort_2(array):
    n=len(array)

    for i in range(n):
        current_num = array[i]
        j=i-1

        while j>=0 and array[j]>current_num: #j>=0用于防止数组越界,要放在循环条件的前边
            array[j+1]=array[j]
            j-=1
        array[j+1]=current_num

    return array
        

冒泡排序

基本思想:

逐次比较并交换位置,每轮过后,将最大的元素放在数组末尾

平均时间复杂度O(n^2)、空间复杂度为O(1)的解决方案

#*****************************************************************两层循环实现*****************************************************************

def bubble_sort(array):
    n=len(array)
    for i in range(n):
        for j in range(n-i-1):
            if array[j]>array[j+1]:
                temp=array[j+1]
                array[j+1]=array[j]
                array[j]=temp
    return array
#*****************************************************************递归实现*****************************************************************
def bubble_sort_2(array,n):
    if n<=2:
        return array
    for i in range(n-1):
        if array[i]>array[i+1]:
            temp=array[i]
            array[i]=array[i+1]
            array[i+1]=temp
    bubble_sort_2(array,n-1)
    return array

选择排序

基本思想:

逐次比较,但并非每次都交换位置,每轮排序过后,该轮最小的元素与首个元素交换位置

平均时间复杂度O(n^2)、空间复杂度为O(1)的解决方案

#*****************************************************************基于循环*****************************************************************
def selection_sort(array,n):
   
    for i in range(n):
        
        min_index = i
        for j in range(i,n):
            if array[j]<array[min_index]:
                min_index = j
                
        temp = array[i]
        array[i]=array[min_index]
        array[min_index]=temp
    
    return array

#*****************************************************************基于递归*****************************************************************
def selection_sort_2(array,start):
    if start==len(array)-1:
        return array
    min_index = start
    for i in range(start,len(array)):
        if array[i]<array[min_index]:
            min_index = i
            
    temp = array[start]
    array[start]=array[min_index]
    array[min_index]=temp

    selection_sort_2(array,start+1)
    return array

快速排序

基本思想:

函数每次将序列array分为小于pivot的左子序列和大于pivot的右子序列,其中左子序列和右子序列使用递归的方式进行同样的处理

平均时间、空间复杂度为nlogn的解决方案

def qik_sort(array):
    if len(array)<=1:
        return array #递归返回的边界条件
    pivot=array[0]
    left=[num for num in array[1:] if num<=pivot]
    right=[num for num in array[1:] if num>=pivot]
    return quik_sort(left)+[pivot]+quik_sort(right) #递归分治

注意:

使用递归一定要注意递归的边界条件,否则递归函数无法退出回调。此处当传入函数的数组长度为1或者为0时候进行返回,因此条件是 len(array)<=1(left和right可能是空)

复杂度分析: 平均情况:递归深度为logn,每次开辟空间n,因此空间复杂度为nlogn,每次循环2n,时间复杂度为nlogn 最差情况:递归深度为n,空间退化为n^2,时间退化为n^2

平均时间复杂度为O(nlogn),空间复杂度为O(n)的双指针解决方案

def quik_sork(array,left,right):
    if left>=right:
        return array  #递归的边界停止条件
    pivot = array[0]
    i = left
    j = right
    while(i<j):
        while(array[j]>=pivot and j>i):#一定要先从右边开始找
            j-=1
        while(array[i]<=pivot and j>i):
            i+=1
        if i<j:
            temp=array[i]
            array[j]=array[i]
            array[i]=temp
    temp=array[i]
    array[i]=pivot
    array[left]=temp
    
    quik_array(array,left,i-1)
    quik_array(array,i+1,right)#递归处理
    
    return array
        

注意:

递归的边界条件是left>=right,这里加上>避免递归无法退出陷入死循环

为何要先从右边开始找?这是因为双指针的while循环在满足两个条件中的一个的时候停止,即找到符合大小关系的数或者或者指针相遇,在指针相遇的情况下我们要与pivot进行交换,此时当然是希望找到的数<pivot,因此从右边开始找。此外循环条件加上=,是为了避免死循环

复杂度分析: 平均情况:递归深度为logn,每次循环n,时间复杂度为nlogn,原位操作,因此空间复杂度为n 最差情况:递归深度为n,时间退化为n^2,空间退化为n^2