vlambda博客
学习文章列表

python-简简单单来学习几个常见的排序算法part1

    算法并没有什么高大上,但是却需要沉下心来动脑动手学习,它无关于开发者使用何种开发语言,但作为码农一员,手撕算法必须经历,不必心怀恐惧,若不忍受痛苦,做什么都可能是从入门到放弃!

    在此总结一下这两天学习的排序算法,因为对python较为熟悉,故此使用python实现,记录于此。

  • 冒泡排序

思想:对乱序数组array中n个元素从开始位两两依次比较大小,想升序排时总让比较的两个元素较大者靠后,如此当n-1和n比较后便可获得数组中的最大元素,此趟结束;再次从开始元素重复之前操作,如此循环n-1趟即可完成对数组的n个元素从大到小排序。

代码实现:

运行后即可将乱序数组升序排列[1, 3, 4, 5, 7, 11, 12, 15, 55, 90, 99]

说明:冒泡排序使用双循环,故时间复杂度O(n^2),当数据量较大时效率较低,但是较为稳定,当列表中有相等元素时排序后不会改变相对位置。此外冒泡排序存在优化之处,因为倘若待排序数组本来就是升序数组或者大部分元素有序,此时如上代码依然会机械执行,我们可以在每趟比较过程中加上标志位,若存在交换则下一趟继续比较,否则证明上一趟比较过程不存在元素交换,已经是有序状态了,直接退出即可,代码如下:

def bubble_sort(arr):
for i in range(len(arr) - 1):
flag = True #此处添加标志位
for
j in range(len(arr) - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
flag = False #若此趟存在比较操作,则改变状态
if flag:#判断,若没有比较操作表示当前数组已经有序,结束整个循环
break

return arr

当然,劳动人民的智慧总是无穷尽的,还存在优化之处,例如鸡尾酒算法就是通过

冒泡算法优化而来,由于代码总是占据太多篇幅,此处就不上代码了。是不是觉得

这些算法名字相当有意思呢!冒泡算法是比较形象的,每趟比较过程,元素就像浮

动的气泡,而鸡尾酒算法是因为一趟比较从头到尾再从尾到头,故而有种飘摇的感

觉吧……

  • 插入排序

思想:插入排序的思想就是我们将待排序的数字array[n]分成两部分,叫做有序区

和无序区。想想牌友们结牌的过程,此时我们手上的牌有序,从牌桌上面结的牌是

随机的,牌友每接一张牌都会扫描一眼手上的牌,然后将牌插入合适的位置保证有

序。不说那么多直接上代码……


插入排序和冒泡排序的时间复杂度都是O(n^2),且也是稳定排序,当然插入排序也有升级优化版-希尔排序,此处不做展开……

  • 快速排序

思想:快速排序使用了分治的思想,将待排序的无序数组不断的分成两段(寻找一个基准值,通常选择1号元素,将数组中大于基准元素的放一边,小于等于基准值的元素放一边),依次直到无法再次分段为止(即数组长度为1时),最后的数组即为有序数组,快速排序的分段又分双端循环和单端循环两种方式,双端循环需要从数组两端往中间走,先上单端循环的代码……

单端循环实现代码:

def quick_sort(arr, start_index, end_index):
"""
# 调用时传入arr列表对象,列表的起始索引即终止索引,递归调用自己排序
:param arr:
:param start_index:
:param end_index:
:return:
"""

if start_index >= end_index:# 递归的终止条件,当起始索引和终止索引

等之时表示数组已经只有一个值

return #此处为什么没有详细的返回值呢?因为arr元素已经操作,无需

返回具体值,也无处需要使用

pivot_index = find_pivot(arr, start_index, end_index)# 获得数组分段

基准停留的索引值

quick_sort(arr, start_index, pivot_index - 1)#对基准索引左侧继续分

quick_sort(arr, pivot_index + 1, end_index)# 对基准索引右侧继续分组
def find_pivot(arr, start_index, end_index):
"""
功能:将数组arr分开
:param arr:
:param start_index:
:param end_index:
:return: 返回基准值的索引
"""
pivot = arr[start_index]# 取数组首位元素为基准值
mark = start_index # 使初始标记指向数组首元素

for i in range(start_index + 1, end_index + 1):#循环从首位元素后一

位开始,知道最后一位

if arr[i] < pivot:# 遍历过程中当发现数组元素小于等于基准值便做两

个动作,如下

mark += 1 # 将mark标记指向后移一位

arr[i], arr[mark] = arr[mark], arr[i]# 调换当前遍历的元素与

mark指向的元素

arr[mark], arr[start_index] = pivot, arr[mark]# 当数组遍历完成后调

换数组的基准值位元素与mark指向元素即可

return mark


双端循环实现代码:

def find_pivot_doubble(arr,start_index, end_index):
pivot = arr[start_index]#取首位元素为基准值
left = start_index# 用两个指针分别指向起止索引
right = end_index
while left != right:#当两个指针相向移动,相等时无需再比较

while right > left and arr[right] > pivot:#先从右边开始,当指针指

向的值大于基准值则将指针向起始方向移动,否则停止

right -= 1

while right > left and arr[left] < pivot:# 右边停止时,比较左侧指

针指向的值与基准值大小,若比基准值小则指针后移,否则停止

left += 1

arr[left], arr[right] = arr[right], arr[left]# 停止后交换左右指针

对应下标的数组值

arr[start_index], arr[left] = arr[left], arr[start_index]# 当左右指针

相等退出循环时交换数组元素

return left # 返回基准值对应的数组索引

一不小心超时了,但是排序算法还有很多,只能下回分解了……悄悄将题目加个part1