排序算法的优劣,以及优化思路

news2024/12/23 13:45:31

文章目录

      • 1. 冒泡排序
      • 2. 选择排序
      • 3. 插入排序
      • 4. 快速排序
      • 5. 归并排序
      • 6. 堆排序
      • 应用案例:对一个随机生成的整数列表进行排序
        • 基础快速排序
        • 快速排序优化:三数取中法
      • 性能测试
      • 非递归快速排序实现
      • 基础归并排序
      • 归并排序的空间优化尝试
      • 自底向上归并排序实现
      • 堆排序简介
      • 基础堆排序实现
      • 堆排序的优化思路
      • 结论

排序算法的优劣主要可以从时间复杂度、空间复杂度、稳定性、及是否原地排序这几个方面来考量。不同的排序算法适用于不同的应用场景,下面是一些常见排序算法的特点及优化思路:

1. 冒泡排序

  • 优点:实现简单,稳定排序。
  • 缺点:时间复杂度最坏情况下为O(n^2),效率低。
  • 优化思路
    • 提前终止:设置一个标志位,若在一次遍历中没有发生交换,则说明数组已经有序,可提前结束。
    • 记录最后一次交换的位置:下一轮遍历只需到该位置即可,因为之后的已经是有序的。

2. 选择排序

  • 优点:实现简单,不依赖于初始序列。
  • 缺点:时间复杂度为O(n^2),效率低下。
  • 优化思路:选择排序的内在机制决定了其优化空间有限,主要是通过减少比较和交换的次数来微调,但本质上的时间复杂度无法改变。

3. 插入排序

  • 优点:对部分有序的数据排序效率高,最好情况时间复杂度为O(n)。
  • 缺点:最坏情况时间复杂度为O(n^2)。
  • 优化思路
    • 二分查找插入位置:减少寻找插入位置的时间,但整体时间复杂度仍为O(n^2)。
    • 希尔排序:通过增量分组进行插入排序,可以看作是插入排序的一种优化,能显著提高效率。

4. 快速排序

  • 优点:平均时间复杂度为O(n log n),效率高,且是原地排序。
  • 缺点:最坏情况下时间复杂度为O(n^2),且不稳定。
  • 优化思路
    • 三数取中法:选取基准时,从首、中、尾三个数中选择中位数作为基准,减少最坏情况发生的概率。
    • 小数组时切换到插入排序:对于小数组,快速排序的优势不明显,可以切换到插入排序。
    • 尾递归优化:减少递归调用栈的深度。

5. 归并排序

  • 优点:稳定排序,时间复杂度始终为O(n log n)。
  • 缺点:需要额外的存储空间,空间复杂度为O(n)。
  • 优化思路
    • 自底向上归并排序:避免递归调用,减少函数调用开销。
    • 减少内存使用:可以通过原地归并的思想(如循环替代拷贝)尝试降低空间复杂度,但这会增加算法的实现难度。

6. 堆排序

  • 优点:原地排序,时间复杂度为O(n log n)。
  • 缺点:不稳定排序,构建和调整堆的过程较为复杂。
  • 优化思路:由于堆排序的核心操作(建堆、调整堆)已经相对高效,优化空间不大,主要集中在减少比较和交换的操作上,如利用位运算加速比较和交换过程。

总的来说,排序算法的选择和优化需根据具体的应用场景来决定,理解每种算法的特性和限制,才能更好地做出选择。在实际应用中,也常结合多种排序算法,利用各自的优势,以达到最佳的排序效果。

让我们通过一个具体的应用案例来展示如何使用排序算法,并尝试进行一些基本的优化。这里我们以快速排序为例,因为它是一种广泛应用且效率较高的排序算法。我们将使用Python语言编写代码,并实现“三数取中法”优化策略。

应用案例:对一个随机生成的整数列表进行排序

基础快速排序

首先,我们从基础的快速排序开始:

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)

# 生成随机整数列表
import random
arr = [random.randint(0, 100) for _ in range(20)]
print("Original array:", arr)
sorted_arr = quick_sort(arr)
print("Sorted array:", sorted_arr)
快速排序优化:三数取中法

