当前位置: 首页 > news >正文

【入门篇|第二篇】从零实现选择、冒泡、插入排序(含对数器)

无前置所需知识,建议会的直接跳过(对数器不建议跳)


文章目录

      • 前言
    • 1 基础排序算法
      • 1.1 选择排序
      • 1.2 冒泡排序
      • 1.3 插入排序
      • 1.4 三种算法对比
    • 2. 对数器
      • 2.1 使用场景
      • 2.2 对数期概述
      • 2.3 对数期实现


前言

排序是将一组数据按照特定顺序(升序或降序)重新排列的过程。本文介绍三种最基础的排序算法,它们是理解更复杂排序算法的基础。

1 基础排序算法

公共工具方法:

//交换数组中两个元素的位置
public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;

1.1 选择排序

在未排序序列中找到最小(或最大)元素,放到排序序列的起始位置,然后继续从剩余未排序元素中寻找最小元素,放到已排序序列的末尾。

核心思想:在 i ~ n-1 的范围上,找到最小值并放在位置 i,然后在 i+1 ~ n-1 范围上继续此操作。

算法步骤演示
以数组 [3, 2, 4, 1] 为例:

初始状态: [3, 2, 4, 1]
第1轮: 在0~3范围找最小值1,与位置0交换 → [1, 2, 4, 3]
第2轮: 在1~3范围找最小值2,与位置1交换 → [1, 2, 4, 3] (无需交换)
第3轮: 在2~3范围找最小值3,与位置2交换 → [1, 2, 3, 4]
结果: [1, 2, 3, 4]

代码示例:

// 选择排序public static void selectionSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int minIndex, i = 0; i < arr.length - 1; i++) {// minIndex i~n-1 范围最小值minIndex = i;for (int j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}swap(arr, i, minIndex);}}

1.2 冒泡排序

通过重复遍历要排序的数列,一次比较两个相邻元素,如果它们的顺序错误就交换过来。重复进行直到没有需要交换的元素。

核心思想:在 0 ~ end 范围上,相邻位置较大的数"冒泡"到右边,最大值最终到达位置 end,然后在 0 ~ end-1 范围上继续。

算法步骤演示:
以数组 [3, 4, 1, 5, 2] 为例:

初始状态: [3, 4, 1, 5, 2]
第1轮: 比较相邻元素并交换3,4(不交换) → [3, 4, 1, 5, 2]4,1(交换)   → [3, 1, 4, 5, 2]4,5(不交换) → [3, 1, 4, 5, 2]5,2(交换)   → [3, 1, 4, 2, 5] (最大值5冒泡到末尾)第2轮: 在前4个数中冒泡 → [1, 3, 2, 4, 5]
第3轮: 在前3个数中冒泡 → [1, 2, 3, 4, 5]
第4轮: 在前2个数中冒泡 → [1, 2, 3, 4, 5] (无需交换)
结果: [1, 2, 3, 4, 5]

代码示例:

// 冒泡排序public static void bubbleSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int end = arr.length - 1; end > 0; end--) {for (int i = 0; i < end; i++) {if (arr[i] > arr[i + 1]) {swap(arr, i, i + 1);}}}}

1.3 插入排序

将数组分为已排序和未排序两部分,逐个将未排序部分的元素插入到已排序部分的正确位置。

核心思想:保证 0 ~ i 范围已经有序,新来的数从右到左滑动到合适位置插入。

以数组 [1, 4, 5, 2] 为例:

初始状态: [1, 4, 5, 2] (位置0的1看作已排序)
第1轮: 将4插入已排序部分[1] → [1, 4, 5, 2] (4>1,位置正确)
第2轮: 将5插入已排序部分[1,4] → [1, 4, 5, 2] (5>4,位置正确)
第3轮: 将2插入已排序部分[1,4,5]2<5,交换 → [1, 4, 2, 5]2<4,交换 → [1, 2, 4, 5]2>1,停止
结果: [1, 2, 4, 5]

注:该程序直到左边的数<=当前的数左边没有数字停止

    public static void insertionSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int i = 1; i < arr.length; i++) {for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {swap(arr, j, j + 1);}}}

1.4 三种算法对比

排序算法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性适用场景
选择排序O(n²)O(n²)O(n²)O(1)不稳定数据量小,内存限制严格
冒泡排序O(n²)O(n²)O(n)O(1)稳定几乎有序的数据,教学演示
插入排序O(n²)O(n²)O(n)O(1)稳定小数据集,几乎有序的数据

性能特点

  • 选择排序:交换次数最少(n-1次),但比较次数固定(n²/2次)
  • 冒泡排序:可以提前结束,最好情况下只需一轮,适合检测数组是否已排序
  • 插入排序:在实际应用中通常比选择和冒泡排序更快,对于小数据集性能优异

稳定性说明

  • 稳定排序:相等元素的相对顺序在排序后保持不变
  • 不稳定排序:相等元素的相对顺序可能改变

2. 对数器

