您的位置:首页 > 编程语言 > Python开发

用 Python 学习算法:初级排序算法

2017-12-01 19:55 423 查看
摘要:

1. 使用 Python 实现初级排序算法:选择排序,插入排序,冒泡排序,希尔排序

2. 简单分析比较四种排序算法的性能

写在最前面

更多内容,请关注我的公众号: PythonAndMachineLearning



如需下载本文中的代码,关注公众号后,回复“初级排序” 即可获得下载链接~。

简介

关于排序的具体定义,这里就不多说了,写多了我都觉得是一些没有的废话。总之一句话,就是将给定的一组数据,按照从小到大或者从大到小的顺序进行排列。

这一组数据,一般情况下都是指数组,Python 中则指列表。需要注意的是,我们没有限定数组内部具体每个元素是什么类型。常见的也就是:整数,实数,字符串等等,当然我们也可以自己定义数据类型,自定义的数据类型需要满足一个条件,那就是元素之间可以比较大小。

我参考学习的主要是《算法》这本书,其中就提及我们使用的数据类型需要通过 Comparable 接口实现 less() 方法。没看懂这句话?并没有没什么影响。这句话的意思就是说,我们使用的数据类型必须能比较大小。感觉在 Java 中实现起来有点小麻烦,但是在 Python 中则相对比较简单,我们需要定义重载运算符
__lt__()
即可,关于这一点,后面会具体提及。

下面我们介绍三种排序算法:选择排序 、插入排序 和 冒泡排序,并做简单的分析。使用的数据为整数或者实数列表。

准备工作

在具体介绍之前,我们首先介绍几个简单的辅助函数:

exch() 函数

def exch(datalist, i, j):
'''
- 交换 datalist 中第 i 个数与第 j 个数的位置
'''
datalist[i], datalist[j] = datalist[j], datalist[i]


这里有一点需要注意,在 Python 中交换两个变量的值,并不需要中间再定义一个临时变量

isSorted() 函数

def isSorted(datalist):
'''
- 检测 datalist 中的元素是否有序
'''
for i in range(len(datalist)-1):
if datalist[i] > datalist[i+1]:
return False


这里需要注意的是 len() 函数的用法,如果 datalist 是一个列表,则不用操心。但是如果 datalist 是我们自定义的数据类型,就得注意在类中加入
__len__()
重载操作符。

选择排序

选择排序算法算是一种最简单的排序算法,其步骤如下:

找到数组中最小的那个元素

将这个最小的元素与第一个元素互换位置

在剩下的元素中找到一个最小的元素

将这个最小的元素与第二个元素互换位置

依次类推,直到最后

下面给出相应的 Python 代码:

# _*_ encoding:utf-8 _*_

'''
Created on 2017\11\29

@author: wangs0622
'''

import sort
# 上面给出的几个辅助函数都在 sort 这个文件中存放

def selectionSort(data):
for i in range(len(data)):
min_index = i
for j in range(i,len(data)):
if data[j] < data[min_index]: min_index = j
sort.exch(data, i, min_index)

if __name__ == '__main__':
data = [5,3,6,9,12,35,32,89,43,1,-2,-5,24,100,-34,23,43,23,89,11]
selectionSort(data)
print data


代码看上去很简单,并不复杂,主要是因为这个算法本身就比较简单。

选择排序算法简单分析

虽然这个算法看上去简洁明了,但是其计算时间比较长。假设给定的数组长度为 N,则整个程序运行下来,我们需要进行大概 N2/2 次比较,并进行 N 次交换。

插入排序

插入排序,这种排序的原理也相对简单。我们从某个位置的元素开始,我们认为,这个元素左边的数组已经有序了,按照从小到大的顺序排列。我们需要做的是,将这个元素插入到左边有序数组的合适位置,使得加入这个元素后,这个数组依旧有序。具体步骤如下:

我们从第 2 个元素开始(其索引为 1,因为从零开始索引),对比第 2 个元素与第 1 个元素,如果第 2 个元素小于第 1 个元素,则交换两者的位置。

然后是第 3 个元素,把它和第二个元素对比,如果第三个元素比较小,则交换两者位置。这时第 3 个元素在 第二个元素的位置,然后对比它与第一个元素的位置,如果小,则交换。

