排序算法---冒泡排序, 选择排序与插入排序 (python版)

     排序算法是面试时常考的基础知识,今天对三个基本排序算法进行总结。

---------------------------------------------------------------------------------------------------------------

首先写一个计算排序算法时间的装饰器以及3个辅助函数来帮助测试排序算法性能。

装饰器:

def timmer(func):  #func 为传进来的排序函数对象
    def warpper(arr, nums):  # arr:待排序数组, nums:数组长度
        start = time.time()
        r = func(arr, nums)
        end = time.time()
        print('toatl time is :', str(end - start))
        return r
    return warpper

生成无序数组或基本有序数组:

def random_list(num, start, end):  # 生成无序数组
    res = []
    for i in range(num):
        res.append(random.randint(start, end))
    return res

def list_near_order(nums, start):  #生成基本有序数组
    a = [i for i in range(start, start + nums)]
    for i in range(nums//10):
        ind1 = random.randint(0,nums)
        ind2 = random.randint(0, nums)
        a[ind1], a[ind2] = a[ind2], a[ind1]
    return a

检查排序是否正确:

def check_list(arr,ori_arr):   #arr:排序后的数组, ori_arr: 原始数组
    new_arr = sorted(ori_arr)
    if arr == new_arr:
        return True
    return False

------------------------------------------------------------------------------------------------------

 

一、冒泡排序

基本思想:比较相邻的两个数,如果前者比后者大,则进行交换。每一轮排序结束,选出一个未排序中最大的数放到数组后面

冒泡排序算法的运作如下:

  1. 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
def bubble_sort(arr):
    n = len(arr)
    for i in range(n-1):
        for j in range(1,n-i):
            if arr[j] < arr[j-1]:
                arr[j],arr[j-1] =  arr[j-1], arr[j]

特点总结

最差时间复杂度为O(n^2),平均时间复杂度为O(n^2)。稳定性:稳定。辅助空间O(1)。

算法改进

 n个元素比较n-1趟,第i趟比较n-i次

若在其中的某一趟排序中:若始终未发生元素的交换说明已经排序号好,函数结束!

def bubble_sort_pro(arr):
    n = len(arr)
    for i in range(n-1):
        flag = 0
        for j in range(1,n-i):
            if arr[j] < arr[j-1]:
                arr[j],arr[j-1] =  arr[j-1], arr[j]
                flag = 1
        if flag == 0:
            break

冒泡排序算法应该是我们学习的第一个排序算法,非常简单,但是时间复杂度高,基本不会选择排序算法来解决排序问题。

二、选择排序

基本思想:依次选出数组最小的数放到数组的前面。首先从数组的第一个元素开始往后遍历,找出最小的数放到第一个位置。再从剩下数组中找出最小的数放到第二个位置。以此类推,直到数组有序。

def slect_sort(arr, nums):   #arr:待排序数组, nums:数组长度
    for i in range(nums):
        minInd = i
        for j in range(i, nums):
            if arr[j] < arr[minInd]:
                minInd = j
        arr[i], arr[minInd] = arr[minInd], arr[i]

  算法特点: 

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

  选择排序是不稳定的排序算法,不稳定发生在最小元素与A[i]交换的时刻。

  比如序列:{ 5, 8, 52, 9 },一次选择的最小元素是2,然后把2和第一个5进行交换,从而改变了两个元素5的相对次序。

三、插入排序

基本思想 :在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,现在要把第n个数找到相应位置并插入,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

def insert_sort(arr,nums):   #arr:待排序数组, nums: 数组长度
    for i in range(1, nums):
        j = i - 1    # i 指向的元素为待插入的元素, j 指向的元素为已排好序序列的最后一个
        if arr[i] < arr[j]:    #若arr[i] > arr[j] 则 i 及之前的元素均为有序,直接过, 否则找到 i 指向的元素应该在的位置
            temp = arr[i]
            while j >= 0 and arr[j] > temp:
                arr[j+1] = arr[j]
                j -= 1
            arr[j+1] = temp

算法分析:

 

在第一趟排序中,插入排序最多比较一次,第二趟最多比较两次,依次类推,最后一趟最多比较N-1次。因此有:

1+2+3+...+N-1 =N*(N-1)/2

因为在每趟排序发现插入点之前,平均来说,只有全体数据项的一半进行比较,我们除以2得到:

N*(N-1)/4

复制的次数大致等于比较的次数,然而,一次复制与一次比较的时间消耗不同,所以相对于随机数据,这个算法比冒泡排序快一倍,比选择排序略快。

与冒泡排序、选择排序一样,插入排序的时间复杂度仍然为O(N2),这三者被称为简单排序或者基本排序。

如果待排序数组基本有序时,插入排序的效率会更高。

算法改进:

 

在插入某个元素之前需要先确定该元素在有序数组中的位置,上例的做法是对有序数组中的元素逐个扫描,当数据量比较大的时候,这是一个很耗时间的过程,可以采用二分查找法改进,这种排序也被称为二分插入排序

改进后的代码如下:

def _binary_search(arr, l, r, target):   #基于二分查找来找到插入点, 但不能直接照搬二分查找的算法。 arr:数组;l,r:查找的左,右端点索引值,target:目标值
    while l < r:   
        if r - l == 1:   #当搜索范围 r - l == 1时,开始判断插入点的具体索引
            if arr[l] > target:  
                return l
            else:
                return r
        ind = (l + r)//2
        if arr[ind] > target:
            r = ind
        else:
            l = ind


def insert_sort_improve(arr, nums):
    for i in range(1, nums):
        j = i - 1
        if arr[i] < arr[j]:
            temp = arr[i]
            ind = _binary_search(arr, 0, j, arr[i])
            while j >= ind:
                arr[j+1] = arr[j]
                j -= 1
            arr[ind] = temp

插入排序的改进算法还有很多,如希尔排序,2-路插入排序等。在次不再一一叙述。

插入排序,在数组基本有序的情况下,性能优异。对于较大数组的排序,一般使用快速排序与插入排序组合。可进一步提升快速排序的性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值