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

【左程云算法03】对数器算法和数据结构大致分类

目录

对数器的实现

代码实现与解析

1. 随机样本生成器 (randomArray)

2. 核心驱动逻辑 (main 方法)

3. 辅助函数 (copyArray 和 sameArray)

对数器的威力

算法和数据结构简介​编辑

1. 硬计算类算法 (Hard Computing)

2. 软计算类算法 (Soft Computing)

核心观点

一个宏观的数据结构分类

1. 连续结构 (Continuous Structure)

2. 跳转结构 (Jumping Structure)


视频链接

算法讲解005【入门】对数器-验证的重要手段

算法讲解008【入门】算法和数据结构简介

对数器的实现

对数器的本质,就是通过大规模的随机样本测试,来对比我们实现的“精巧算法”和一个“绝对正确”的暴力方法,从而验证我们算法的正确性。

构建一个对数器的完整流程分为以下六步:

  1. 你要有一个想测的方法 a(最优解)
    这是我们自己实现的、希望验证其正确性的算法。

  2. 实现一个复杂度不好但是容易实现的方法 b(暴力解)
    这个方法是我们的“真理标杆”。我们不要求它性能好,但必须保证它的逻辑是绝对正确的。通常,我们会用最直观、最暴力的方式来实现它。

  3. 实现一个随机样本产生器
    我们需要一个函数,能够生成各种随机情况的测试数据(例如,长度随机、值也随机的数组)。

  4. 把方法 a 和方法 b 跑相同的输入样本,看看得到的结果是否一样
    这是对数器的核心对比环节。对于同一个随机样本,分别用方法 a 和方法 b 去处理。

  5. 如果有某个随机样本使得比对结果不一致,打印出这个出错的样本进行人工干预,改对方法 a 和方法 b
    一旦发现不一致,就意味着我们的方法 a 存在 bug。对数器会自动捕获这个导致错误的“反例”,我们就可以用这个具体的样本去轻松地进行 debug。

  6. 当样本数量很多时比对依然正确,可以确定方法 a(最优解)已经正确。
    经过成千上万,甚至几十万次随机样本的“轰炸”都未曾出错,我们就可以非常有信心地认为,我们的算法 a 是正确的。

代码实现与解析

下面,我们结合截图中的 Java 代码,来一步步拆解对数器的具体实现。这里的例子是用来同时验证我们写的多个排序算法是否正确。

1. 随机样本生成器 (randomArray)

负责源源不断地提供测试数据。

// 得到一个随机数组,长度是n
// 数组中每个数,都在1~v之间,随机得到
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)的一个小数// 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;
}

这个函数接收最大长度 n 和最大值 v,生成一个长度和值都在指定范围内的随机数组。注释清晰地解释了 Math.random() 如何通过一系列变换,生成我们需要的 [1, v] 范围内的随机整数。

2. 核心驱动逻辑 (main 方法)

这里是对数器的“发动机”,它组织了整个测试流程。

// 为了验证
public static void main(String[] args) {// 随机数组最大长度int N = 100;// 随机数组每个值,在1~V之间随机int V = 1000;// 测试次数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("出错了!");// 当出错时,可以把原始样本arr打印出来,// 方便把这个例子带入到每个方法去debug!}}System.out.println("测试结束");
}

关键点解析

在后续的很多题目中,对数器都会是我们验证复杂算法(如动态规划、贪心等)的得力助手。

算法和数据结构简介

  • 循环测试:通过 testTimes 控制测试次数,量级越大,覆盖的样本情况就越全面,结果越可靠。

  • 拷贝数组:copyArray(arr) 这一步至关重要!因为排序是原地操作,会修改原数组。如果不拷贝,那么第一个排序(selectionSort)完成后,arr2 和 arr3 拿到的将是一个有序数组,测试就失去了意义。必须保证每个待测算法都工作在完全相同的、未经修改的原始样本上。

  • 结果比对:if (!sameArray(arr1, arr2) || !sameArray(arr1, arr3)) 负责检查。这里我们让多个排序算法互相验证

    3. 辅助函数 (copyArray 和 sameArray)

    这两个是保证对数器能正确运行的工具函数。

    copyArray:

    。如果其中任何两个的结果不一致,就说明至少有一个算法出了问题。

    // 为了验证
    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;
    }```
    功能很简单:创建一个和原数组一模一样的新数组,避免引用传递导致的问题。**`sameArray`:**
    ```java
    // 为了验证
    public static boolean sameArray(int[] arr1, int[] arr2) {// 这里可以先补充长度是否相等的判断int n = arr1.length;for (int i = 0; i < n; i++) {if (arr1[i] != arr2[i]) {return false;}}return true;
    }

    对数器的威力

    对数器的门槛其实是比较高的,因为它往往需要你用两种不同的思路去实现相同功能的方法。但它的价值是巨大的:

  • 信心:它是你验证自己算法正确性的最强后盾。

  • 能力:它锻炼你从不同角度思考同一个问题的能力(暴力解 vs. 最优解)。

  • 效率:它能自动找到你手动很难想到的边界“反例”,让你的 debug 过程从大海捞针变成按图索骥。

