排序算法——详解

news2025/6/4 20:17:16

排序算法

(冒泡、选择、插入、快排、归并、堆排、计数、桶、基数)


稳定性 (Stability): 如果排序算法能保证,当待排序序列中存在值相等的元素时,排序后这些元素的相对次序保持不变,那么该算法就是稳定的。 例如:如果原始序列中有两个相同的元素 AB (A在B前面),排序后 A 仍然在 B 的前面,则该排序算法是稳定的。

  1. 冒泡排序 (Bubble Sort)
  • 思想: 重复遍历待排序的列表,比较相邻的两个元素,如果顺序错误就交换它们,直到没有元素可以交换,即排序完成。每次遍历都会将最大(或最小)的元素“冒泡”到其最终位置。

  • C++ 示例 (伪代码):

    void bubbleSort(int arr[], int n) {
        for (int i = 0; i < n - 1; ++i) {
            bool swapped = false; // 优化:如果一趟没有发生交换,说明已经有序
            for (int j = 0; j < n - 1 - i; ++j) {
                if (arr[j] > arr[j+1]) {
                    std::swap(arr[j], arr[j+1]);
                    swapped = true;
                }
            }
            if (!swapped) break;
        }
    }
    
  1. 选择排序 (Selection Sort)
  • 思想: 每次遍历(或扫描)整个待排序的无序部分,找到其中最小(或最大)的元素,然后将其放到无序部分的起始位置。重复这个过程直到所有元素都排好序。

  • C++ 示例 (伪代码):

    void selectionSort(int arr[], int n) {
        for (int i = 0; i < n - 1; ++i) {
            int min_idx = i;
            for (int j = i + 1; j < n; ++j) {
                if (arr[j] < arr[min_idx]) {
                    min_idx = j;
                }
            }
            if (min_idx != i) {
                std::swap(arr[i], arr[min_idx]);
            }
        }
    }
    
  1. 插入排序 (Insertion Sort)
  • 思想: 将一个元素插入到已经排序好的部分。从第二个元素开始,将其与前面已排序的元素进行比较,如果它比前面的元素小,就将前面的元素后移,直到找到合适的位置插入该元素。

  • C++ 示例 (伪代码):

    void insertionSort(int arr[], int n) {
        for (int i = 1; i < n; ++i) {
            int key = arr[i];
            int j = i - 1;
            // 将比 key 大的元素向后移动
            while (j >= 0 && arr[j] > key) {
                arr[j+1] = arr[j];
                j--;
            }
            arr[j+1] = key; // 插入 key
        }
    }
    
  1. 快速排序 (Quick Sort)
  • 思想: 采用分治策略。从数组中选择一个元素作为“基准”(pivot),将数组分割成两个子数组:一个子数组的所有元素都小于基准,另一个子数组的所有元素都大于基准。然后递归地对这两个子数组进行排序。

  • C++ 示例 (手写实现见下文)。

    #include <iostream>
    #include <vector>
    #include <algorithm> // For std::swap
    
    // 辅助函数:分区操作
    // 返回基准元素最终的索引
    int partition(std::vector<int>& arr, int low, int high) {
        int pivot = arr[high]; // 选择最后一个元素作为基准
        int i = (low - 1);     // 小于基准的元素区域的结束索引
    
        for (int j = low; j <= high - 1; j++) {
            // 如果当前元素小于或等于基准
            if (arr[j] <= pivot) {
                i++; // 增加小于基准的元素区域的结束索引
                std::swap(arr[i], arr[j]);
            }
        }
        std::swap(arr[i + 1], arr[high]); // 将基准元素放到正确的位置
        return (i + 1);
    }
    
    // 快速排序主函数
    void quickSort(std::vector<int>& arr, int low, int high) {
        if (low < high) {
            // pi 是分区点索引,arr[pi] 现在在正确的位置
            int pi = partition(arr, low, high);
    
            // 递归地对左右两部分进行排序
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }
    
    /*
    // 示例使用
    int main() {
        std::vector<int> arr = {10, 7, 8, 9, 1, 5};
        int n = arr.size();
        std::cout << "Original array: ";
        for (int x : arr) {
            std::cout << x << " ";
        }
        std::cout << std::endl;
    
        quickSort(arr, 0, n - 1);
    
        std::cout << "Sorted array (Quick Sort): ";
        for (int x : arr) {
            std::cout << x << " ";
        }
        std::cout << std::endl;
        return 0;
    }
    */
    
  1. 归并排序 (Merge Sort)
  • 思想: 采用分治策略。将待排序的序列递归地分成两半,直到每个子序列只包含一个元素(这被认为是自然有序的)。然后将这些有序的子序列两两合并,形成更大的有序序列,直到所有序列合并成一个完整的有序序列。

  • C++ 示例 (手写实现见下文)。

    #include <iostream>
    #include <vector>
    #include <algorithm> // Not strictly needed for merge, but good for general use
    
    // 辅助函数:合并两个有序子数组
    void merge(std::vector<int>& arr, int left, int mid, int right) {
        int n1 = mid - left + 1;
        int n2 = right - mid;
    
        // 创建临时数组
        std::vector<int> L(n1);
        std::vector<int> R(n2);
    
        // 复制数据到临时数组 L[] 和 R[]
        for (int i = 0; i < n1; i++) {
            L[i] = arr[left + i];
        }
        for (int j = 0; j < n2; j++) {
            R[j] = arr[mid + 1 + j];
        }
    
        // 合并临时数组回到 arr[left..right]
        int i = 0; // L[] 的起始索引
        int j = 0; // R[] 的起始索引
        int k = left; // 合并后数组的起始索引
    
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) {
                arr[k] = L[i];
                i++;
            } else {
                arr[k] = R[j];
                j++;
            }
            k++;
        }
    
        // 复制 L[] 中剩余的元素 (如果有)
        while (i < n1) {
            arr[k] = L[i];
            i++;
            k++;
        }
    
        // 复制 R[] 中剩余的元素 (如果有)
        while (j < n2) {
            arr[k] = R[j];
            j++;
            k++;
        }
    }
    
    // 归并排序主函数
    void mergeSort(std::vector<int>& arr, int left, int right) {
        if (left >= right) { // 基本情况:如果只有一个或零个元素
            return;
        }
        int mid = left + (right - left) / 2; // 计算中间点,防止溢出
        mergeSort(arr, left, mid);           // 递归排序左半部分
        mergeSort(arr, mid + 1, right);      // 递归排序右半部分
        merge(arr, left, mid, right);        // 合并已排序的两半
    }
    
    /*
    // 示例使用
    int main() {
        std::vector<int> arr = {12, 11, 13, 5, 6, 7};
        int n = arr.size();
        std::cout << "Original array: ";
        for (int x : arr) {
            std::cout << x << " ";
        }
        std::cout << std::endl;
    
        mergeSort(arr, 0, n - 1);
    
        std::cout << "Sorted array (Merge Sort): ";
        for (int x : arr) {
            std::cout << x << " ";
        }
        std::cout << std::endl;
        return 0;
    }
    */
    
  1. 堆排序 (Heap Sort)
  • 思想: 利用堆(通常是最大堆)的数据结构。首先将待排序的序列构建成一个最大堆。然后,重复地将堆顶元素(最大值)与堆的最后一个元素交换,并缩小堆的范围,重新调整剩余元素以保持堆的性质。

  • C++ 示例 (伪代码):

    void heapify(int arr[], int n, int i) {
        int largest = i; // 初始化最大元素为根
        int left = 2 * i + 1; // 左子节点
        int right = 2 * i + 2; // 右子节点
    
        if (left < n && arr[left] > arr[largest])
            largest = left;
    
        if (right < n && arr[right] > arr[largest])
            largest = right;
    
        if (largest != i) {
            std::swap(arr[i], arr[largest]);
            heapify(arr, n, largest); // 递归地堆化受影响的子树
        }
    }
    
    void heapSort(int arr[], int n) {
        // 构建最大堆 (从最后一个非叶子节点开始)
        for (int i = n / 2 - 1; i >= 0; i--)
            heapify(arr, n, i);
    
        // 一个个从堆顶取出元素
        for (int i = n - 1; i > 0; i--) {
            std::swap(arr[0], arr[i]); // 将当前最大元素移到数组末尾
            heapify(arr, i, 0); // 对剩余元素进行堆化
        }
    }
    
  1. 计数排序 (Counting Sort)
  • 思想: 非比较排序。适用于待排序元素是整数,且范围不大的情况。它统计每个数字出现的次数,然后根据统计结果将数字按顺序输出。

  • C++ 示例 (伪代码):

    void countingSort(int arr[], int n) {
        int max_val = arr[0];
        for (int i = 1; i < n; ++i) {
            if (arr[i] > max_val) {
                max_val = arr[i];
            }
        }
    
        std::vector<int> count(max_val + 1, 0);
        std::vector<int> output(n);
    
        for (int i = 0; i < n; ++i) {
            count[arr[i]]++;
        }
    
        for (int i = 1; i <= max_val; ++i) {
            count[i] += count[i - 1]; // 累加计数,表示小于等于当前值的元素个数
        }
    
        // 从后向前遍历,保证稳定性
        for (int i = n - 1; i >= 0; --i) {
            output[count[arr[i]] - 1] = arr[i];
            count[arr[i]]--;
        }
    
        for (int i = 0; i < n; ++i) {
            arr[i] = output[i];
        }
    }
    
  1. 桶排序 (Bucket Sort)
  • 思想: 非比较排序。将待排序的元素分散到有限数量的桶中,每个桶再分别进行排序(可以使用其他排序算法,如插入排序),最后将所有桶中的元素依次取出。

  • C++ 示例 (伪代码):

    void bucketSort(float arr[], int n) {
        // 创建 n 个空桶
        std::vector<std::vector<float>> buckets(n);
    
        // 将元素放入对应的桶中
        for (int i = 0; i < n; ++i) {
            int bucket_idx = n * arr[i]; // 假设元素在 [0, 1) 之间
            buckets[bucket_idx].push_back(arr[i]);
        }
    
        // 对每个桶进行排序 (可以使用插入排序或其他简单排序)
        for (int i = 0; i < n; ++i) {
            std::sort(buckets[i].begin(), buckets[i].end()); // 使用std::sort简化
        }
    
        // 将桶中的元素按顺序放回原数组
        int index = 0;
        for (int i = 0; i < n; ++i) {
            for (float val : buckets[i]) {
                arr[index++] = val;
            }
        }
    }
    
  1. 基数排序 (Radix Sort)
  • 思想: 非比较排序。根据数字的各个位(或基数)进行排序。从最低有效位(LSD)或最高有效位(MSD)开始,对每一位使用稳定的子排序算法(通常是计数排序)进行排序。

  • C++ 示例 (伪代码,通常结合计数排序实现):

    // 辅助函数:根据指定位数对数组进行计数排序
    void countSortForRadix(int arr[], int n, int exp) {
        std::vector<int> output(n);
        std::vector<int> count(10, 0); // 0-9
    
        for (int i = 0; i < n; i++) {
            count[(arr[i] / exp) % 10]++;
        }
    
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }
    
        for (int i = n - 1; i >= 0; i--) {
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];
            count[(arr[i] / exp) % 10]--;
        }
    
        for (int i = 0; i < n; i++) {
            arr[i] = output[i];
        }
    }
    
    void radixSort(int arr[], int n) {
        int max_val = arr[0];
        for (int i = 1; i < n; ++i) {
            if (arr[i] > max_val) {
                max_val = arr[i];
            }
        }
    
        // 对每一位进行计数排序
        for (int exp = 1; max_val / exp > 0; exp *= 10) {
            countSortForRadix(arr, n, exp);
        }
    }
    
