继续排序算法。这几天下来,对算法有了点了解,但是还是感觉到不透彻,老规矩,还是多总结多磨。
一.堆排序(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.稳定性:稳定
写在最后:总的来说这三个排序相较前面四种有一定的难度上升,希尔结合插入可以好理解一点,归并要加深对递归的理解,堆排序我这里用了较大的笔墨来总结,确实一些基本概念要理解下,相对来说也更为复杂。至此基本排序算法总结告一段落,感觉还行,还要多磨几天加深印象。