排序算法在计算机科学中占有重要地位,不同的排序算法适用于不同的场景。本文将介绍几种常见的排序算法,并通过 Python 代码演示它们的实现。我们还将涵盖一些搜索算法,以展示在排序过程中如何查找元素。
顺序查找与二分查找
顺序查找:
顺序查找是一种简单的搜索方法,它从列表的第一个元素开始逐个检查,直到找到目标元素。在代码中,我们使用 for
循环遍历列表,如果找到目标元素,则输出其索引。
二分查找:
二分查找适用于已排序的列表。它通过反复将目标值与有序列表的中间元素进行比较,从而迅速缩小搜索范围。在代码中,我们使用 while
循环在有序列表中进行二分查找,直到找到目标元素或搜索范围缩小为空。
list = [1, 2, 5, 6, 8, 7, 9, 5, 46, 12, 3]
target = 7
# 顺序查找
for i in list: # 遍历列表中的每个元素
if i == target: # 如果元素等于目标值
print(list.index(i)) # 打印该元素在列表中的位置
# 二分查找
def binary_search(arr, target): # 定义一个名为binary_search的函数,接受一个列表和目标值作为参数
low = 0 # 定义查找范围的起始位置
high = len(arr) - 1 # 定义查找范围的结束位置
while low <= high: # 当查找范围仍然有效时
mid = (low + high) // 2 # 计算中间位置
guess = arr[mid] # 获取中间位置的元素值
if guess == target: # 如果中间元素等于目标值
return mid # 返回中间位置
if guess > target: # 如果中间元素大于目标值
high = mid - 1 # 更新查找范围的结束位置为中间位置的前一个位置
else:
low = mid + 1 # 否则,更新查找范围的起始位置为中间位置的后一个位置
return None # 如果未找到目标值,返回None
冒泡排序
冒泡排序是一种简单的比较排序算法,它多次遍历列表,每次比较相邻的两个元素,如果它们的顺序错误就交换它们。通过多次遍历,最大的元素会像气泡一样上浮到列表的末尾。在代码中,我们使用两个嵌套的 for
循环进行遍历和比较,通过交换元素的位置来排序。
def bubble_sort(lst): # 定义一个名为bubble_sort的函数,接受一个列表作为参数
for i in range(len(lst) - 1): # 外层循环,控制排序的轮数
for j in range(len(lst) - i - 1): # 内层循环,用于遍历当前未排序部分的元素
if lst[j] > lst[j + 1]: # 比较相邻的两个元素,如果前一个元素大于后一个元素
lst[j], lst[j + 1] = lst[j + 1], lst[j] # 交换这两个元素的位置
return lst # 返回已排序的列表
选择排序
简单选择排序:
简单选择排序是一种每次选择最小元素的排序算法。在代码中,我们使用 min
函数找到列表中的最小元素,将其添加到新列表,并从原列表中移除。通过重复这个过程,我们逐步构建有序的新列表。
选择排序:
选择排序是一种每次选择最小元素并放置到已排序部分末尾的排序算法。在代码中,我们使用两个嵌套的 for
循环,外层循环控制当前位置,内层循环找到未排序部分的最小元素并交换位置。
# 简单选择排序
def select_sort_simple(lst):
# 初始化一个空的新列表 new_list,用于存放排序后的元素
new_list = []
# 遍历输入列表 lst
for i in range(len(lst)):
# 在 lst 中找到最小值,并赋值给 min_val
min_val = min(lst)
# 将最小值添加到 new_list
new_list.append(min_val)
# 从 lst 中移除最小值
lst.remove(min_val)
# 返回排序后的新列表 new_list
return new_list
# 选择排序
def select_sort(lst):
# 遍历输入列表 lst,从索引 0 到 len(lst) - 2
for i in range(len(lst) - 1):
# 初始化最小值索引为当前索引 i
min_loc = i
# 遍历当前未排序部分的元素,从索引 i+1 到 len(lst) - 1
for j in range(i + 1, len(lst)):
# 如果发现有更小的元素,更新最小值索引 min_loc
if lst[j] < lst[min_loc]:
min_loc = j
# 将最小值与当前索引位置的值进行交换
lst[i], lst[min_loc] = lst[min_loc], lst[i]
# 返回排序后的列表
return lst
插入排序
插入排序:
插入排序是一种每次将一个元素插入到已排序部分的排序算法。在代码中,我们从列表的第二个元素开始,逐个将当前元素与已排序部分比较,找到合适的位置插入。通过重复这个过程,我们逐步构建有序的新列表。
def insert_sort(lst):
# 从列表的第二个元素开始(索引为1),因为第一个元素默认是有序的
for i in range(1, len(lst)):
# 将当前元素赋值给target
target = lst[i]
# 指向已排序部分的最后一个元素的前一个位置
j = i - 1
# 当目标值小于已排序部分的元素,并且已排序部分元素的位置不越界时,执行循环
while target < lst[j] and j >= 0:
# 将已排序部分的元素向后移动一位
lst[j + 1] = lst[j]
# 将j指针向前移动一位
j -= 1
# 在已排序部分中找到目标值的位置,并插入该值
lst[j + 1] = target
return lst # 返回已排序的列表
快速排序
快速排序是一种分而治之的排序算法,它选择一个基准元素,并将列表分为两部分,使得小于基准的元素在基准的左边,大于基准的元素在右边。递归地对左右两部分进行排序。在代码中,我们使用 partition
函数实现分区,并递归调用 quick_sort
函数。
def partition(lst, left, right):
# 将left索引处的元素赋值给tmp,作为pivot(基准)
tmp = lst[left]
# 当left < right时,执行循环
while left < right:
# 右指针开始时指向right索引,然后向左移动,直到找到一个比pivot小的元素或者右指针已经到了最左边
while left < right and lst[right] >= tmp:
right -= 1
# 将找到的元素赋值给left索引处
lst[left] = lst[right]
# 左指针开始时指向left索引,然后向右移动,直到找到一个比pivot大的元素或者左指针已经到了最右边
while left < right and lst[left] <= tmp:
left += 1
# 将找到的元素赋值给right索引处
lst[right] = lst[left]
# 将pivot(tmp)放到正确的位置上
lst[left] = tmp
# 返回left的新位置(pivot的位置)
return left
def quick_sort(lst, left, right):
# 如果left < right,执行以下操作
if left < right:
# 使用partition函数来获取pivot的位置,并将left和right调整为合适的范围
mid = partition(lst, left, right)
# 对左边的部分进行快速排序
quick_sort(lst, left, mid - 1)
# 对右边的部分进行快速排序
quick_sort(lst, mid + 1, right)
堆排序
堆排序利用堆这种数据结构来实现排序。在代码中,我们首先建立一个最大堆,然后反复将堆顶元素与当前最后一个元素交换,并调整堆,使得剩余部分仍然保持最大堆的性质。通过反复执行这个过程,我们完成排序。
# 堆排序算法中的堆化函数,用于维护最大堆的性质
def sift(lst, low, high):
i = low # 初始化i为low
j = 2 * i + 1 # j的初始值为左孩子的索引
tmp = lst[low] # 用tmp保存堆的根元素
# 当j小于等于high时,执行循环
while j <= high:
# 如果存在右孩子且右孩子的值大于左孩子的值,则将j设置为右孩子的索引
if j + 1 <= high and lst[j + 1] > lst[j]:
j = j + 1
# 如果当前元素大于tmp,则将当前元素和tmp交换,并更新i和j的值
if lst[j] > tmp:
lst[i] = lst[j]
i = j
j = 2 * i + 1
# 否则将tmp的值赋给当前位置,并跳出循环
else:
lst[i] = tmp
break
else:
lst[i] = tmp # 在所有孩子都被检查过后,将tmp的值赋给当前位置
# 堆排序函数,用于对列表进行排序
def heap_sort(lst):
n = len(lst) # 获取列表长度
# 从最后一个非叶子节点开始,向上遍历整个树,调用sift函数来维护最大堆的性质
for i in range(n // 2 - 1, -1, -1):
sift(lst, i, n - 1)
# 将堆的根节点(最大值)与最后一个元素交换,然后调整剩下的元素为最大堆
for i in range(n - 1, -1, -1):
lst[0], lst[i] = lst[i], lst[0] # 交换根节点和最后一个元素
sift(lst, 0, i - 1) # 调整剩下的元素为最大堆
return lst # 返回排序后的列表
内置堆排序
Python 提供了 heapq
模块,其中包含用于堆排序的相关功能。在代码中,我们使用 heapq.heapify
将列表转换为最小堆,然后通过反复调用 heapq.heappop
来获取最小元素,实现排序。
import heapq
list = [5,2,3,4,1,6,8,7,9]
heapq.heapify(list)
for i in range(len(list)):
print(heapq.heappop(list), end=" ")
归并排序
归并排序(Merge Sort)是一种经典的分治算法,通过递归地将数组拆分为更小的部分,然后再将已排序的部分合并起来。本文将详细介绍归并排序的实现及每一行代码的解释。
def merge(arr, low, mid, high):
"""
合并函数:将两个有序的子数组 arr[low:mid+1] 和 arr[mid+1:high+1] 合并成一个有序数组
Args:
arr: 待排序的数组
low: 左侧子数组的起始索引
mid: 中间索引,用于划分左右两个子数组
high: 右侧子数组的结束索引
Returns:
合并后的有序数组
"""
i = low # 初始化左侧数组的索引
j = mid + 1 # 初始化右侧数组的索引
new_arr = [] # 用于存放合并后的数组
# 比较左右两个数组的元素,按顺序合并到新数组中
while i <= mid and j <= high:
if arr[i] < arr[j]:
new_arr.append(arr[i])
i += 1
else:
new_arr.append(arr[j])
j += 1
# 处理左侧数组中剩余的元素
while i <= mid:
new_arr.append(arr[i])
i += 1
# 处理右侧数组中剩余的元素
while j <= high:
new_arr.append(arr[j])
j += 1
# 将合并后的数组复制回原数组对应的位置
arr[low:high + 1] = new_arr
return arr
def merge_sort(arr, low, high):
"""
归并排序函数:对数组 arr 进行归并排序
Args:
arr: 待排序的数组
low: 当前处理的子数组的起始索引
high: 当前处理的子数组的结束索引
Returns:
排序后的数组
"""
if low < high: # 至少有两个元素时进行排序
mid = (low + high) // 2 # 计算中间索引
# 递归地对左右两部分进行归并排序
merge_sort(arr, low, mid)
merge_sort(arr, mid + 1, high)
# 将两个有序的子数组合并
merge(arr, low, mid, high)
return arr
# 示例使用
my_list = [38, 27, 43, 3, 9, 82, 10]
result = merge_sort(my_list, 0, len(my_list) - 1)
print(result)
以上是一些常见的排序算法及其在 Python 中的实现。这些算法各有特点,适用于不同的场景。在实际应用中,选择合适的排序算法对于提高程序性能非常重要。希望本文对你理解排序算法及其实现有所帮助。