选择排序

在之前的讲座中,我讨论了Python中的排序。在今天的讲座中,我们将更深入地探讨这个话题。

在之前的课上,我展示了一些最简单排序算法的代码,选择排序。

以下是精选分类中主要思想的概要。

  1. 该算法将列表分为两部分,排序的部分和未排序的部分。
  2. 开始时,列表的已排序部分为空,未排序部分覆盖整个列表。
  3. 该算法以一系列回合的形式进行。在每一轮中,我们做以下事情:
    1. 在列表的未排序部分中搜索我们能找到的最小的数。
    2. 将最小的数字与未排序部分开头的数字交换位置。
    3. 将列表中已排序的部分展开1以容纳新数字,并将未排序的部分缩小1
  4. 该算法将继续,直到未排序的部分为空,并且排序的部分覆盖整个列表。此时,列表已经排序完毕。

下面是该算法的实现。为方便起见,我将算法结构化为两个函数。函数sort()实现了完整的排序算法,而函数findSmallest()作为辅助函数来帮助sort()完成其工作。

def findSmallest(list,start,end): smallest = list [start] where esmallest = start for i in range(start+1,end):如果list [i] < smallest: smallest = list [i] where esmallest = i return where esmallest def sort(list): for i in range(0,len(list)): where = findSmallest(list,i,len(list)) temp = list [i] list [i] = list [where] list [where] = temp data = [12,23,51,14,25,67,33,46,60,89] sort(data) for x in data: print(x)

插入排序

为了让我们在编写更复杂的循环时有更多的练习,这里是第二种排序算法,插入排序。

这是插入排序的主要思想大纲。

  1. 该算法将列表分为两部分,排序的部分和未排序的部分。
  2. 开始时,列表的排序部分仅包含列表中的第一个数字,未排序部分覆盖列表的其余部分。
  3. 该算法以一系列回合的形式进行。在每一轮中,我们做以下事情:
    1. 列表中未排序部分开头的数字成为“移动数”。
    2. 将移动数与其左边的数进行比较。如果他们两个有问题,让他们交换位置。重复这个步骤,直到移动数和它左边的数字的顺序正确,或者移动数一直移动到列表的左端。

这是插入排序的代码。

defsort (list): for i in range(1,len(list)): j = i while j > 0 and list [j] < list [j]: temp = list [j] list [j] = list [j-1] list [j-1] = temp j- = 1 data = [12,23,51,14,25,67,33,46,60,89] sort(data) for x in data: print(x)

归并排序

更强大和高效的排序算法使用一种称为递归的策略。这种策略处理排序这样的问题,将列表分解为多个部分,分别对每个部分进行排序,然后将这些部分组合在一起。

下面是归并排序的代码。

def合并(倾斜的,i, j, k):第一次=[船向一边倾斜的[n] n的范围(i, j + 1)]第二=[船向一边倾斜的[n] n的范围(j + 1, k)] n = 0 m = 0 p =我虽然n < len(第一个)和m < len(二):如果第一个[n] <第二[m]:船向一边倾斜的[p] =第一[n] n + = 1:船向一边倾斜的第二[m] [p] = m + = 1 p + = 1, n < len(第一):船向一边倾斜的[p] =第一[n] n + = 1 p + = 1 m < len(二):船向一边倾斜的第二[m] [p] = m + = 1 p + = 1 def(倾斜的,i, j):如果j >我:sort(list,i,mid) = (i + j)//2 sort(list,i,mid,j) sort(list,i,mid,j) data = [12, 23, 51, 14, 25, 67, 33, 46, 60, 89] sort(data,0,len(data)) for x in data: print(x)

快速排序

快速排序算法也使用递归来完成它的工作。该算法从一个分区步骤开始,该步骤选择列表中的一个项目作为枢轴值,然后将列表重新组织为小于枢轴、枢轴和大于枢轴的项目。然后我们递归地对主节点两边的列表进行排序。

