数据结构与算法(5):排序算法

排序算法

1. 简介

排序是将集合中的元素按照某种顺序排列的过程。许多查找算法的前提就是排序,排序也是计算机科学中一个重要的研究领域。排序过程通常需要将元素位置进行交换,交换在计算机中是比较耗时的操作,因此排序算法总的交换次数也作为衡量排序算法效率的重要指标。

本文主要介绍冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序。

2. 冒泡排序

冒泡排序需要多次遍历列表,每一次遍历将较大的元素沉底。

假设列表有 n n n个元素,那么第一次遍历需要比较 ( n − 1 ) (n-1) (n1)对相邻值大小,第二次需要遍历 ( n − 2 ) (n-2) (n2)对相邻值大小,最后一次需要遍历比较1对相邻值大小(其中第一次遍历流程如下图所示,之后的遍历流程类似),因此算法总的需要比较次数为 1 + 2 + . . . + ( n − 1 ) 1+2+...+(n-1) 1+2+...+(n1) = 1 / 2 n 1/2n 1/2n2- 1 / 2 n 1/2n 1/2n,算法时间复杂度为 O ( n O(n O(n2 ) ) )
在这里插入图片描述

#冒泡排序python实现

def bubbleSort(numlist):
    for j in range(len(numlist)-1):#遍历次数
        i = 0
        while i+1 < len(numlist)-j:#比较次数
            if numlist[i] > numlist[i+1]:
                numlist[i],numlist[i+1] = numlist[i+1],numlist[i]#交换
            i += 1
    return numlist

3. 选择排序

选择排序是每次遍历选出未排序元素中的最大元素,然后将该最大元素沉底。与冒泡排序相比,选择排序遍历列表次数一样,但交换元素次数减少,每次遍历只进行一次元素交换或不交换。

假设列表有 n n n个元素,那么第一次遍历需要比较 ( n − 1 ) (n-1) (n1)对元素的大小,定位最大值后,将最大元素交换至列表末尾(沉底完成),第二次遍历比较 ( n − 2 ) (n-2) (n2)对元素的大小(沉底完成的元素无须再比较),定位未排序元素最大值后,将该最大元素交换至未排序列表末尾,以此类推,同样可得到算法时间复杂度为 O ( n O(n O(n2 ) ) )选择排序第一次遍历如下图
在这里插入图片描述

#选择排序python实现

def selectSort(numlist):
    for j in range(len(numlist)):
        i = 1
        maxindex = 0
        while i < len(numlist)-j:#定位
            if numlist[maxindex] < numlist[i]:
                maxindex = i
            i += 1
        numlist[maxindex],numlist[len(numlist)-j-1] = numlist[len(numlist)-j-1],numlist[maxindex]#交换
    return numlist 

4. 插入排序

插入排序是按顺序依次遍历列表,但每遍历一个元素,便将该元素按顺序大小插入到以遍历元素构成的子列表中。该算法只需遍历一次列表,但是将当前元素插入到子列表中需要对子列表元素进行比较对比。

假设列表有 n n n个元素,那么最好情况下空间复杂度为 O ( n ) O(n) O(n),最坏情况复杂度为 O ( n O(n O(n2 ) ) ),平均复杂度为 O ( n O(n O(n2 ) ) )。算法完整流程如下图。
在这里插入图片描述

#插入排序python实现

def insertSort(numlist):
    for i in range(1,len(numlist)):#从第二个元素开始遍历
        num = numlist[i]
        j = i
        
        while (j>0) & (numlist[j-1]>num):#依次往后移动较大元素
            numlist[j] = numlist[j-1]
            j -= 1
            
        numlist[j] = num 
    return numlist

5. 希尔排序

希尔排序,又称缩小增量排序,是在插入排序的基础上进行改进的排序算法。与插入排序相比,希尔排序通过将列表切分为 k k k个子列表,并对 k k k个子列表进行插入排序操作,过程中需要不断调整子列表数量,最终达到排序效果。如何切分列表是希尔排序的关键,切分的大小会影响算法的复杂度;列表通常不是连续切分,而是根据增量(步长)选择元素构成子列表。

