1、插入排序法
def inster_sort(arr):
# 排序n个数要进行n-1次排序,且要从第二个元素开始
for i in range(1, len(arr)):
# 在后面while循环会改变列表中元素的顺序,导致arr[i]的值与最初不一样
# 因此将要插入的元素提前存储起来并且不去改变它
temp = arr[i]
# 从arr[i]的前一个元素开始与后面元素进行比较
j = i - 1
# 如果arr[j]大于要插入的数,将其向后移动
while j >= 0 and arr[j] > temp:
# 将自己的值赋予后一个元素,以达到向后移动的效果
arr[j + 1] = arr[j]
# 将自己的位置空出来,下一次循环时,如果有大于temp的数则重复上面的操作
# 直到没有大于temp的数,最终跳出循环
j = j - 1
# 在空出来的位置插入temp
arr[j + 1] = temp
插入排序的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。具体来说,插入排序每次取出一个未排序的元素,与已排序序列中的元素从后向前扫描比较,如果该元素(已排序)大于新元素,将该元素移到下一位置,重复该步骤,直到找到已排序的元素小于或者等于新元素的位置。接着将新元素插入到该位置后。重复以上步骤,直至所有元素都排序完毕。因此,插入排序的时间复杂度为O(n^2),但对于小规模数据的排序,插入排序具有稳定且高效的性能。
2、选择排序法
def select_sort(arr):
# 对n个数进行排序需要进行n-1次排序
for i in range(len(arr) - 1):
# 找到未排序部分中的最小元素
min_idx = i
for j in range(i+1, n):
if arr[j] < arr[min_idx]:
min_idx = j
# 将最小元素放到已排序部分的末尾
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr
选择排序的工作原理是每次从未排序的序列中选择最小(或最大)的一个元素,存放在已排序序列的末尾。具体来说,选择排序每次从未排序的序列中选择一个最小的元素,然后与已排序序列的最后一个元素交换位置。然后对未排序序列中剩下的元素重复上述步骤,直到未排序序列为空。选择排序的时间复杂度为O(n^2),是一种简单但效率较低的排序算法,也是最不稳定的排序算法。(演示代码是升序排列,将 '<' 改为 '>' 可变为降序)
3、冒泡排序法
def bubble_sort(arr):
for i in range(len(arr)):
# 每一轮冒泡将最大值放到最后
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
冒泡排序的工作原理是通过不断地比较相邻元素并交换顺序,将较大的元素逐步“浮”到数列的顶端。具体来说,冒泡排序会重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序错误就交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,即该元素列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。冒泡排序的时间复杂度是O(n^2),也是一种简单但效率较低的排序算法。
4、归并排序法
def merge_sort(arr):
# 归并排序递归结束条件:数组长度为1或0
if len(arr) <= 1:
return arr
# 将数组分成两部分
mid = len(arr) // 2
left_arr = arr[:mid]
right_arr = arr[mid:]
# 递归地对左右两部分进行归并排序
left_arr = merge_sort(left_arr)
right_arr = merge_sort(right_arr)
# 合并左右两部分
return merge(left_arr, right_arr)
def merge(left_arr, right_arr):
# 合并两个有序数组
result = []
i = j = 0
# 将左右两部分依次比较,将较小的元素添加到结果数组中
while i < len(left_arr) and j < len(right_arr):
if left_arr[i] < right_arr[j]:
result.append(left_arr[i])
i += 1
else:
result.append(right_arr[j])
j += 1
# 将剩余的元素添加到结果数组中
result.extend(left_arr[i:])
result.extend(right_arr[j:])
return result
归并排序(Merge Sort)是一种采用分治法的排序算法。它的基本思路是将两个或两个以上的有序表合并成一个新的有序表。
归并排序的工作原理如下:
- 分解:首先需要将待排序的数组分割成两个或多个小数组,直到每个小数组只有一个元素。
- 解决:然后使用归并操作,将两个小数组合并成一个有序的数组。合并的过程中,我们会比较两个数组的首元素,将较小的元素放入新的数组,并移动相应的指针。重复这个过程,直到一个数组的所有元素都被放入新数组,然后将另一个数组剩下的元素也放入新数组。
- 合并:最终,所有的小数组都会被合并成一个大的有序数组,这就是我们的归并排序结果。
归并排序的时间复杂度是O(nlogn),在处理大量数据时,归并排序的优势就明显体现出来了。但它采用用递归调用的方式来进行排序,在减少了时间复杂度的的同时,空间复杂度达到了O(n),因此更加的占用内存空间。归并排序是一种稳定排序算法,这意味着相同元素在排序后不会改变它们原有的前后顺序。
注释说明:
merge_sort
函数是归并排序的递归函数,它首先判断数组长度是否小于等于1,如果是,则直接返回数组本身,作为递归结束条件。否则,将数组分成两部分,递归地对左右两部分进行归并排序,并合并左右两部分。merge
函数用于合并两个有序数组。它首先创建一个空的结果数组,然后使用两个指针i
和j
分别指向左右两个数组的开头,依次比较左右两个数组中的元素,将较小的元素添加到结果数组中。最后,将剩余的元素添加到结果数组中,并返回结果数组。
归并排序对于前面的排序法而言更为复杂和难以理解,以下是一个归并排序过程的演示(以便更好地去理解该算法):
- 输入一个无序数组:
[5, 3, 8, 6, 2, 7, 1, 4]
- 递归地将数组分成两部分:
[5, 3, 8, 6]
和[2, 7, 1, 4]
- 再次递归地将两部分分别分成两小部分:
[5, 3]
和[8, 6]
,以及[2, 7]
和[1, 4]
- 继续递归地分,直到每个小部分只有一个元素为止:
[5]
和[3]
,以及[8]
和[6]
等 - 开始合并,从最细分的部分开始,依次向上合并:
[5, 3]
,[8, 6]
,[2, 7, 1, 4]
等 - 合并的过程中,将较小的元素移到右边,最后得到一个有序数组:
[1, 2, 3, 4, 5, 6, 7, 8]
5、堆排序法
def heapify(arr, n, i):
"""
以i为根节点,调整堆的结构
"""
largest = i # 初始化根节点最大
left = 2 * i + 1 # 左子节点
right = 2 * i + 2 # 右子节点
# 如果左子节点比根节点大,则更新最大值节点
if left < n and arr[left] > arr[largest]:
largest = left
# 如果右子节点比最大值节点大,则更新最大值节点
if right < n and arr[right] > arr[largest]:
largest = right
# 如果最大值节点不是根节点,则交换最大值节点和根节点的位置,继续调整堆
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heap_sort(arr):
"""
堆排序算法实现
"""
n = len(arr)
# 构建最大堆
for i in range(n//2 - 1, -1, -1):
heapify(arr, n, i)
# 从最后一个元素开始,将最大值放到末尾,再重新调整堆
for i in range(n-1, 0, -1):
arr[0], arr[i] = arr[i], arr[0] # 交换元素
heapify(arr, i, 0) # 重新调整堆
堆排序法的工作原理如下:
- 建堆:将待排序序列构造成一个大顶堆(或小顶堆)。这样,整个序列的最大值(或最小值)就是堆的根节点。将其与末尾元素进行交换,此时末尾就为最大值(或最小值)。然后将剩余n-1个元素重新构造成一个堆。这样,新的根节点就是次大(或次小)的值。重复步骤2,直到整个序列都已排序。
例如,以大顶堆为例,步骤如下:
- 首先将待排序序列建成一个大顶堆。
- 将堆顶元素(最大值)与堆底元素交换。
- 然后将除最后一个元素外的其它元素重新调整为大顶堆。
- 重复步骤2和3,直到整个序列都已排序。
堆排序法在处理大数据集时效率较高,其时间复杂度为O(nlogn),空间复杂度为O(1)。堆排序的时间复杂度与归并排序都是O(nlogn),并且不需要额外的内存空间。在大部分情况下,堆排序的性能都高于归并排序。只有在处理极大的数据量时,堆排序的效率会低于归并排序。堆排序不是稳定的算法,相同元素的相对顺序可能会改变。
注释说明:
heapify
函数是以某个节点为根节点,调整堆的结构,使其满足堆的性质(父节点的值大于或等于其子节点的值)。调整过程中,首先假设根节点最大,然后与其左右子节点比较,找出最大的节点,如果最大节点不是根节点,则交换根节点和最大节点的位置,然后递归地调整交换后的子树,使其仍然满足堆的性质。heap_sort
函数首先构建初始的最大堆(通过从中间节点开始向前遍历,对每个节点调用heapify
函数),然后通过交换堆顶元素(最大值)和当前未排序部分的最后一个元素,将最大值放到正确位置,然后对剩余未排序部分重新调整堆的结构,重复此过程,直到整个数组都有序。