2.1 使用场景

1.你在网上找到了某个公司的面试题,想了好久,感觉自己会做,但找不到在线测试
2.你和朋友交流面试题,想了好久,感觉自己会做,但是找不到在线测试
3.你在网上做笔试,前几个测试用例都过了,突然巨大无比的数据量来了,代码报错了,且如此大的数据根本看不出哪错了。

2.2 对数期概述

什么是对数器?

有一个算法题,你想出暴力解与最优解,然后你随机生成数据,控制数据的长度和范围,跑很多次,如果暴力解和最优解的答案基本一样,则正确,如果两者答案不一样,可以把原始测试数据打印,将数组长度小一点,在去找问题即可,这就是对数器。

2.3 对数期实现

1.你想要测的方法a(最优解)
2.实现复杂度不好但容易实现的方法b(暴力解)
3.实现一个随机样本产生器(长度、值随机)
4.把方法a和方法b跑相同的输入样本,看看得到的结果是否一样
5.如果有一个随机样本比对结果不一致,打印出错样本,进行人工干预,改正方法a和b
6.样本数量很多组仍正确,可确定最优解a正确

当处于第5步时,找到一个数据量小的错误样本,去带入debug,把错误例子带入代码排查Print打法、断点技术皆可

示例:随机数组

    public static int[] randomArray(int n, int v) {int[] arr = new int[n];for (int i = 0; i < n; i++) {// Math.random() -> double -> [0,1)范围山的一个小数,0.37463473126、0.001231231,等概率!// Math.random() * v -> double -> [0,v)一个小数,依然等概率// (int)(Math.random() * v) -> int -> 0 1 2 3 ... v-1,等概率的!// (int) (Math.random() * v) + 1 -> int -> 1 2 3 .... v,等概率的!arr[i] = (int) (Math.random() * v) + 1;}return arr;}

示例:复制数组

    public static int[] copyArray(int[] arr) {int n = arr.length;int[] ans = new int[n];for (int i = 0; i < n; i++) {ans[i] = arr[i];}return ans;}

示例:主程序

        // 随机数组最大长度int N = 200;// 随机数组每个值,在1~V之间等概率随机int V = 1000;// testTimes : 测试次数int testTimes = 50000;System.out.println("测试开始");for (int i = 0; i < testTimes; i++) {// 随机得到一个长度,长度在[0~N-1]int n = (int) (Math.random() * N);// 得到随机数组int[] arr = randomArray(n, V);int[] arr1 = copyArray(arr);int[] arr2 = copyArray(arr);int[] arr3 = copyArray(arr);selectionSort(arr1);bubbleSort(arr2);insertionSort(arr3);if (!sameArray(arr1, arr2) || !sameArray(arr1, arr3)) {System.out.println("出错了!");// 当有错了// 打印是什么例子,出错的// 打印三个功能,各自排序成了什么样// 可能要把例子带入,每个方法,去debug!}}System.out.println("测试结束");}

细节:使用三个复制的数组去运行算法,当出现问题时,可以直接打印原数组,检测问题


本文为算法学习笔记,持续更新中…

如果我的内容对你有帮助,希望可以收获你的点赞、评论、收藏。

请添加图片描述

http://www.dtcms.com/a/388811.html

相关文章:

  • javaweb Servlet基本介绍及开发流程
  • MySQL MHA高可用
  • 整体设计 逻辑拆解之2 实现骨架:一元谓词+ CNN的谓词系统
  • SpEL(Spring Expression Language)学习笔记
  • Java 字节码进阶3:面向对象多态在字节码层面的原理?
  • Tensor :核心概念、常用函数与避坑指南
  • 机器学习实战·第四章 训练模型(1)
  • 一次因表单默认提交导致的白屏排查记录
  • Linux:io_uring
  • 《第九课——C语言判断:从Java的“文明裁决“到C的“原始决斗“——if/else的生死擂台与switch的轮盘赌局》
  • 学习日报|Spring 全局异常与自定义异常拦截器执行顺序问题及解决
  • Spring Boot 参数处理
  • Debian系统基本介绍:新手入门指南
  • Spring Security 框架
  • Qt QPercentBarSeries详解
  • RTT操作系统(3)
  • DNS服务管理
  • IDA Pro配置与笔记
  • 虚函数表在单继承与多继承中的实现机制
  • 矿石生成(1)
  • Linux 线程的概念
  • Unity学习之资源管理(Resources、AssetDatabase、AssetBundle、Addressable)
  • LG P5138 fibonacci Solution
  • 删除UCPD监控服务或者监控驱动
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(33):文法運用第10回1+(考え方14)
  • 向量技术研究报告:从数学基础到AI革命的支柱
  • 802.1x和802.1Q之间关联和作用
  • 基于大模型多模态的人体体型评估:从“尺码测量”到“视觉-感受”范式
  • 更符合人类偏好的具身导航!HALO:面向机器人导航的人类偏好对齐离线奖励学习
  • Transformer多头注意力机制