python数据结构与算法(堆排序,希尔排序,归并排序)

继续排序算法。这几天下来,对算法有了点了解,但是还是感觉到不透彻,老规矩,还是多总结多磨。

一.堆排序(Heapsort)

先吐槽下,对于非计算机专业,这个算法花了点时间去理解- -。
堆排序,百度百科一句话,“是指利用堆这种数据结构所设计的一种排序算法”。刚接触到,一脸懵。这里接触到一些计算机的基本概念,这里简单阐述下。
基本概念及实现流程
1.首先需要了解的是堆排序是用堆这种数据结构来进行排序。
2.我们需要大概了解堆是什么。
堆是一种特殊的数据结构,与我们常听到的栈是相对的。
这里引用个人感觉对小白非常友好的一个讲解链接。
什么是堆
这篇文章非常推荐,自己在理解时,确实收获了很多。
感觉这篇文章讲的很细了= =,圈下关键词:完全二叉树,大根堆,小根堆以及根的节点
3.重点看下堆的流程实现(升序为例-大根堆)
(1)需要将待排序列构成一个大根堆。
(2)将大根堆的顶点(即最大值)和大根堆最下方最右边元素交换,这里用python列表概念转换下,大根堆顶点即第一位元素0号位,要交换的是最下方最右边元素即列表最后一位
(3)交换完毕后,隔离该最下方最右边的元素。简单来说就是隔离列表最后一位元素,此时最后一位元素为最大值。
(4)隔离之后剩下元素再次构成大根堆后,重复上述步骤,直到大根堆长度为一。
堆排序图示
堆排序动态演示
思路
初上手,虽然能简单理解,但是实际写的时候还是懵的,推荐跟着动态图流程一步步自己实现下,加深理解。
下面贴出个人的思路,网上查了之后各种自己都觉得有点繁琐,自己深入理解后给出自己的思路。
1.大根堆函数
2.交换大根堆首尾函数
3.结合上述两函数
python实现
1.首先看大根堆函数,关键带在于节点的索引从下往上排的节点,排完一轮要更新父节点。关于父节点索引,在列表中假设长度为8,那么形成一个堆,它的父节点在python列表中索引为range(8//2),3->2->1->0(在堆中是4->3->2->1,堆从1开始)

def big_heap(arr,n):

    f = n//2
    for i in range(f):
        # j为父节点索引
        j = f-i-1
        # so为左下子结点索引
       son = 2*j+1
        while son< n:
            # 判断左右子节点大小,选取大的和父节点比较
            if son+1<n and arr[son+1]>arr[son]:
                son+=1
            # 子节点,父节点比较
            if arr[son]>arr[j]:
                arr[son],arr[j] = arr[j],arr[son]
            # 交换完更新父节点
            j = son
            son =2*son +1

2.交换大根堆首尾函数很简单,最后一起贴出来

#交换大根堆首尾
def swap(arr,end):
    arr[0],arr[end] = arr[end],arr[0]
 #结合函数
def heap_sort(arr):
    # goudui(arr,len(arr))
    for i in range(len(arr),1,-1):
        big_heap(arr, i)
        #排完大根堆,交换大根堆首尾
        swap(arr,i-1)
        # goudui(arr,i-1)

时间复杂度
1.最优时间复杂度:O(nlogn)
2.最坏时间复杂度:O(nlogn)
3.稳定性:不稳定

二.希尔排序(ShellSort)

希尔排序,是插入排序的改进,具体实现上有同工之处,但是相比起来更高效。回顾下插入排序,插入是一个个移位插入,效率确实慢,所以希尔排序是分组插入排序,然后慢慢减小分组。本质上和插入排序相比是步长的不同,这里直接给出python实现。
Python实现

# 希尔
def shell(arr):
    n = len(arr)
    # 初始步长
    step = n//2
    while step>0:
        for i in range(step,n):
            j =i
            # 插入排序
            while j<n:
                if arr[j]<arr[j-step]:
                    arr[j],arr[j-step] = arr[j-step],arr[j]
                j+=step
        #新步长
        step = step//2

时间复杂度
1.最优时间复杂度:根据步长序列的不同而不同
2.最坏时间复杂度:O(n2)
3.稳定性:不稳定

三.归并排序

归并排序是采用分治法的典型应用,基本思想是先递归分解数组,然后合并数组。具体来看下动态演示:
归并动态演示
归并排序动态演示
归并个人感觉还是要理解下递归加深印象。
python实现

# 归并
def guibing(arr):
    n = len(arr)
    # 递归终止条件
    if n<2:
        return arr
    # 二分分解列表
    mid = n//2
    # 利用递归截取左右列表
    left_l = guibing(arr[:mid])
    right_l = guibing(arr[mid:])
    # 左右初始指针
    left,right = 0,0
    # 生成新列表
    result = []
    while left< len(left_l)and right<len(right_l):
        # 左右列表逐个比较后插入
        if left_l[left]<right_l[right]:
            result.append(left_l[left])
            left+=1
        else:
            result.append(right_l[right])
            right+=1
    # 比完,插入剩余元素
    result+=left_l[left:]
    result+=right_l[right:]
    # 返回新列表
    return result

时间复杂度(通过增加新的空间来减少时间)
1.最优时间复杂度:O(nlogn)
2.最坏时间复杂度:O(nlogn)
3.稳定性:稳定

写在最后:总的来说这三个排序相较前面四种有一定的难度上升,希尔结合插入可以好理解一点,归并要加深对递归的理解,堆排序我这里用了较大的笔墨来总结,确实一些基本概念要理解下,相对来说也更为复杂。至此基本排序算法总结告一段落,感觉还行,还要多磨几天加深印象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值