def分区(倾斜的,i, j): k = i - 1主=船向一边倾斜的(j - 1) n范围(i, j - 1):如果船向一边倾斜的[n] < =主:k + = 1 temp =船向一边倾斜的[k]倾斜的[k] =船向一边倾斜的[n]倾斜的[n] = temp倾斜的[j - 1] =船向一边倾斜的[k + 1]倾斜的(k + 1) =主返回k + 1 def(倾斜的,i, j):如果j >我:k =分区(倾斜的,i, j)排序(倾斜的,我,k)排序(倾斜的,k + 1, j)数据=[51 12日,23日,14日,25日,67年,33岁,46岁,60岁,89]类(数据、0 len(数据))的x数据:打印(x)

可视化排序算法

在互联网上有许多网站,您可以找到演示基本算法操作的动画,例如这四种排序算法。其中一个站点是www.hackerearth.com,它提供了许多排序算法的可视化,包括我在这里展示的四个排序算法。

比较排序算法

我上面展示的四种排序算法可分为两大类:基本算法和递归算法。基本算法是基本的,因为它们依赖于易于理解和编码的思想。递归算法在某种程度上不那么明显,但最终具有比基本算法更好的性能。

为了说明算法之间的性能差异,我将设置一个演示。在这个演示中,我们将使用排序算法对相同的数字列表进行排序,并对每个算法进行排序。这将给我们关于算法的相对效率的即时反馈。

为了给各种相互竞争的排序算法提供一个可供排序的通用数字列表,我将从编写一个生成随机整数长列表的简单程序开始。程序将这些整数保存到一个文件中,以便排序程序从中读取。

Import random nums = [random.randint(1,100000) for _ in range(20000)] pickle。转储(num、开放(“nums.p”,“世界银行”))

保存数字的程序使用了Python pickle库,它提供了一种简单方便的方法来将二进制数据从Python程序转储到二进制文件中。

下面是从文件中读取数字并对它们进行两次排序的程序,一次使用插入排序,第二次使用快速排序。

import pickle import time def insert (list):使用插入排序将list按升序排序。”“我的范围(1,len(倾斜的)):j =我虽然> 0和倾斜的[j] <船向一边倾斜的[j - 1]: temp =船向一边倾斜的[j]倾斜的[j] =船向一边倾斜的(j - 1)船向一边倾斜的(j - 1) = temp j - = 1 def分区(倾斜的,i, j): k = i - 1主=船向一边倾斜的(j - 1) n范围(i, j - 1):如果船向一边倾斜的[n] < =主:k + = 1 temp =船向一边倾斜的[k]倾斜的[k] =船向一边倾斜的[n]倾斜的[n] = temp倾斜的[j - 1] =船向一边倾斜的[k + 1]倾斜的(k + 1) =主返回k + 1 def快速排序(倾斜的,i, j):如果j >我:k =分区(倾斜的,i, j)快速排序(倾斜的,我,k)快速排序(倾斜的,k + 1, j) num =泡菜。加载(open) ('nums。p', 'rb')) now = time.time() insert (nums) later = time.time() print(' insert sort took ',later - now,' seconds.') nums = pickle。加载(open) ('nums。p', 'rb')) now = time.time() quicksort(nums,0,len(nums)) later = time.time() print(' quicksort took ',later - now,' seconds.')

这个程序显示了两种算法的速度之间的巨大差异。对于包含20000个随机整数的列表,插入排序需要19.8秒来排序列表,而快速排序只需要0.04秒。随着列表长度的增加,这种差异变得更加明显。插入排序所需的时间与列表长度的平方成正比,因此列表长度翻倍会使排序时间增加4倍。另一方面,快速排序,花费的时间与n log n成正比排序一个有n个元素的列表。列表长度翻倍只会使排序时间增加2倍多一点。

编程任务

在这个任务中,我们将把插入排序和快速排序算法结合起来,使快速排序更加高效。快速排序的一个不太理想的方面是,快速排序将要排序的列表细分为大小为1的列表。在此基础上的改进是使用混合策略。在混合策略中,我们修改了快速排序,这样当它遇到大小小于某个预设截止值的子列表时,它使用插入排序对子列表进行排序,而不是继续递归地将子列表细分为越来越小的列表。

实现这个版本的快速排序,并使其在遇到大小小于10的子列表时转换为使用插入排序。将这个修改版本的QuickSort与原始版本在一个大的数字列表上进行比较,并让您的程序证明修改版本更快。