📜  Python的堆排序

📅  最后修改于: 2020-10-29 01:23:50             🧑  作者: Mango

Python的堆排序

堆排序与选择排序完全相同,在选择排序中,我们找到最大元素并将其放在最后。它基于比较排序算法,该算法适用于二进制堆数据结构。这是高效排序算法的最佳示例。

什么是堆排序?

堆排序是一种有效且流行的排序算法。堆排序的概念是从列表的堆部分“消除”元素,并将其插入到列表的排序部分。在学习有关堆排序算法的更多信息之前,让我们讨论堆数据结构。

它是就地算法,这意味着使用固定数量的内存来存储排序列表,或者内存大小不依赖于初始列表的大小。

例如-我们不需要额外的内存堆栈来存储排序后的数组,也不需要递归调用堆栈。堆排序算法通常使用第二个数组对固定值进行排序。此过程快速,简单,自然且易于实施。

另一方面,堆排序是不稳定的,这意味着它不会保持具有相等值的元素的比较顺序。它可以快速对原始类型(例如整数和字符排序,但是它对复杂的类型和对象存在问题。

让我们通过以下示例了解它-

我们有一个具有年龄和名称属性的自定义班级“学生”,并且该班级中的多个对象(包括一个名为“托马斯”,年龄为“ 20″和“彼得”的学生)以相同的顺序出现。

如果我们按年龄对人群进行排序,则不能保证在排序的数组中“托马斯”出现在“彼得”之前。可以定义顺序,但不能保证。

堆数据结构

堆数据结构是一个完整的二叉树,可实现堆属性。也称为二进制堆。

完整的二叉树满足以下属性。

  • 每个级别都应填写。
  • 所有节点都在最左侧。

正如我们在堆的上图中所看到的,但它没有排序。我们将不深入研究本文,因为我们的重点是解释堆排序算法,而不是堆。在堆排序中,第二个最小的元素始终是第一个元素。

堆树可以是两种类型-最小堆和最大树。最小堆保留最大元素的记录。最大堆跟踪最大的元素。堆主要支持以下操作-delete_minimum(),get_minimum()和add()。

堆中的第一个元素可以在还原后删除。这需要O(log N)时间,因此非常有效。

实作

Python提供了用于使用堆排序对元素进行排序的内置函数。功能如下。

  • heappush(list,item)-用于添加堆元素并对其进行重新排序。
  • heappop(list)-用于删除元素并返回该元素。
  • heapfy()-用于将给定列表转换为堆。

考虑下面的堆排序示例。

范例-

from heapq import heappop, heappush
 
 def heapsort(list1):
     heap = []
     for ele in list1:
         heappush(heap, ele)
 
     sort = []
 
     # the elements are lift in the heap
     while heap:
         sort.append(heappop(heap))
 
     return sort
 
 list1 = [27, 21, 55, 15, 60, 4, 11, 17, 2, 87]
 print(heapsort(list1))

输出:

[2, 4, 11, 15, 17, 21, 27, 55, 60, 87]

说明

在上面的代码中,我们导入了由heappop()和heappush()方法组成的heapq模块。我们创建了Heapsort Heapsort()方法,该方法将list1作为参数。一个for循环迭代list1并将元素推入空堆。我们使用了while循环和sorted元素添加到空排序中。

我们调用了Heapsort Heapsort()函数并传递了一个列表。它返回排序列表。

排序自定义对象

堆排序对于预定义的数据类型很有用,但是处理用户定义的数据类型(例如类对象)则更为复杂。我们将在本节中对自定义对象进行排序。

如我们所见,我们的实现取决于内置方法。 Python提供了以下方法。

  • heapq.nlargest(* n *,* iterable *,* key = None)-此方法用于从数据集中获取具有n个最大元素的列表,该列表由iterable定义。
  • heapq.nsmallest(* n *,* iterable *,* key = None)-此方法用于从数据集中获取具有n个最小元素的列表,该列表由iterable定义。

让我们了解自定义对象的以下实现。

范例-

from heapq import heappop, heappush
 
 class Car:
     def __init__(self, model, year):
         self.model = model
         self.year = year
 
     def __str__(self):
         return str.format("Model Name: {}, Year: {}", self.model, self.year)
 
     def __lt__(self, other):
         return self.year < other.year
 
     def __gt__(self, other):
         return other.__lt__(self)
 
     def __eq__(self, other):
         return self.year == other.year
 
     def __ne__(self, other):
         return not self.__eq__(other)
 
 
 def heapsort(list1):
     heap = []
     for element in list1:
         heappush(heap, element)
 
     ordered = []
 
     while heap:
         ordered.append(heappop(heap))
 
     return ordered
 
 
 car1 = Car("Renault", 2001)
 car2 = Car("Bentley", 2005)
 car3 = Car("Kia", 2014)
 car4 = Car("Maruti Suzuki", 1999);
 car5 = Car("Nano", 2012)
 
 list1 = [car1, car2, car3, car4, car5]
 
 for c in Heapsort Heapsort (list1):
     print(c)

输出:

Model Name: Maruti Suzuki, Year: 1999
Model Name: Renault, Year: 2001
Model Name: Bentley, Year: 2005
Model Name: Nano, Year: 2012
Model Name: Kia, Year: 2014

我们已按年份对对象进行了排序。

堆排序与其他 STL algorithm的比较

流行的快速排序算法之一也非常有效,但是由于其可靠性,合法使用堆排序。就时间复杂度而言,堆排序的主要好处是O(nlogn)上限。

在平均情况和最坏情况下,堆排序算法都需要O(nlogn)时间,而在平均情况下,快速排序要快20%。

在可预测的情况下,快速排序算法会变慢。由于可以很容易地触发犯规O(n2),因此存在快速违反安全性的可能性。

现在,我们将合并排序与堆排序花费的时间进行比较。

合并排序非常稳定并且可以直观地并行化,而堆排序则没有这种优势。

此外,合并排序在大多数情况下比堆排序要快,因为它们具有相同的时间复杂度。

相反,HeapsortHeapsort可以比Marge sort更快地就地实现。

结论

Heapsort并不那么流行且速度更快,但是它比其他任何排序算法都更具可预测性。当优先考虑内存和安全性时,首选此算法。

可以使用Python快速实现。我们需要将元素插入堆中并取出。