数据结构--排序及搜索算法

资料链接:https://pan.quark.cn/s/3957fd44d232
提取码:CCUs

一.冒泡排序

  • 冒泡排序是一种简单的排序算法,它重复地遍历待排序的列表,比较相邻的元素并交换它们的顺序,让较大的元素“冒泡”到列表的末端。
示例代码

def bubble_sort(arr):
    n = len(arr)
    # 外层循环控制遍历的轮数
    for i in range(n):
        # 内层循环进行相邻元素的比较与交换
        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

# 示例
if __name__ == "__main__":
    arr = [64, 34, 25, 12, 22, 11, 90]
    sorted_arr = bubble_sort(arr)
    print("排序后的列表:", sorted_arr)

代码说明:

  1. 外层循环:控制要进行的轮数,总共需要进行 n 轮比较。
  2. 内层循环:对未排序的部分进行相邻元素比较和交换,确保较大的元素最终“冒泡”到数组的右端。每次内层循环后,都会将当前最大的元素放到末端。
  3. 交换:如果当前元素比下一个元素大,就交换它们的位置。
  4. 返回排序后的数组。

冒泡排序的时间复杂度

  • 最坏情况下:O(n²),即原数组是逆序的情况下。
  • 最好情况下:O(n),即原数组已经是有序的情况下(可以通过设置一个标志变量来优化)。
  • 平均情况下:O(n²)。

尽管冒泡排序很简单,但它的效率较低,特别是对于大规模数据,通常不推荐使用。

二.选择排序

  • 选择排序是一种简单的排序算法,它的基本思想是不断地选择未排序部分中的最小(或最大)元素,然后将其放到已排序部分的末尾。
示例代码

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]
    return arr

# 示例
if __name__ == "__main__":
    arr = [64, 25, 12, 22, 11]
    sorted_arr = selection_sort(arr)
    print("排序后的列表:", sorted_arr)

代码说明:

  1. 外层循环:控制已排序部分的元素位置。
  2. 内层循环:从未排序部分中寻找最小的元素。
  3. 最小值索引:在内层循环中,如果当前元素小于当前的最小值,则更新最小值的索引。
  4. 交换:在内层循环完成后,将找到的最小值与已排序部分的下一个位置的元素进行交换。

选择排序的时间复杂度

  • 最坏情况下:O(n²),无论原数组是否有序。
  • 最好情况下:O(n²)。
  • 平均情况下:O(n²)。

因为选择排序的比较次数与输入数组的排列方式无关,所以它在复杂度上比较稳定,但效率较低,通常不适合大规模数据的排序。

下面是对三种常见排序算法——快速排序、归并排序和插入排序的详细说明,包括它们的工作原理、优缺点及时间复杂度。

三. 快速排序(Quick Sort)

工作原理:
  • 分治法:快速排序采用分治法,通过一个基准元素(pivot)将待排序数组分为两部分。
  • 步骤
    1. 从数组中选择一个基准元素(常见选择为中间元素)。
    2. 将数组重新排列,使得所有小于基准元素的值位于其左侧,所有大于基准元素的值位于其右侧。此时,基准元素的位置就是经过排序之后的位置。
    3. 对左侧和右侧的两个子数组递归应用上述步骤,直到数组被完全排序。
示列代码:

def quick_sort(arr):  
    if len(arr) <= 1:  
        return arr  
    pivot = arr[len(arr) // 2]  # 选择基准元素  
    left = [x for x in arr if x < pivot]  # 小于基准的元素  
    middle = [x for x in arr if x == pivot]  # 等于基准的元素  
    right = [x for x in arr if x > pivot]  # 大于基准的元素  
    return quick_sort(left) + middle + quick_sort(right)  

# 示例  
if __name__ == "__main__":  
    arr = [64, 34, 25, 12, 22, 11, 90]  
    sorted_arr = quick_sort(arr)  
    print("排序后的列表:", sorted_arr)
优缺点:
  • 优点

    • 平均情况下性能良好,尤其适合大数据集。
    • 原地排序,空间复杂度较低(O(log n))。
  • 缺点

    • 最坏情况下时间复杂度为O(n²),例如,选择的基准元素恰好是最大或最小值。
    • 不稳定排序,不保持相等元素的相对位置。
时间复杂度:
  • 最坏情况下:O(n²)
  • 平均情况下:O(n log n)
  • 最好情况下:O(n log n)

四. 归并排序(Merge Sort)

工作原理:
  • 分治法:归并排序也采用分治法,将数组分成两半,递归进行排序,然后合并已排序的部分。
  • 步骤
    1. 将数组分成两个子数组,直到每个子数组只有一个元素(单个元素是有序的)。
    2. 合并两个子数组,将它们合并成已排序数组。
    3. 递归应用上述合并过程,直到所有元素归并成一个有序数组。
def merge_sort(arr):  
    if len(arr) <= 1:  
        return arr  
    mid = len(arr) // 2  
    left = merge_sort(arr[:mid])    # 左半部分  
    right = merge_sort(arr[mid:])   # 右半部分  

    return merge(left, right)  

def merge(left, right):  
    result = []  
    i = j = 0  
    # 合并两个已排序的数组  
    while i < len(left) and j < len(right):  
        if left[i] < right[j]:  
            result.append(left[i])  
            i += 1  
        else:  
            result.append(right[j])  
            j += 1  
    result += left[i:]  # 添加剩余元素  
    result += right[j:]  
    return result  

# 示例  
if __name__ == "__main__":  
    arr = [64, 34, 25, 12, 22, 11, 90]  
    sorted_arr = merge_sort(arr)  
    print("排序后的列表:", sorted_arr)
优缺点:
  • 优点

    • 稳定排序,能保持相同元素的相对位置。
    • 在最坏情况下也能达到O(n log n)的时间复杂度,不受数据初始顺序的影响。
  • 缺点

    • 需要额外的空间来存放合并的子数组,空间复杂度为O(n)。
    • 在小数据集下性能不如插入排序。
时间复杂度:
  • 最坏情况下:O(n log n)
  • 平均情况下:O(n log n)
  • 最好情况下:O(n log n)

五. 插入排序(Insertion Sort)

工作原理:
  • 逐步构建有序序列:插入排序通过维护一个已排序的部分,并将未排序的元素逐一插入到已有序列中。
  • 步骤
    1. 从第二个元素开始,将当前元素(key)与已排序部分进行比较。
    2. 如果当前元素小于已排序的元素,则将已排序的元素向右移动一位,直到找到合适的位置插入当前元素。
    3. 继续对未排序的元素重复这一过程,直到所有元素都有序。
示列代码:

def insertion_sort(arr):  
    for i in range(1, len(arr)):  
        key = arr[i]  # 当前要插入的元素  
        j = i - 1  
        # 将已排序部分中比 key 大的元素向右移动  
        while j >= 0 and arr[j] > key:  
            arr[j + 1] = arr[j]  
            j -= 1  
        arr[j + 1] = key  # 插入 key 元素  
    return arr  

# 示例  
if __name__ == "__main__":  
    arr = [64, 34, 25, 12, 22, 11, 90]  
    sorted_arr = insertion_sort(arr)  
    print("排序后的列表:", sorted_arr)
    
优缺点:
  • 优点

    • 简单直观,容易实现。
    • 对于小规模的数组或部分已排序数据时效率较高。
    • 稳定排序,保持相同元素相对位置。
  • 缺点

    • 时间复杂度较高,尤其对于大型数据集,表现不佳。
    • 不适合大型规模的数据,因为时间复杂度为O(n²)。
时间复杂度:
  • 最坏情况下:O(n²)
  • 平均情况下:O(n²)
  • 最好情况下:O(n)(当输入数据基本有序时)

顺序搜索和二分搜索是两种基本的搜索算法,各自适用于不同的场合。以下是它们的详细介绍:

六. 顺序搜索(Linear Search)

工作原理:
  • 顺序搜索可以在未排序的列表中查找目标元素。
  • 步骤
    1. 从列表的第一个元素开始,逐个比对每个元素与目标值。
    2. 如果找到与目标值相等的元素,则返回该元素的位置。
    3. 如果遍历完整个列表仍未找到目标值,返回“未找到”的标志。
代码示列:

def linear_search(arr, target):  
    for index, element in enumerate(arr):  
        if element == target:  
            return index  # 返回目标值的索引  
    return -1  # 返回未找到标志  

# 示例用法  
array = [5, 3, 8, 4, 2]  
target = 4  
result = linear_search(array, target)  

if result != -1:  
    print(f"目标值 {target} 在索引 {result} 处找到。")  
else:  
    print(f"目标值 {target} 未找到。")
    
优缺点:
  • 优点

    • 实现简单,无需额外的数据结构。
    • 不需要预先对数据进行排序,适用于任何类型的集合。
  • 缺点

    • 时间复杂度是O(n),效率较低,特别是数据规模较大时。
时间复杂度:
  • 最坏情况下:O(n)
  • 平均情况下:O(n)
  • 最好情况下:O(1)(当目标值为第一个元素时)

七. 二分搜索(Binary Search)

工作原理:
  • 二分搜索是一种高效的搜索算法,仅适用于已排序的列表。
  • 步骤
    1. 确定当前搜索范围的中间元素。
    2. 如果中间元素等于目标值,则返回该位置。
    3. 如果目标值小于中间元素,则在左半部分继续搜索。
    4. 如果目标值大于中间元素,则在右半部分继续搜索。
    5. 重复上述步骤,直到找到目标值或搜索范围为空。
def binary_search(arr, target):  
    left = 0  
    right = len(arr) - 1  

    while left <= right:  
        mid = left + (right - left) // 2  # 计算中间索引  
        if arr[mid] == target:  
            return mid  # 返回目标值的索引  
        elif arr[mid] < target:  
            left = mid + 1  # 在右半部分继续搜索  
        else:  
            right = mid - 1  # 在左半部分继续搜索  
    
    return -1  # 返回未找到标志  

# 示例用法  
sorted_array = [2, 3, 4, 5, 8]  
target = 5  
result = binary_search(sorted_array, target)  

if result != -1:  
    print(f"目标值 {target} 在索引 {result} 处找到。")  
else:  
    print(f"目标值 {target} 未找到。")
优缺点:
  • 优点

    • 效率高,时间复杂度为O(log n),适合大量数据的搜索。
    • 每次搜索都能显著缩小查找范围。
  • 缺点

    • 需对数据进行排序,适用性有限。
    • 实现较顺序搜索复杂。
时间复杂度:
  • 最坏情况下:O(log n)
  • 平均情况下:O(log n)
  • 最好情况下:O(1)(当目标值为中间元素时)

总结

  • 使用场景
    • 顺序搜索:适用于小规模或未排序数据。
    • 二分搜索:适用于大规模的已排序数据,使用效率更高。

八.补充—常用的取整方式:

在Python中,常用的取整方式主要有以下几种:

  1. 向下取整(floor)
    使用 math.floor() 函数可以向下取整到最接近的整数。

    import math
    
    num = 3.7
    result = math.floor(num)  # result = 3
    
  2. 向上取整(ceil)
    使用 math.ceil() 函数可以向上取整到最接近的整数。

    import math
    
    num = 3.1
    result = math.ceil(num)  # result = 4
    
  3. 四舍五入(round)
    使用内置的 round() 函数进行四舍五入。

    num = 3.5
    result = round(num)  # result = 4
    
    num = 3.4
    result = round(num)  # result = 3
    
  4. 转换为整数(int)
    使用 int() 进行取整(会向零取整)。

    num = 3.7
    result = int(num)  # result = 3
    
    num = -3.7
    result = int(num)  # result = -3
    
  5. 取整除法(//)
    使用 // 进行取整除法。

    num1 = 7
    num2 = 3
    result = num1 // num2  # result = 2
    
    num1 = -7
    num2 = 3
    result = num1 // num2  # result = -3
    
  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值