算法名称最佳时间复杂度平均时间复杂度最坏时间复杂度空间复杂度稳定性
冒泡排序O(n)O(n2)O(n2)O(1)稳定
选择排序O(n2)O(n2)O(n2)O(1)不稳定
插入排序O(n)O(n2)O(n2)O(1)稳定
快速排序O(nlogn)O(nlogn)O(n2)O(logn) (递归栈)不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)稳定
桶排序O(n+k)O(n+k)O(n2) (桶内不均匀)O(n+k)稳定 (依赖桶内排序)
基数排序O(d(n+k))O(d(n+k))O(d(n+k))O(n+k)稳定 (依赖子排序)
  • 注:
    • n 表示待排序元素的数量。
    • k 表示待排序元素的范围(例如,计数排序中最大值-最小值+1)。
    • d 表示数字的位数(基数排序)。
    • 快速排序的最坏时间复杂度 O(n2) 发生在输入已经有序或接近有序,且选择的基准不好的情况下。
    • 桶排序的最坏时间复杂度 O(n2) 发生在所有元素都分到同一个桶里,并且桶内排序算法是 O(n2) 的时候。

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

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

相关文章

电子电气架构 --- 如何应对未来区域式电子电气(E/E)架构的挑战?

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

易学探索助手-个人记录(十二)

