目录
冒泡排序
冒泡排序是一种简单的比较排序算法,其核心思想是不断地比较相邻的两个元素并交换它们的位置,将较大(或较小)的元素逐渐“浮”到数组的一端(冒泡到顶部或底部),从而实现排序。冒泡排序的名字来源于像气泡一样从底部冒到顶部或相反。
def bubble_sort(arr):
n = len(arr)
# 外层循环控制需要比较的轮数
for i in range(n):
# 标记是否发生过交换,如果某一轮没有发生交换,则说明数组已经有序
swapped = False
# 内层循环比较相邻元素并交换
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j] # 交换位置
swapped = True
# 如果这一轮没有发生交换,说明数组已经有序,可以提前退出
if not swapped:
break
# 示例
arr = [64, 34, 25, 12, 22, 11, 90]
bubble_sort(arr)
print("排序后的数组:", arr)
冒泡排序是稳定的排序,上述示例中,外层循环控制需要比较的轮数,内层循环用于比较相邻元素并进行交换。每轮内层循环都将较大的元素“冒泡”到数组的末尾,因此排序完成后,数组中的元素按升序排列。冒泡排序的时间复杂度为O(n^2),不适合大规模数据排序,但是它是一种容易理解和实现的排序算法。
选择排序
一种简单的排序算法,其核心思想是不断地选择最小(或最大)的元素并将其放置在已排序部分的末尾。这个过程不断重复,直到整个数组被排序完成。
以下是选择排序的实现步骤:
- 在未排序部分中找到最小(或最大)的元素。
- 将找到的最小(或最大)元素与未排序部分的第一个元素交换位置。
- 将已排序部分的末尾扩展,未排序部分减小。
- 重复步骤1至3,直到整个数组被排序。
def selection_sort(arr):
n = len(arr)
for i in range(n):
# 假设当前位置的元素是最小的
min_index = i
# 在未排序部分找到最小的元素
for j in range(i + 1, n):
if arr[j] < arr[min_index]:
min_index = j
# 将找到的最小元素与当前位置交换
arr[i], arr[min_index] = arr[min_index], arr[i]
# 示例
arr = [64, 34, 25, 12, 22, 11, 90]
selection_sort(arr)
print("排序后的数组:", arr)
选择排序不断地在未排序部分中寻找最小的元素,并将其交换到已排序部分的末尾。通过重复这个过程,数组逐渐被排序。选择排序的时间复杂度为O(n^2),因此它不适合用于大规模数据的排序,但它是一种容易理解和实现的排序算法。
选择排序是不稳定的算法,例如43,89,21,43,28,15,经过选择第一遍排序时候会将第一个43跟最后15对调位置,原来第一个43位置放到第二个43的后面,而后面的排序步骤也不会修改两个43的相对顺序。
插入排序
逐步将未排序的元素插入到已排序的部分中,直到整个数组都有序。插入排序是一种稳定排序。
- 从第二个元素开始,将其与前面的已排序部分进行比较。
- 将未排序元素逐步向前移动,直到找到合适的位置插入,使得插入后的部分仍然保持有序。
实现步骤:
- 从第二个元素开始,将当前元素保存为"待插入元素"。
- 与已排序部分的最后一个元素进行比较,如果待插入元素比最后一个元素小,则将最后一个元素向后移动,为待插入元素腾出位置。
- 继续与前一个已排序元素比较,直到找到合适的位置,插入待插入元素。
- 重复以上步骤,直到所有元素都有序。
def insertion_sort(arr):
for i in range(1, len(arr)): # 从第二个元素开始
current_element = arr[i] # 待插入的元素
j = i - 1 # 已排序部分的最后一个元素的索引
while j >= 0 and current_element < arr[j]:
arr[j + 1] = arr[j] # 将较大的元素向后移动
j -= 1
arr[j + 1] = current_element # 插入待插入元素到正确的位置
# 测试插入排序
arr = [5, 2, 9, 3, 6]
insertion_sort(arr)
print("排序后的数组:", arr)
插入排序是一种稳定的排序算法,适用于小型数组或部分有序的数组。它的时间复杂度为O(n^2),因此在大型数据集上不太高效,但对于小规模数据集来说,它是一个简单而有效的排序算法。
快速排序
思想:分治法,即将一个大问题分割成若干个小问题,解决小问题,最终将小问题的解整合起来获得大问题的解。
快速排序的基本思想可以概括为以下步骤:
- 选择基准元素(Pivot):从待排序的数组中选择一个基准元素。通常选择第一个元素、最后一个元素或者随机选择一个元素作为基准。
- 分割阶段:将数组中的元素分为两部分,一部分小于或等于基准元素,一部分大于基准元素。这个过程称为分割(Partitioning)。
- 递归排序:分割阶段之后,将分割得到的两个子数组分别进行快速排序,这是递归的过程。递归的终止条件是子数组的大小为1或0,因为已经有序。
- 合并:在递归排序结束后,就可以将子数组合并在一起,得到完整的有序数组。
快速排序是一种高效的排序算法,平均情况下的时间复杂度为O(n log n),最坏情况下为O(n^2),但最坏情况很少发生,通常情况下表现非常出色。快速排序不需要额外的存储空间,是一种原地排序算法。需要注意的是,快速排序的性能与选取的基准元素有关,如果选择的基准元素不合适,可能导致性能下降。因此,一些变种的快速排序算法对基准元素的选择进行了优化。
下面是以Python实现的代码:
def quick_sort(arr):
if len(arr) <= 1:
return arr
base_num = arr[0] # 选择第一个元素作为基准
less_than_base = [x for x in arr[1:] if x <= base_num]
greater_than_base = [x for x in arr[1:] if x > base_num]
return quick_sort(less_than_base) + [base_num] + quick_sort(greater_than_base)
# 示例用法
arr = [3, 6, 8, 10, 1, 2, 9]
sorted_arr = quick_sort(arr)
print(sorted_arr)
快速排序通常是不稳定的排序算法。 例如43,89,21,43,28,15, 选择第一个43位基准,经过第一次分组,对于<=43放到左边组,>43组放到右边组,整组变为[21,43,28,15]+[43]+[89]。 可以看出第一个43已经放到后面的位置,所以是不稳定的算法。
归并排序
基于分治思想的排序算法,它的主要思想是将一个数组分割成若干子数组,分别对子数组进行排序,然后将它们合并为一个有序数组。以下是归并排序的思想:
- 将原始数组分割成若干子数组,直到每个子数组都只包含一个元素,这些单元素数组可以看作是有序的。
- 逐步合并相邻的子数组,同时保持合并后的子数组有序,一直重复这个过程,直到整个数组都有序。
实现步骤:
- 分割:将原始数组分成两个子数组,递归地将这两个子数组分割成更小的子数组,直到子数组中只包含一个元素。
- 合并:将两个有序的子数组合并为一个更大的有序数组,重复这一过程直到所有子数组都合并完成。
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]
merge_sort(left_half) # 递归地对左半部分排序
merge_sort(right_half) # 递归地对右半部分排序
i = j = k = 0
# 合并两个子数组
while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1
# 检查是否有剩余的元素
while i < len(left_half):
arr[k] = left_half[i]
i += 1
k += 1
while j < len(right_half):
arr[k] = right_half[j]
j += 1
k += 1
# 测试归并排序
arr = [38, 27, 43, 3, 9, 82, 10]
merge_sort(arr)
print("排序后的数组:", arr)
归并排序是稳定的,上述代码演示了如何使用归并排序对整数数组进行排序。它递归地将数组分割成两个子数组,然后合并这些子数组,保持它们有序。运行示例代码后,您将看到排序后的数组。归并排序是一种稳定的排序算法,它的时间复杂度为O(n log n),适用于大型数据集的排序。
堆排序
堆排序(Heap Sort)是一种高效的排序算法,它的思想基于堆数据结构。堆是一种特殊的树形数据结构,具有以下特点:
- 堆是一个完全二叉树。
- 每个节点的值大于或等于(最大堆)或小于或等于(最小堆)其子节点的值。
堆排序的基本思想是将待排序的元素构建成一个堆,然后逐步将堆顶元素取出,再重新调整堆,不断重复这个过程,直到整个数组排序完成。以下是堆排序的实现步骤:
- 构建最大堆:将待排序数组视为一个完全二叉树,从最后一个非叶子节点开始,依次向前构建最大堆。
- 排序:不断将堆顶元素(最大值)与数组末尾元素交换,然后将数组范围缩小,重新调整堆,直到数组完全有序。
def heapify(arr, n, 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[i], arr[0] = arr[0], arr[i] # 交换堆顶元素和当前元素
heapify(arr, i, 0) # 调整剩余元素的堆
# 测试堆排序
arr = [38, 27, 43, 3, 9, 82, 10]
heap_sort(arr)
print("排序后的数组:", arr)
堆排序是一种不稳定的排序算法,其时间复杂度为O(n log n),适用于大型数据集的排序。