接下来,我们加入“三数取中法”来优化选取基准值的过程,以减少最坏情况发生的概率:

def partition(arr, low, high):
    # 三数取中找到更好的pivot
    mid = (low + high) // 2
    if arr[low] > arr[mid]: arr[low], arr[mid] = arr[mid], arr[low]  
    if arr[low] > arr[high]: arr[low], arr[high] = arr[high], arr[low]
    if arr[mid] > arr[high]: arr[mid], arr[high] = arr[high], arr[mid]
    pivot = arr[mid]
    i = low - 1
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i + 1

def quick_sort_optimized(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quick_sort_optimized(arr, low, pi - 1)
        quick_sort_optimized(arr, pi + 1, high)

# 调用优化后的快速排序
arr = [random.randint(0, 100) for _ in range(20)]
quick_sort_optimized(arr, 0, len(arr) - 1)
print("Optimized Sorted array:", arr)

在这个优化版本中,我们通过partition函数实现了三数取中法来选取更佳的基准值,进而提高了排序的效率和稳定性。注意,这里的快速排序实现是递归的,为了简化示例,没有包含尾递归优化等进一步的高级优化技术。在实际应用中,根据具体需求选择合适的优化策略是非常重要的。

除了实现和优化快速排序算法之外,我们还可以讨论如何测试这些排序函数的性能,以及如何进一步提升代码的实用性和效率。下面,我将向您展示如何进行性能测试并引入一种非递归的方式来实现快速排序,以减少递归调用带来的开销。

性能测试

在Python中,可以使用timeit模块来简单测试函数的执行时间,从而对比不同版本快速排序的性能。

import timeit

# 测试基础快速排序
setup_code = """
from __main__ import quick_sort, quick_sort_optimized, arr
"""
test_code_quick_sort = """
quick_sort(arr.copy())
"""

test_code_quick_sort_optimized = """
quick_sort_optimized(arr.copy(), 0, len(arr) - 1)
"""

times_quick_sort = timeit.timeit(setup=setup_code, stmt=test_code_quick_sort, number=100)
times_quick_sort_optimized = timeit.timeit(setup=setup_code, stmt=test_code_quick_sort_optimized, number=100)

print(f"Time taken by basic quick sort: {times_quick_sort} seconds")
print(f"Time taken by optimized quick sort: {times_quick_sort_optimized} seconds")

非递归快速排序实现

为了减少递归调用栈的深度,我们可以使用栈来模拟递归过程,实现非递归版的快速排序。

def quick_sort_non_recursive(arr):
    stack = [(0, len(arr) - 1)]
    while stack:
        low, high = stack.pop()
        if low < high:
            pi = partition(arr, low, high)
            stack.extend([(low, pi - 1), (pi + 1, high)])
    return arr

# 测试非递归快速排序
arr = [random.randint(0, 100) for _ in range(2000)]  # 使用更大的数组测试性能
times_quick_sort_non_recursive = timeit.timeit(setup=setup_code.replace('quick_sort_optimized', 'quick_sort_non_recursive'), stmt="quick_sort_non_recursive(arr.copy())", number=100)

print(f"Time taken by non-recursive quick sort: {times_quick_sort_non_recursive} seconds")

通过上述代码,我们不仅实现了快速排序的几种变体,还学习了如何进行基本的性能测试,并探索了减少递归调用以优化性能的方法。实际开发中,选择哪种实现方式还需根据具体应用场景和数据规模来决定。

在前面的内容中,我们讨论了快速排序的实现、优化方法、性能测试以及非递归版本的快速排序。接下来,我们探讨如何在Python中实现另一种高效的排序算法——归并排序,并对其进行简单的优化。归并排序是一种稳定的排序算法,时间复杂度始终为O(n log n),但其缺点在于需要额外的存储空间。

基础归并排序

首先,我们从基础的归并排序开始实现:

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        L = arr[:mid]
        R = arr[mid:]

        merge_sort(L)
        merge_sort(R)

        i = j = k = 0

        # 合并过程
        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i += 1
            else:
                arr[k] = R[j]
                j += 1
            k += 1

        # 检查是否有遗漏
        while i < len(L):
            arr[k] = L[i]
            i += 1
            k += 1

        while j < len(R):
            arr[k] = R[j]
            j += 1
            k += 1

# 测试归并排序
arr = [random.randint(0, 100) for _ in range(20)]
merge_sort(arr)
print("Merge sorted array:", arr)

归并排序的空间优化尝试

归并排序的主要缺点是需要额外的存储空间来合并子数组。尽管完全消除这部分空间开销很难,但可以通过以下方式尝试减少空间使用:

  • 原地归并思想:对于小数组,可以尝试直接在原数组上进行操作,但这通常会使算法实现变得非常复杂,且可能破坏稳定性。
  • 循环代替递归:实现自底向上的归并排序,通过循环而非递归来减少递归调用栈的深度,虽然这不直接减少空间复杂度,但可以避免深度递归导致的栈溢出问题。

自底向上归并排序实现

下面是一个自底向上归并排序的实现示例,它减少了递归的使用:

def merge_sort_bottom_up(arr):
    size = 1
    while size < len(arr):
        for start in range(0, len(arr), 2*size):
            mid = min(start + size, len(arr))
            end = min(start + 2*size, len(arr))
            merged = []
            left, right = start, mid
            while left < mid and right < end:
                if arr[left] < arr[right]:
                    merged.append(arr[left])
                    left += 1
                else:
                    merged.append(arr[right])
                    right += 1
            merged.extend(arr[left:mid])
            merged.extend(arr[right:end])
            arr[start:end] = merged
        size *= 2

# 测试自底向上归并排序
arr = [random.randint(0, 100) for _ in range(20)]
merge_sort_bottom_up(arr)
print("Bottom-up merge sorted array:", arr)

这个自底向上的归并排序版本,虽然在空间复杂度上并没有实质性的降低(依然为O(n)),但它通过减少递归调用来提高了算法的实用性,尤其是在处理大规模数据集时,可以有效避免栈溢出的问题。

在之前的讨论中,我们详细介绍了快速排序和归并排序的实现及其优化思路。现在,让我们转向另一种常用的排序算法——堆排序,并探讨它的实现与优化策略。

堆排序简介

堆排序是一种基于比较的排序算法,利用完全二叉树(通常是最大堆或最小堆)的性质来排序数组。它的时间复杂度为O(n log n),并且是原地排序算法,但不是稳定的排序算法。堆排序分为两个主要步骤:构建最大堆和调整堆结构进行排序。

基础堆排序实现

首先,我们实现基本的堆排序算法:

def heapify(arr, n, i):
    largest = i  # 初始化最大元素为根
    l = 2 * i + 1     # 左 = 2*i + 1
    r = 2 * i + 2     # 右 = 2*i + 2

    # 如果左子节点大于根
    if l < n and arr[l] > arr[largest]:
        largest = l

    # 如果右子节点大于当前最大
    if r < n and arr[r] > arr[largest]:
        largest = r

    # 如果最大元素不是根
    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 = [random.randint(0, 100) for _ in range(20)]
heap_sort(arr)
print("Heap sorted array:", arr)

堆排序的优化思路

堆排序本身作为一种高效的排序算法,在实现上已经相对精简,但仍有几点潜在的优化方向:

  1. 减少交换操作:在调整堆的过程中,可以直接通过临时变量暂存最大值,而不是频繁交换元素。
  2. 原地构建堆:在构建初始堆时,可以采用自下而上的方法,从最后一个非叶子节点开始,逐层向上进行下沉操作,这样可以减少遍历次数。
  3. 并行化处理:在大型数据集上,可以考虑将堆的构建和调整过程并行化,利用多核处理器的优势,但这会显著增加实现的复杂度。

结论

堆排序因其原地性、时间复杂度稳定在O(n log n)而被广泛应用于各种场合,尤其是在内存敏感的环境中。尽管其本身已经相当高效,但通过上述优化思路,可以在特定场景下进一步提升其性能。在实际应用中,选择合适的排序算法应综合考虑数据特性、空间限制以及对稳定性的需求。

😍😍 大量H5小游戏、微信小游戏、抖音小游戏源码😍😍
😍😍试玩地址: https://www.bojiogame.sg😍😍
😍看上哪一款,需要源码的csdn私信我😍

————————————————

​最后我们放松一下眼睛
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1860508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深圳比创达电子EMC|EMI电磁干扰行业:提升电子产品质量的关键

随着电子技术的飞速发展&#xff0c;电磁干扰&#xff08;EMI&#xff09;问题日益凸显&#xff0c;成为影响电子产品性能和市场竞争力的重要因素。 一、EMI电磁干扰行业的概述 电磁干扰&#xff0c;即电子设备在运行过程中产生的电磁波对其他设备或系统产生的干扰。这种干扰…

基于决策树的旋转机械故障诊断(Python)

前置文章&#xff1a; 将一维机械振动信号构造为训练集和测试集&#xff08;Python&#xff09; https://mp.weixin.qq.com/s/DTKjBo6_WAQ7bUPZEdB1TA 旋转机械振动信号特征提取&#xff08;Python&#xff09; https://mp.weixin.qq.com/s/VwvzTzE-pacxqb9rs8hEVw import…

AI大模型的制造业场景,一文读懂

摘要 AI大模型是通用人工智能发展的重要里程碑。中央会议关于人工智能发展提出三个第一次&#xff1a;第一次提出“通用人工智能”&#xff0c;第一次提出“产业智能化”&#xff0c;第一次提出“把握人工智能等新科技革命浪潮”。 这一轮AI所体现“革命性”特征&#xff0c;不…

防止多次点击,vue的按钮上做简易的防抖节流处理

话不多说,上个视频,看看是不是你要的效果 防抖节流 1.创建一个directive.js // directive.js export default {install(Vue) {// 防重复点击(指令实现)Vue.directive(repeatClick, {inserted(el, binding) {el.addEventListener(click, () > {if (!el.disabled) {el.disabl…

利用LabVIEW和机器学习实现无规律物体识别

针对变化无规律的物体识别&#xff0c;LabVIEW结合机器学习算法提供了一种高效的解决方案。介绍如何使用LabVIEW编程实现此功能&#xff0c;包括所需工具包、算法选择和实现步骤&#xff0c;帮助开发者在无规律的复杂环境中实现高精度的物体识别。 1. 项目概述 无规律物体的识…

YOLOv8模型代码学习

1.参考文献 链接1 2.网络模型解析 2.1卷积神经单元&#xff08;conv.py&#xff09; 在该文件中定义了yolov8网络中的卷积神经单元&#xff0c;位置如图所示。 def autopad(k, pNone, d1): # kernel(卷积核), padding(填充), dilation(扩张)"""Pad to same…

PLSQL下ORA-00904:

plsql下查看执行计划报错:ORA-00904:“OTHER_XML”:标识符无效 解决办法: 1.报错的用户登录到sqlplus,执行以下命令: sql> drop table PLAN_TABLE;2.创建表: sql> ?/rdbms/admin/utlxplan.sql; Table created.重新登录plsql按F5选择text: 报错问题解决.

苹果电脑压缩pdf文件,苹果电脑里如何压缩pdf文件

压缩PDF文件是现代办公和日常生活中经常需要处理的一项任务&#xff0c;无论是为了节省存储空间、方便网络传输&#xff0c;还是为了在移动设备上更流畅地阅读文档&#xff0c;学会有效地压缩PDF都显得尤为重要。在本文中&#xff0c;我们将详细探讨压缩PDF的方法&#xff0c;从…

解码数智升级良方:中国一拖、中原传媒、神火股份等企业数字化实践分析

大模型、AI等技术的成熟以及政策法规的细化&#xff0c;数据资源的权属论证、合规确权、资产论证等环节逐渐走向实用性、价值化。 而伴随着“业财税数融”综合性数字化成为企业数字化转型的主流选择&#xff0c;财务部门的纽带属性被放大&#xff0c;财务数据的融合能力成为企业…

【华为HCIA数通网络工程师真题-构建以太网交换网络】

华为HCIA数通网络工程师真题-构建以太网交换网络 一、1-10题 一、1-10题 1、如图所示&#xff0c;四台交换机都运行 STP&#xff0c;各种参数都采用默认值如果交换机C的G0/0/2端口发生阻塞并无法通过该端口发送配置 BPDU&#xff0c;则网络中 blocked 端口多久之后会进入到转发…

【JavaEE精炼宝库】多线程进阶(1)常见锁策略 | CAS | ABA问题

目录 一、常见的锁策略&#xff1a; 1.1 悲观锁 | 乐观锁&#xff1a; 1.2 重量级锁 | 轻量级锁&#xff1a; 1.3 自旋锁 | 挂起等待锁&#xff1a; 1.4 公平锁 | 非公平锁&#xff1a; 1.5 可重入锁 | 不可重入锁&#xff1a; 1.6 互斥锁 | 读写锁&#xff1a; 1.7 面…

Web前端第四次作业

目录 一、编写一个函数&#xff0c;形参是一个数组&#xff0c;返回数组中所有数字的平均值 二、编写一个函数&#xff0c;形参是一个数组&#xff0c;返回数组中的最大值 三、编写一个函数&#xff0c;形参是一个字符串&#xff0c;统计该字符串中每个字母出现的次数&#…

大模型微调方法总结

一 LoRA&#xff1a; 1 低(秩)rank 自适应微调方法 2 背景及本质   大模型的参数更新耗费大量现存为此&#xff0c; 微软的研究者们于2021年通过论文《LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS》提出了低秩适应LoRA 它冻结了预训练的模型权重&#xff0c;并将可…

2005年下半年软件设计师【下午题】试题及答案

文章目录 2005年下半年软件设计师下午题--试题2005年下半年软件设计师下午题--答案 2005年下半年软件设计师下午题–试题 2005年下半年软件设计师下午题–答案

AI绘画Stable Diffusion人物背景替换实操教程,让创意无限延伸

大家好&#xff0c;我是画画的小强 Stable Diffusion以其强大的能力可以实现人物背景的更换。本文将带你深入了解如何利用Stable Diffusion中的Inpaint Anything插件快速且精准地实现人物背景的替换&#xff0c;从而让你的图片焕发新生。 前期准备 本文会使用到Inpaint Anyt…

以敏感数据保护为中心,建立健全高校数据安全治理体系

教育行业数据安全事件频发 2023年8月&#xff0c;南昌某高校3万余条师生个人信息数据在境外互联网上被公开售卖&#xff0c;该校受到责令改正、警告并处80万元人民币罚款的处罚&#xff0c;主要责任人被罚款5万元人民币。2023 年 7月&#xff0c;中国人民大学一名毕业生马某某…

ClickHouse vs. Elasticsearch:十亿行数据的较量

本文字数&#xff1a;15291&#xff1b;估计阅读时间&#xff1a;39 分钟 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 Meetup活动 ClickHouse 上海首届 Meetup 讲师招募中&#xff0c;欢迎讲师在文末扫码报名&#xff01; 引言 这…

2024上海初中生古诗文大会倒计时4个月:单选题真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&#xff0c;每道题都有参考答案和解析。 为帮助孩子自测和练习&…

Chromium下载

https://chromium.woolyss.com/download/ https://www.chromium.org/getting-involved/download-chromium/

力扣SQL50 即时食物配送 II min函数 嵌套查询

Problem: 1174. 即时食物配送 II &#x1f468;‍&#x1f3eb; 参考题解 Code -- 计算立即配送的订单百分比 select round (-- 计算订单日期与客户偏好配送日期相同的订单数量sum(case when order_date customer_pref_delivery_date then 1 else 0 end) * 100 /-- 计算总订…