近期我完成了古籍处理板块页面升级&#xff0c;补充完成原文、句读、翻译的清空、保存和编辑&#xff08;其中句读仅可修改标点&#xff09;功能&#xff0c;新增原文和句读的繁简体切换功能 一、古籍处理板块整体页面升级 将原来一整个页面呈现的布局改为分栏呈现&#xff0…

Python窗体编程技术详解

文章目录 1. Tkinter简介示例代码优势劣势 2. PyQt/PySide简介示例代码(PyQt5)优势劣势 3. wxPython简介示例代码优势劣势 4. Kivy简介示例代码优势劣势 5. PySimpleGUI简介示例代码优势劣势 技术对比总结选择建议 Python提供了多种实现图形用户界面(GUI)编程的技术&#xff0c…

NVMe协议简介之AXI总线更新

更新AXI4总线知识 AXI4总线协议 AXI4总线协议是由ARM公司提出的一种片内总线协议 &#xff0c;旨在实现SOC中各模块之间的高效可靠的数据传输和管理。AXI4协议具有高性能、高吞吐量和低延迟等优点&#xff0c;在SOC设计中被广泛应用 。随着时间的推移&#xff0c;AXI4的影响不…

设计模式——责任链设计模式(行为型)

摘要 责任链设计模式是一种行为型设计模式&#xff0c;旨在将请求的发送者与接收者解耦&#xff0c;通过多个处理器对象按链式结构依次处理请求&#xff0c;直到某个处理器处理为止。它包含抽象处理者、具体处理者和客户端等核心角色。该模式适用于多个对象可能处理请求的场景…

