数据结构与算法--排序算法

排序算法

如何分析一个“排序算法”?

学习排序算法,我们除了学习它的算法原理、代码实现之外,更重要的是要学会如何评价、分析一个排序算法。那分析一个排序算法,要从哪几个方面入手呢?

  • 排序算法的执行效率
    对于排序算法执行效率的分析,我们一般会从这几个方面来衡量:
  1. 时间复杂度
  2. 比较次数和交换(或移动)次数
  • 排序算法的内存消耗
    原地排序算法,就是特指空间复杂度是 O(1) 的排序算法

  • 排序算法的稳定性
    仅仅用执行效率和内存消耗来衡量排序算法的好坏是不够的。针对排序算法,我们还有一个重要的度量指标,稳定性。这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变

递归

递归是一种应用非常广泛的算法(或者编程技巧)
需要三个条件

  1. 分解成几个子问题
  2. 循环条件
  3. 基线条件
# 用递归的方式输出l=['jack',('tom',23),'rose',(14,55,67)] 列表内的每一个元素
# jack tom 23 rose 14 55 67

l = ['jack', ('tom', 23), 27, (14, 55, 67)]
def dp(l):
    if isinstance(l, (str, int)):
        print(l)
    else:
        for i in l:  #先进入这里的循环
            dp(i)
dp(l)

一. 冒泡排序

需要重复对序列进行遍历, 两个两个相邻的数进行比较,若前面的比后面的数大, 则把他们的位置调换过来., 直到把最大的数放到最后,需要比较的次数会逐渐减少

图片演示

在这里插入图片描述
在这里插入图片描述

代码演示

li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  索引0    1   2  3    4  5   6    7   8
def bu_sort(li):
    n = len(li)
    for j in range(n-1, 0, -1):  #每次需要比较的次数. 8 7 6 5 4 3 2 1
        for i in range(j):
            if li[i] > li[i+1]:
                li[i], li[i+1] = li[i+1], li[i]
if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    bu_sort(li)
    print(li)

冒泡排序是稳定的, 因为两个相同的数不会进行交换, 时间复杂度分析: 若序列本来就是有序的, 时间复杂度是O(n)

二. 选择排序(Selection Sort)

概念: 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
通俗说:分为已排和为排, 从未排序的那里找出一个最大或者最小, 找最小就放在最前, 以此类推, 次数逐渐减少
交换时是两个进行交换, 仅仅两个位置交换而已
在这里插入图片描述

选择排序演示

在这里插入图片描述

实现
def select_sort(li):
    n = len(li)
    for i in range(n-1):  #操作的次数  len(li)-1
        min_index = i  这是第一个值
        for j in range(min_index, n):  #未排序的下标
            if li[j] < li[min_index]:
                min_index = j  #把最小的值的索引给第一个最小的索引
        li[i], li[min_index]  = li[min_index], li[i]  #若可以进入这里, 就交换

li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
select_sort(li)
print(li)

稳定性:最小值重复稳定, 最大值重复不稳定
时间复杂度n^2

三. 插入排序(Insertion Sort)

概念: 先构建一个有序的序列, 第一个就为有序的序列, 然后从后面的无序的序列中拿出第一个与前面的有序序列进行比较, 插入符合的位置
需要两个操作:一个是与前面的比较, 一个是移动前面的给后面的补上

演示如下:

在这里插入图片描述
在这里插入图片描述

代码实现

def insert_sort(li):
    n = len(li)
    for i in range(n-1, 0, -1):
        for j in range(i):
            if li[j] > li[i]: #i 是先为8所以表示后面
                li[j], li[i] = li[i], li[j]
if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    insert_sort(li)
    print(li)

时间复杂度是O(n^2)
是稳定的

四. 希尔排序(Shell_sort)

按照一定的步长选出几组,然后再进行第一次组合,再按照一定的步长取出,步长一般是长度的一半直到步长为1才可以完成排序
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

def shell_sort(li):
    n = len(li)
    gap = n // 2
    while gap > 0:
        for i in range(gap, n):  # 4-8
            while i >= gap and li[i - gap] > li[i]:
                li[i - gap], li[i] = li[i], li[i - gap]
                i -= gap
        gap = gap // 2  #更新gap, 逐渐减小
if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    shell_sort(li)
    print(li)

时间复杂度
不稳定

五. 快速排序(Quick_sort)