一个有趣、有用的算法分类

首先,左老师提出了一种非官方但极具现实意义的算法分类方法,将算法分为两大类。

注意:这两个名词都不是计算机科学或算法中的标准术语。

1. 硬计算类算法 (Hard Computing)

定义:追求精确解、最优解的算法。

特点:为了找到那个唯一的、精确的答案,可能会导致算法的复杂度较高。

应用场景:这是目前绝大多数程序员面试、笔试、ACM/ICPC 类竞赛考察的核心。无论是大厂还是初创公司,面试中考察的算法基本都属于硬计算的范畴。

重要性硬计算类算法是所有程序员岗位都必须会考、任何写代码的工作都会用到的基础能力。无论是前端、后端、架构,算法的所有岗位都需要用到。

2. 软计算类算法 (Soft Computing)

核心观点

任何数据结构都一定是这两个结构拼出来的!没有例外!

这是一个非常重要的论断。我们将来要学习的各种纷繁复杂的数据结构,例如:

等等,无论它们看起来多么复杂,其最底层的实现原理,都离不开“连续的内存”和“跳转的指针”这两种基本构造单元的组合与运用。

在后续的课程中,我们将会深入学习这些具体的数据结构,届时可以回头再来体会这个宏观分类的精妙之处。

一个宏观的数据结构分类

接着,左老师提出了一个同样宏观的、用于理解所有数据结构的底层逻辑分类。他认为,无论多么复杂的数据结构,其底层都是由两种最基本的结构“拼”出来的。

1. 连续结构 (Continuous Structure)

可以理解为像数组这样的结构。数据在内存中是连续存放的,一个挨着一个,可以通过索引直接计算出内存地址,实现快速的随机访问。

2. 跳转结构 (Jumping Structure)

可以理解为像链表这样的结构。数据在内存中是离散存放的,每个节点通过一个指针或引用,“跳转”到下一个节点的位置。

  • 定义:更注重逼近一个可接受的解决方案,而不是强求精确的最优解。

  • 特点:计算时间可控,允许在一定程度上牺牲精度来换取效率。

  • 典型例子:模糊逻辑、神经网络、进化计算、概率论、混沌理论、支持向量机、群体智能等。这些通常是机器学习、人工智能领域的核心算法。

  • 重要性:对于普通的开发岗位,软计算不是必须掌握的。但对于算法工程师,除了必须精通硬计算类算法之外,还需要掌握软计算类算法。

  • 链表

  • 队列、栈

  • 树(二叉树、平衡树、B树等)

  • 可持久化线段树

  • 树链剖分

  • 后缀数组

http://www.xdnf.cn/news/20475.html

相关文章:

  • FPGA会用到UVM吗?
  • Context Engineering survey
  • GraphQL API 性能优化实战:在线编程作业平台指南
  • EG1160 SOP16 高压大电流 半桥驱动芯片
  • 从 scheduler_tick 到上下文切换:深入解析 Linux 内核的 TIF_NEED_RESCHED 标志设置流程
  • 服务器防黑加固指南:SSH端口隐藏、Fail2ban与密钥登录
  • docker run 命令,不接it选项,run一个centos没有显示在运行,而run一个nginx却可以呢?
  • 【LeetCode热题100道笔记】腐烂的橘子
  • Typora处理markdown文件【给.md文档加水印】
  • 使用 TCMalloc 检查内存使用情况和内存泄漏
  • 残差网络 迁移学习对食物分类案例的改进
  • STL模版在vs2019和gcc中的特殊问题
  • STM32项目分享:基于物联网的健康监测系统设计
  • 基于STM32的智能宠物屋系统设计
  • 人工智能学习:什么是seq2seq模型
  • Java全栈开发工程师的面试实战:从基础到复杂场景的技术探索
  • Compose笔记(四十九)--SwipeToDismiss
  • RabbitMQ工作模式(下)
  • 贪心算法应用:蛋白质折叠问题详解
  • Eureka与Nacos的区别-服务注册+配置管理
  • AI模型测评平台工程化实战十二讲(第一讲:从手工测试到系统化的觉醒)
  • 力扣29. 两数相除题解
  • Qt资源系统学习
  • 【继承和派生】
  • 【Flask】测试平台开发,重构提测管理页面-第二十篇
  • 把装配想象成移动物体的问题
  • java基础学习(五):对象中的封装、继承和多态
  • C++经典的数据结构与算法之经典算法思想:排序算法
  • phpMyAdmin文件包含漏洞复现:原理详解+环境搭建+渗透实战(windows CVE-2014-8959)
  • 吴恩达机器学习(七)