基于Android的医院陪诊预约系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

基于Spring Boot 电商书城平台系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

【金融基础学习】债券回购方式

债券回购作为货币市场的重要工具&#xff0c;本质上是一种以债券为抵押的短期资金借贷行为。在银行间市场&#xff0c;质押式回购与**买断式回购*是两种主要形式。 1. 质押式回购(Pledged Repo, RP) – 所有权不转移的短期融资工具 1.1 质押式回购概述 质押式回购是交易双方…

第五十九节:性能优化-GPU加速 (CUDA 模块)

在计算机视觉领域,实时性往往是关键瓶颈。当传统CPU处理高分辨率视频流或复杂算法时,力不从心。本文将深入探索OpenCV的CUDA模块,揭示如何通过GPU并行计算实现数量级的性能飞跃。 一、GPU加速:计算机视觉的必由之路 CPU的强项在于复杂逻辑和低延迟任务,但面对图像处理中高…

单元测试-概述入门

目录 main方法测试缺点&#xff1a; 在pom.xm中&#xff0c;引入junit的依赖。,在test/java目录下&#xff0c;创建测试类&#xff0c;并编写对应的测试方法&#xff0c;并在方法上声明test注解。 练习&#xff1a;验证身份证合法性 测试成功 测试失败 main方法测试缺点&am…

⚡ Hyperlane —— 比 Rocket 更快的 Rust Web 框架!

⚡ Hyperlane —— 比 Rocket 更快的 Rust Web 框架&#xff01; 在现代 Web 服务开发中&#xff0c;开发者需要一个既轻量级又高性能的 HTTP 服务器库来简化开发流程&#xff0c;同时确保服务的高效运行。Hyperlane 正是为此而生——一个专为 Rust 开发者设计的 HTTP 服务器库…