快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort)
定义: 首先设定一个基准指, 然后比较后面的值, 若比基准值小放到基准值前面, 比基准值大放到基准值后面
然后利用递归重复上面的操作, 这排序是不稳定的

在这里插入图片描述
29就是基准值
在这里插入图片描述

快速排序的实现(老师)
def quick_sort(alist, start, end):
    """快速排序"""

    # 递归的退出条件
    if start >= end:
        return

    # 设定起始元素为要寻找位置的基准元素
    mid = alist[start]

    # low为序列左边的由左向右移动的游标
    low = start

    # high为序列右边的由右向左移动的游标
    high = end

    while low < high:
        # 如果low与high未重合,high指向的元素不比基准元素小,则high向左移动
        while low < high and alist[high] >= mid:
            high -= 1
        # 将high指向的元素放到low的位置上
        alist[low] = alist[high]

        # 如果low与high未重合,low指向的元素比基准元素小,则low向右移动
        while low < high and alist[low] < mid:
            low += 1
        # 将low指向的元素放到high的位置上
        alist[high] = alist[low]
  
    
    # 退出循环后,low与high重合,此时所指位置为基准元素的正确位置
    # 将基准元素放到该位置
    alist[low] = mid

    # 对基准元素左边的子序列进行快速排序
    quick_sort(alist, start, low-1)

    # 对基准元素右边的子序列进行快速排序
    quick_sort(alist, low+1, end)


alist = [54,26,93,17,77,31,44,55,20]
quick_sort(alist,0,len(alist)-1)
print(alist)

自己的

def quick_sort(li, strat, end):
    low  = strat  #为了递归那里方便控制
    higt = end
    mid = li[low]  #设定一个基准值, 一般是第一个
    if strat >= end   #退出条件, 递归一定要有退出条件
        return
    while low < higt: #两个游标重合, 退出循环
        while low < higt and li[higt] > mid:
            higt -= 1
        li[low] = li[higt]  #将比中间值小的值放到左边
        while low < higt and li[low] < mid:
            low += 1
        li[higt] = li[low] #将比中间值大的值放到右边
    li[low] = mid  #退出这个循环, 两个游标重合,将中间值传给游标指定的值
    quick_sort(li, strat, low-1)  #递归, 将中间值前面的值再来重复上面的操作
    quick_sort(li, low+1, end)    #递归, 将中间值后面的值再来重复上面的操作
if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    quick_sort(li, 0, len(li)-1)
    print(li)

六. 归并排序(Merge sort)

先对一组数进行拆分, 按中间位置进行分, 分到不能再分,然后按原来的步骤排序和起来,将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可
在这里插入图片描述

代码实现

归并排序的实现(老师)
def merge_sort(li):
    # 如果列表长度小于1,不在继续拆分
    if len(li) <= 1:
        return li
    # 二分分解
    mid_index = len(li) // 2
    left = merge_sort(li[:mid_index])
    right = merge_sort(li[mid_index:])
    # 合并
    return merge(left,right)

def merge(left, right):
    '''合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组'''
    # left与right的下标指针
    l_index, r_index = 0, 0
    result = []
    while l_index < len(left) and r_index < len(right):
        if left[l_index] < right[r_index]:
            result.append(left[l_index])
            l_index += 1
        else:
            result.append(right[r_index])
            r_index += 1

    result += left[l_index:]
    result += right[r_index:]
    return result

alist = [54,26,93,17,77,31,44,55,20]
sorted_alist = merge_sort(alist)
print(sorted_alist)

我的

"""
归并排序:对半拆分序列,拆到不能拆为止,然后比较大小合并
"""
def merge_sort(li):
    mid_index = len(li)//2#对半分
    if len(li) <= 1: #递归退出条件
        return li
    left = merge_sort(li[:mid_index]) 
    right = merge_sort(li[mid_index:])
    left_index, right_index = 0, 0
    result = []
    while left_index < len(left) and right_index < len(right): #一次长度只是1 , 就是两个数, 一个左一个右
        
        if left[left_index] < right[right_index]:#进入这里是左的小于右边的直接添加进去
            result.append(left[left_index])
            left_index += 1
        else:
            result.append(right[right_index])  #如果左边的数比较大, 就添加右边的数
            right_index += 1
    result += left[left_index:]
    result += right[right_index:]
    return result

if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    sort = merge_sort(li)
    print(sort)

常见排序算法效率比较

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值