假设列表有 n n n个元素,那么最好情况下空间复杂度为 O ( n ) O(n) O(n),最坏情况复杂度为 O ( n O(n O(n2 ) ) ),平均复杂度为 O ( n O(n O(n3/2 ) ) )。算法完整流程如下图。
在这里插入图片描述

希尔排序python实现

def shellSort(numlist):
    steplength = len(numlist) // 2
    while steplength >= 1:
        for step in range(steplength):
            for i in range(step,len(numlist),steplength):#切分值列表,并插入排序
                num = numlist[i]
                j = i
                
                while (j>=steplength) & (numlist[j-steplength]>num):
                    numlist[j] = numlist[j-steplength]
                    j -= steplength
                    
                numlist[j] = num    
        steplength = steplength//2
    return numlist

6. 归并排序

归并排序是一种递归算法,首先将 n n n个元素的序列拆分为 n / 2 n/2 n/2个子序列,然后对子序列执行归并排序(递归调用),最后将两个排序完成的子序列合并成最终序列。该递归算法的基本情况是:当列表为空或者列表只有一个元素时,认为列表是有序的。

假设列表有 n n n个元素,算法首先进行拆分,再进行归并,拆分次数为 l o g log log2 n n n,每一次拆分需处理的次数为 n n n,因此归并次数平均复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。算法完整流程如下图。
在这里插入图片描述

归并排序python实现

def mergeSort(numlist):
    print(numlist)
    if len(numlist)>1:
        mid = len(numlist) // 2
        left = numlist[:mid]
        right = numlist[mid:]
        
        mergeSort(left)#递归
        mergeSort(right)#递归
        
        i,j,k = 0,0,0#初始化,i指向左子列表,j指向右子列表,k指向左右两个子列表归并的列表
        while (i < len(left)) & (j < len(right)):
            if left[i]<right[j]:#比较并左右列表元素大小
                numlist[k] = left[i]
                i += 1
            else:
                numlist[k] = right[j]
                j += 1
            k += 1
        
        if i < len(left):
            numlist[k:len(left)+len(right)] = left[i:len(left)].copy()
            
        if j < len(right):
            numlist[k:len(left)+len(right)] = right[j:len(right)].copy()
            
    return numlist

7. 快速排序

快速排序也是一种递归算法,算法首先选出一个基准值,然后根据基准值将有序数列拆分成左子序列(小于基准值)和右子序列(大于基准值),然后分别对子序列执行快速排序(递归调用)。每执行一次算法,选定的基准值便会确定其位置。

基准值的选择会影响算法切分的次数,当选择的基准值都是序列的中间值,那么只需要切分 l o g n logn logn次,此时与归并算法切割和操作此时一样,算法复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),而极端情况下,每次基准值左/右子序列均为空,那么此时算法复杂度为 O ( n O(n O(n2 ) ) )。为了使切分更趋近前者,选择基准值可以采用三数取中法,即选择开头、中间和末尾元素的中间值。
在这里插入图片描述

快速排序python实现

def quickSort(numlist):
    return subquickSort(numlist, 0, len(numlist)-1)
    
def subquickSort(numlist, first, last):
    if (len(numlist)>1) & (first<last):
        basevalue = numlist[first]#默认基准值为第一个元素
        
        leftpointer = first+1
        rightpointer = last
        
        while True:
            
            while (leftpointer <= rightpointer) and (numlist[leftpointer]<=basevalue):#左指针移动
                leftpointer += 1
                
            while (leftpointer <= rightpointer) and (numlist[rightpointer]>=basevalue):#右指针移动
                rightpointer -= 1
                
            if leftpointer > rightpointer:#判断子序列是否遍历
                break
            else:
                numlist[leftpointer],numlist[rightpointer] = numlist[rightpointer],numlist[leftpointer]#交换
        
        numlist[first],numlist[rightpointer] = numlist[rightpointer],numlist[first]#确定基准值位置
        subquickSort(numlist, first, rightpointer-1)#递归调用
        subquickSort(numlist, rightpointer+1, last)
    
    return numlist
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值