《AI Agent项目开发实战》DeepSeek R1模型蒸馏入门实战

一、模型蒸馏环境部署 注&#xff1a;本次实验仍然采用Ubuntu操作系统&#xff0c;基本配置如下&#xff1a; 需要注意的是&#xff0c;本次公开课以Qwen 1.5-instruct模型为例进行蒸馏&#xff0c;从而能省略冷启动SFT过程&#xff0c;并且 由于Qwen系列模型本身性能较强&…

字节golang后端二面

前端接口使用restful格式&#xff0c;post与get的区别是什么&#xff1f; HTTP网络返回的状态码有哪些&#xff1f; go语言切片与数组的区别是什么&#xff1f; MySQL实现并发安全避免两个事务同时对一个记录写操作的手段有哪些&#xff1f; 如何实现业务的幂等性&#xff08;在…

vscode + cmake + ninja+ gcc 搭建MCU开发环境

vscode cmake ninja gcc 搭建MCU开发环境 文章目录 vscode cmake ninja gcc 搭建MCU开发环境1. 前言2. 工具安装及介绍2.1 gcc2.1.1 gcc 介绍2.1.2 gcc 下载及安装 2.2 ninja2.2.1 ninja 介绍2.2 ninja 安装 2.3 cmake2.3.1 cmake 介绍2.3.2 cmake 安装 2.4 VScode 3. 上手…

三种经典算法优化无线传感器网络(WSN)覆盖(SSA-WSN、PSO-WSN、GWO-WSN),MATLAB代码实现

三种经典算法优化无线传感器网络(WSN)覆盖&#xff08;SSA-WSN、PSO-WSN、GWO-WSN&#xff09;&#xff0c;MATLAB代码实现 目录 三种经典算法优化无线传感器网络(WSN)覆盖&#xff08;SSA-WSN、PSO-WSN、GWO-WSN&#xff09;&#xff0c;MATLAB代码实现效果一览基本介绍程序设…

JVM 核心组件深度解析:堆、方法区、执行引擎与本地方法接口

一、JVM 堆内存&#xff1a;对象的生存与消亡之地 作为 Java 虚拟机中最大的内存区域&#xff0c;堆内存是所有对象实例的 “出生地” 与 “安息所”。从程序运行的角度看&#xff0c;所有通过new关键字创建的对象都在堆中分配内存&#xff0c;其生命周期完全由垃圾回收机制&am…

OpenCV4.4.0下载及初步配置(Win11)

目录 OpenCV4.4.0工具下载安装环境变量系统配置 OpenCV4.4.0 工具 系统&#xff1a;Windows 11 下载 OpenCV全版本百度网盘链接&#xff1a;: https://pan.baidu.com/s/15qTzucC6ela3bErdZ285oA?pwdjxuy 提取码: jxuy找到 opencv-4.0.0-vc14_vc15 下载得到 安装 运行op…

使用Mathematica观察多形式根的分布随参数的变化

有两种方式观察多项式的根随着参数变化&#xff1a;&#xff08;1&#xff09;直接制作一个小的动态视频&#xff1b;&#xff08;2&#xff09;绘制所有根形成的痕迹&#xff08;locus&#xff09;。 制作动态视频&#xff1a; (*Arg-plane plotting routine with plotting …

【C++高级主题】转换与多个基类

目录 一、多重继承的虚函数表结构&#xff1a;每个基类一个虚表 1.1 单继承与多重继承的虚表差异 1.2 代码示例&#xff1a;多重继承的虚函数覆盖 1.3 虚表结构示意图 二、指针与引用的类型转换&#xff1a;地址调整的底层逻辑 2.1 派生类指针转基类指针的地址偏移 2.2 …

『uniapp』添加桌面长按快捷操作 shortcuts(详细图文注释)

目录 手机环境适配说明安卓效果图代码 iOS(暂未实测,没有水果开发者)总结 欢迎关注 『uniapp』 专栏&#xff0c;持续更新中 欢迎关注 『uniapp』 专栏&#xff0c;持续更新中 手机环境适配说明 个别手机系统可能需要进行特别的权限设置,否则会无法使用 桌面快捷方式: 已知的有…