然后对第4…,N个元素分别进行相同的操作。

具体的流程,我们可以参考如下的动态演示图:



使用 Python 代码实现上述的过程:

def insertionSort(data):
for i in range(1, len(data)):
temp = range(i)
temp.reverse()
for j in temp:
if data[j+1] < data[j]:
sort.exch(data, j, j+1)


插入排序算法简单分析

插入排序所需的时间取决于输入列表中元素的顺序,如果输入列表本身就是有序的,那我们需要做 N−1 次比较, 0 次交换。这也是最理想的情况了。最糟糕的情况下,我们需要做 N2/2 次比较, N2/2 次交换。由此可见,插入排序要比选择排序更加高效。

冒泡排序

冒泡排序与选择排序有异曲同工之妙。当初我学习 C++ 的时候就遇见过这个名词,貌似还是一个考试重点。如今,关于 C++ 的知识基本上都快还给老师了,不过“冒泡排序”这个名词依稀还记得,因为这个名字起的很有特色,很难忘记。具体的排序步骤如下:

比较相邻的两个元素,如果第一个元素比第二个元素大,则交换这两个元素。

从头到尾,将每一对相邻元素,做如上的处理。这样第一圈下来,最后一个元素一定是最大的元素。

针对处理最后一个元素外的所有元素在进行上述操作。(因为最后的元素已经是最大的了,不用进行对比了)

直到所有元素排序完成为止。

上述的过程就和我们的选择排序算法道理是一样的,将最大的那个数放在最后。具体流程参照如下的演示图:



def bubbleSort(data):
for i in range(len(data)-1):
for j in range(len(data)-i-1):
if data[j] > data[j+1]:
sort.exch(data, j, j+1)


冒泡排序复杂度简单分析

冒泡排序的操作次数也与列表元素的初始情况有关,最好的情况就是已经排好顺序的情况,这种情况下,我们需要做大概 N2/2 次比较,0 次交换;最差的情况就是输入列表是按照从大小的顺序排列,我们大概需要做 N2/2 次比较,并做 N2/2 次交换。总体来讲,不比选择排序高效。

上面我们举的例子都是整数的例子,正如一开所说的那样, 在 Python 我们可以自定义数据类型,只有这种数据类型是可以比较大小的,我们都可以进行排序,下面我们给出一个自定义的数据类型:日期。然后针对自定义的数据类型,使用上述介绍的三种排序算法进行排序。

自定义数据类型举例

我们自定义的数据类型是日期,它包括了年、月、日三个变量,其定义大小的习惯于我们日常习惯相同,下面给出具体的代码:

class Date():

def __init__(self, year = 0, month = 0, day = 0):
self.year = year
self.month = month
self.day = day

def __lt__(self, date):
if self.year < date.year:
return True
elif self.year > date.year:
return False
else:
if self.month < date.month:
return True
elif self.month > date.month:
return False
else:
if self.day < date.day:
return True
else:
return False
def __repr__(self):
return (str(self.year) + ' - '+
str(self.month) + ' - ' +
str(self.day))


在 Date 类中,我们定义了
__lt__()
方法,这个方法在两个 Date 对象使用 ‘<’ 的时候,自动调用。例如: date1 < date2,此时自动调用
__lt__()
方法,相当于 date1.
__lt__
(data2)。

__repr__()
方法在之前的“用 Python 学习算法 – 链表” 那篇文章中已经介绍过了,简单来说,这个函数在我们打印对象的时候,自动调用。

下面,我们随机生成一些日期,将其放在列表中,然后进行排序,具体程序如下:

if __name__ == '__main__':

date = []
for i in range(30):
year = random.randint(2000,3000)
month = random.randint(0,12)
day = random.randint(0,30)
date.append(Date(year, month, day))
temp = date[:-1]
selectionSort(date)
for x, y in zip(temp, date):
print x, '           ', y


我们随机生成了 30 个日期对象,放在 date 这个列表中,然后调用了 选择排序函数对这个列表中的元素进行排序,然后打印结果。结果中第一列为原始数据,第二列为排序后的数据。

需要注意一点,在进行列表复制的时候,我们的程序是:

temp = date[:-1]


而不是

temp = date


如果是第二种写法的话,再对 date 中的元素进行排序后, temp 中的元素也会改变。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息