sorting-algorithms-python
与许多其他高级编程语言一样,Python语言提供了使用sorted()
函数对数据进行开箱即用的功能。示例:
>>> li = [9, 5, 3, 6, 7]
>>> sorted(li)
[3, 5, 6, 7, 9]
冒泡排序是最直接的排序算法之一。它的名称来自算法的工作方式:每经过一次便利,列表中最大的元素就会“冒泡”至正确位置。
冒泡排序包括:遍历一个列表,一次比较元素,以及交换不规则的相邻项。
def bublle_sort(array):
n = len(array)
for i in range(n):
# 创建一个标志位
already_sorted = True
for j in range(n - i - 1):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
already_sorted = False
if already_sorted:
break
return array
时间复杂度:O( n 2 n^2 n2).
像冒泡排序一样,插入排序算法也易于实现和理解。但是与冒泡排序不同,它通过将每个项目与列表的其余部分进行比较并将其插入正确的位置,来一次构建一个排序的列表元素。此“插入”过程为算法命名。
解释插入排序的一个很好的类比是您对一副纸牌进行排序的方式。想象一下,您手里拿着一组卡片,并且想要按顺序排列它们。首先,将一张卡片与其余卡片进行逐步比较,直到找到正确的位置为止。届时,您需要将卡插入正确的位置,然后重新开始制作新卡,重复进行直到您手上的所有卡都被排序为止。
def insertion_sort(array):
for i in range(1, len(array)):
key_item = array[i]
j = i - 1
while j >= 0 and array[j] > key_item:
array[j + 1] = array[j]
j -= 1
array[j + 1] = key_item
return arry
时间复杂度:O( n 2 n^2 n2).
归并排序是一种非常有效的排序算法。它基于分治法,这是一种用于解决复杂问题的强大算法技术。
为了正确理解分而治之,您应该首先了解递归的概念。递归涉及将问题分解为较小的子问题,直到它们足够小以至于无法解决。在编程中,递归通常由调用自身的函数表示。
分而治之算法通常遵循相同的结构:
在归并排序的情况下,分而治之的方法将输入值的集合划分为两个大小相等的部分,对每个一半进行递归排序,最后将这两个排序的部分合并为一个排序列表。
def merge(left, right):
if len(left) == 0:
return right
if len(right) == 0:
return left
result = []
index_left = index_right = 0
while len(result) < len(left) + len(right):
if left[index_left] <= right[index_right]:
result.append(left[index_left])
index_right += 1
else:
result.append(right[index_right])
index_left += 1
if index_right == len(right):
result += left[index_left:]
break
if index_left == len(left):
result += right[index_right:]
break
return result
def merge_sort(array):
if len(array) < 2:
return array
midpoint = len(array) // 2
return merge(
left=merge_sort(array[:midpoint]),
right=merge_sort(array[midpoint:]))
时间复杂度: O( n l o g n n logn nlogn)
就像合并排序一样,快速排序算法采用分而治之的原理将输入数组分为两个列表,第一个包含小项目,第二个包含大项目。然后,该算法将对两个列表进行递归排序,直到对结果列表进行完全排序为止。
划分输入列表称为对列表进行分区。Quicksort首先选择一个枢轴元素,然后将列表围绕该枢轴进行分区,将每个较小的元素放入一个低数组,将每个较大的元素放入一个高数组。
将每个元素从低位列表放置到数据透视表的左侧,将每个元素从高位列表放置在数据透视表的右侧,将其精确定位在最终排序列表中需要的位置。这意味着函数现在可以递归地将相同的过程递归地从低到高,直到对整个列表进行排序。
rom random import randint
def quicksort(array):
# If the input array contains fewer than two elements,
# then return it as the result of the function
if len(array) < 2:
return array
low, same, high = [], [], []
# Select your `pivot` element randomly
pivot = array[randint(0, len(array) - 1)]
for item in array:
# Elements that are smaller than the `pivot` go to
# the `low` list. Elements that are larger than
# `pivot` go to the `high` list. Elements that are
# equal to `pivot` go to the `same` list.
if item < pivot:
low.append(item)
elif item == pivot:
same.append(item)
elif item > pivot:
high.append(item)
# The final result combines the sorted `low` list
# with the `same` list and the sorted `high` list
return quicksort(low) + same + quicksort(high)
时间复杂度: O( n l o g n n logn nlogn)
Timsort算法被认为是一种混合排序算法,因为它采用了插入排序和合并排序的两全其美组合。Timsort与Python社区近在咫尺,因为它是由Tim Peters在2002年创建的,被用作Python语言的标准排序算法。
Timsort的主要特征是它利用了大多数现实数据集中存在的已排序元素。这些称为自然运行。然后,该算法会遍历列表,将元素收集到运行中,然后将它们合并到一个排序的列表中。
在本部分中,您将创建一个准系统的Python实现,该实现说明Timsort算法的所有部分。如果您有兴趣,也可以查看Timsort的原始C实现。
def insertion_sort(array, left=0, right=None):
if right is None:
right = len(array) - 1
# Loop from the element indicated by
# `left` until the element indicated by `right`
for i in range(left + 1, right + 1):
# This is the element we want to position in its
# correct place
key_item = array[i]
# Initialize the variable that will be used to
# find the correct position of the element referenced
# by `key_item`
j = i - 1
# Run through the list of items (the left
# portion of the array) and find the correct position
# of the element referenced by `key_item`. Do this only
# if the `key_item` is smaller than its adjacent values.
while j >= left and array[j] > key_item:
# Shift the value one position to the left
# and reposition `j` to point to the next element
# (from right to left)
array[j + 1] = array[j]
j -= 1
# When you finish shifting the elements, position
# the `key_item` in its correct location
array[j + 1] = key_item
return array
此修改后的实现添加了左右两个参数,这些参数指示应对数组的哪一部分进行排序。这使Timsort算法可以对数组的一部分进行排序。修改功能而不是创建新功能意味着可以将其同时用于插入排序和Timsort。
def timsort(array):
min_run = 32
n = len(array)
# Start by slicing and sorting small portions of the
# input array. The size of these slices is defined by
# your `min_run` size.
for i in range(0, n, min_run):
insertion_sort(array, i, min((i + min_run - 1), n - 1))
# Now you can start merging the sorted slices.
# Start from `min_run`, doubling the size on
# each iteration until you surpass the length of
# the array.
size = min_run
while size < n:
# Determine the arrays that will
# be merged together
for start in range(0, n, size * 2):
# Compute the `midpoint` (where the first array ends
# and the second starts) and the `endpoint` (where
# the second array ends)
midpoint = start + size - 1
end = min((start + size * 2 - 1), (n-1))
# Merge the two subarrays.
# The `left` array should go from `start` to
# `midpoint + 1`, while the `right` array should
# go from `midpoint + 1` to `end + 1`.
merged_array = merge(
left=array[start:midpoint + 1],
right=array[midpoint + 1:end + 1])
# Finally, put the merged array back into
# your array
array[start:start + len(merged_array)] = merged_array
# Each iteration should double the size of your arrays
size *= 2
return array
此修改后的实现添加了左右两个参数,这些参数指示应对数组的哪一部分进行排序。这使Timsort算法可以对数组的一部分进行排序。修改功能而不是创建新功能意味着可以将其同时用于插入排序和Timsort。