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

Java 黑马程序员学习笔记(进阶篇9)

常见算法

1. 二分查找

(1) 前提条件:待查找的数组 必须是已排序的(通常为升序)。

(2) 核心思想:通过不断将查找区间一分为二,利用中间元素与目标值的比较,缩小查找范围,直到找到目标元素或确定区间为空(元素不存在)。

(3) 代码实现
public class BinarySearch {public static void main(String[] args) {// 前提:数组已按升序排序int[] arr = {11, 22, 33, 44, 55, 66, 77, 88};int target = 33;int result = binarySearch(arr, target);System.out.println("元素 " + target + " 的位置: " + result);}public static int binarySearch(int[] arr, int target) {int min = 0;        // 左边界(初始为数组起始索引)int max = arr.length - 1; // 右边界(初始为数组末尾索引)// 当区间有效(左边界 ≤ 右边界)时,继续查找while (min <= max) {// 计算中间索引(推荐用 `min + (max - min) / 2` 避免整数溢出)int mid = (min + max) / 2;if (arr[mid] == target) {return mid; // 找到目标,返回元素索引} else if (arr[mid] < target) {// 中间元素 < 目标 → 目标在**右半段**,调整左边界min = mid + 1;} else {// 中间元素 > 目标 → 目标在**左半段**,调整右边界max = mid - 1;}}return -1; // 区间为空,未找到目标元素}
}

2. 分块查找

(1) 概念:

分块查找是一种结合顺序查找和二分查找优点的查找算法,核心思路是 “先分块、再定位”—— 把无序的大数据表,拆成若干个 “块”,满足 “块内无序、块间有序” 的规则,再通过 “索引表” 快速定位目标所在的块,最后在块内查找目标。

(2) 关键前提(必须满足)
  • 块间有序:比如第 1 块的所有元素都小于第 2 块的元素,第 2 块都小于第 3 块(或反之,按升 / 降序约定一致);
  • 块内无序:每个块内部的元素不需要排序(降低维护成本);
  • 需额外维护 “索引表”:索引表记录每个块的 “关键信息”(通常是块的最大 / 最小值、块的起始索引、块的长度)。
(3) 分块查找两步流程(黑马经典拆解)
步骤 1:构建索引表

假设我们有一组数据:{8, 3, 12, 15, 20, 28, 25, 40, 35, 50, 48, 60},计划分 3 块(每块 4 个元素):

① 先给数据分块,确保块间有序:
  • 第 1 块:{8, 3, 12, 15} → 最大值 15;
  • 第 2 块:{20, 28, 25, 40} → 最大值 40;
  • 第 3 块:{35, 50, 48, 60} → 最大值 60;
    (满足 “第 1 块最大值<第 2 块最大值<第 3 块最大值”,块间有序)
② 构建索引表:每个索引项对应一个块,包含 “块的最大值” 和 “块的起始索引”,如下:
索引项块最大值块起始索引块长度
01504
14044
26084
步骤 2:查找目标元素(以找 “25” 为例)
阶段 1:定位目标所在的块(查索引表)

目标是 25,需找到 “块最大值≥25 且前一个块最大值<25” 的块;

索引表是有序的(块最大值递增),这里可选择两种方式查索引表:

  • 若索引表小:用顺序查找(简单);
  • 若索引表大:用二分查找(高效);
阶段 2:在目标块内查找(顺序查找)
  • 第 1 块的数据是{20, 28, 25, 40},块内无序,只能用顺序查找
  • 遍历第 1 块,找到 25 在数组中的索引为 6,返回结果。
(4) 题目:分块查找算法实现

现有一个整数数组 arr = {16,5,9,12,21,18,32,23,37,26,45,34,50,48,61,52,73,66},该数组按照 “块内无序、块间有序” 的规则分为 3 个块,具体划分如下:

  • 第 1 块:包含数组索引 0-5 的元素,最大值为 21
  • 第 2 块:包含数组索引 6-11 的元素,最大值为 45
  • 第 3 块:包含数组索引 12-17 的元素,最大值为 7

请完成以下任务:

① 设计一个 Block 类,用于存储每个块的关键信息:块的最大值(max)、块的起始索引(startIndex)、块的结束索引(endIndex),并提供必要的构造方法和 getter 方法。

② 实现分块查找算法,要求:

  • 创建索引表(由 Block 对象组成的数组)管理上述 3 个块
  • 编写 findIndexBlock 方法:根据目标值在索引表中定位其可能所在的块,返回该块在索引表中的索引(若目标值大于所有块的最大值,返回 - 1)
  • 编写 getIndex 方法:利用 findIndexBlock 定位的块,在该块对应的数组范围内查找目标值,返回目标值在原始数组中的索引(若未找到,返回 - 1)

③ 使用目标值 30 测试上述实现,输出查找结果(若找到则返回索引,否则返回 - 1)

package demo4;public class test8 {public static void main(String[] args) {int[] arr = {16,5,9,12,21,18,32,23,37,26,45,34,50,48,61,52,73,66};Block b1 = new Block(21,0,5);Block b2 = new Block(45,6,11);Block b3 = new Block(73,12,17);//定义索引表来管理三个块的对象Block[] blockArr = {b1,b2,b3};int number = 30;int index = getIndex(blockArr,arr,number);}public static int getIndex(Block[] blockArr, int[] arr, int number) {   //这里的参数不太理解int indexBlock = findIndexBlock(blockArr, number);if (indexBlock == -1) {return -1;}int startIndex = blockArr[indexBlock].getStartIndex();int endIndex = blockArr[indexBlock].getEndIndex();for (int i = startIndex; i <= endIndex; i++) {if (arr[i] == number) {return i;}}return -1;}public static int findIndexBlock(Block[] blockArr, int number) {   //这里的参数不太理解for (int i = 0; i < blockArr.length; i++) {if (number <= blockArr[i].getMax()) {   //不太理解,这里的getMax()是什么意思return i;}}return -1;}
}class Block {private int max;private int startIndex;private int endIndex;public Block() {}public Block(int max, int startIndex, int endIndex) {this.max = max;this.startIndex = startIndex;this.endIndex = endIndex;}public int getMax() {return max;}public void setMax(int max) {this.max = max;}public int getStartIndex() {return startIndex;}public void setStartIndex(int startIndex) {this.startIndex = startIndex;}public int getEndIndex() {return endIndex;}public void setEndIndex(int endIndex) {this.endIndex = endIndex;}
}
关键逻辑 1:blockArr[i].getMax()的含义

要理解这个方法,必须先看Block类的结构 —— 它是一个 “封装块信息的模板”:

class Block {private int max;          // 块的最大值(私有属性,外部不能直接访问)private int startIndex;   // 块的起始索引private int endIndex;     // 块的结束索引// getMax()是“ getter方法 ”——专门用来获取私有属性max的值public int getMax() {return max;}// 其他getter/setter方法...
}

所以:
blockArr[i] → 表示 “索引表中第 i 个块”(比如blockArr[0]b1blockArr[1]b2);
blockArr[i].getMax() → 表示 “获取索引表中第 i 个块的最大值”(比如blockArr[1].getMax()就是b2的最大值 45)。

关键逻辑 2:getIndex方法的参数
参数名类型含义与作用
blockArrBlock[]这是 “索引表”—— 存储所有块的关键信息(每个元素是一个Block对象,包含块的最大值、起始索引、结束索引)。
作用:通过它能拿到 “目标块” 的起始 / 结束索引(比如找到目标在第 2 块,就从blockArr[1]里拿该块的startIndexendIndex)。
arrint[]这是 “原始数据数组”—— 我们要查找目标值的数据源
作用:当确定 “目标块” 的范围后(比如起始索引 6、结束索引 11),需要遍历arr的这个范围,找到目标值的具体位置。
numberint这是 “目标查找值”—— 我们最终要在arr中找到的数字(比如代码里的 30)。
作用:既是 “定位目标块” 的依据,也是 “块内查找” 时的匹配条件。

3. 冒泡排序

(1) 概念:

冒泡排序的核心思想是 “相邻元素两两比较,大值逐步‘沉底’(移到数组末尾)”,每一轮排序都会将当前未排序区间的 “最大值” 通过多次交换,移动到未排序区间的末尾,如同水中的气泡逐渐上浮(小值靠前)、大值沉底(大值靠后),因此得名 “冒泡”。

(2) 关键特征
  • 比较规则:仅比较相邻的两个元素
  • 交换规则:若前一个元素 > 后一个元素(升序排序),则交换两者位置;
  • 轮次规律:数组长度为 n 时,最多需要 n-1 轮排序(每轮沉底 1 个大值,最后 1 个元素无需比较)。

(3) 代码实现

package demo4;public class test9 {public static void main(String[] args) {int[] arr = {2, 4, 5, 3, 1};for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}printArr(arr);}private static void printArr(int[] arr) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}}
}
关键逻辑 1:外层循环控制排序的 “轮数”
for (int i = 0; i < arr.length - 1; i++)

循环条件i < arr.length - 1的原因

  • 数组长度为n(如代码中arr.length=5)时,最多需要n-1轮排序。因为每轮会确定 1 个元素的最终位置(沉底),当n-1个元素排好后,最后 1 个元素自然也在正确位置,无需再排序。
  • 例如:数组长度 5,只需 4 轮(i=0i=3)即可完成排序。
关键逻辑 2:内层循环控制每轮的 “比较次数”
for (int j = 0; j < arr.length - 1 - i; j++)

① 循环条件j < arr.length - 1 - i的原因

每轮排序后,已有i个最大值 “沉底”(排在数组末尾,位置固定),这些元素无需再参与比较。因此 “未排序区间” 的长度会随i增大而减小,每轮的比较次数也随之减少。

② 具体来说:arr.length - 1:保证j+1不越界(因为要比较arr[j]arr[j+1]);

4. 选择排序

① 原理:

选择排序是一种基于 “选择最值” 的基础排序算法,核心思想可概括为:
“每轮从待排序区间中找到‘最小值’(或最大值),将其与待排序区间的‘第一个元素’交换位置,逐步缩小待排序区间,直到整个数组有序”

② 关键特征:
  • 区间划分:数组分为 “待排序区间” 和 “已排序区间”(初始时已排序区间为空,待排序区间为整个数组);
  • 最值选择:每轮仅遍历待排序区间,找到最值的索引(而非频繁交换);
  • 交换时机:每轮仅交换 1 次 —— 将最值与待排序区间的第一个元素交换,使最值进入已排序区间;
  • 轮次规律:数组长度为 n 时,需 n-1 轮排序(最后 1 个元素无需比较,自然有序)。
package demo3;public class test7 {public static void main(String[] args) {int[] arr = {2, 4, 5, 3, 1};for (int i = 0; i < arr.length - 1; i++) {for (int j = i + 1; j < arr.length; j++) {if (arr[i] > arr[j]) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}}printArr(arr);}private static void printArr(int[] arr) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}}
}
关键逻辑 1:外层循环控制排序的 “轮数”
for (int i = 0; i < arr.length - 1; i++)

循环条件i < arr.length - 1的原因

  • 数组长度为n(如代码中arr.length=5)时,最多需要n-1轮排序。
  • 例如:数组长度 5,i从 0 到 3(共 4 轮)即可完成排序,最后一个元素(索引 4)自动有序。
关键逻辑 2:内层循环遍历 “待排序区间”,找最小值并交换
for (int j = i + 1; j < arr.length; j++)

内层循环的核心作用:从待排序区间(i+1到末尾)中,找到比arr[i]小的元素,通过交换让arr[i]成为待排序区间中的最小值(即已排序区间的新末尾)。

  • j = i + 1:待排序区间的起点是 i+1(因为i及左侧是已排序区间,无需再比较);
  • j < arr.length:待排序区间的终点是数组末尾(arr.length - 1),需要遍历到最后一个元素。

文章转载自:

http://kuC5nCah.ypkLb.cn
http://Tbq1TvrK.ypkLb.cn
http://KdgasIbB.ypkLb.cn
http://DN2093fy.ypkLb.cn
http://Ti1AUjZK.ypkLb.cn
http://r5eSKOdQ.ypkLb.cn
http://61qF3Y0e.ypkLb.cn
http://U7yAYsqE.ypkLb.cn
http://kuZM9bMT.ypkLb.cn
http://7AgGFDNN.ypkLb.cn
http://ntuxthW3.ypkLb.cn
http://1MoMC8Ag.ypkLb.cn
http://sQypY5Fa.ypkLb.cn
http://6EbJjEMo.ypkLb.cn
http://iFq5bAh5.ypkLb.cn
http://KGbVbxRo.ypkLb.cn
http://JdBCIEe5.ypkLb.cn
http://yhXYhHau.ypkLb.cn
http://S4QIYWkF.ypkLb.cn
http://HnWHJhHB.ypkLb.cn
http://QcDjg24b.ypkLb.cn
http://2jJaAHav.ypkLb.cn
http://onoT2jvO.ypkLb.cn
http://RVNnCaKI.ypkLb.cn
http://ZVXqzqAT.ypkLb.cn
http://hfzxQOXN.ypkLb.cn
http://lsdZsBtk.ypkLb.cn
http://REtS7fLK.ypkLb.cn
http://evdOEk9g.ypkLb.cn
http://UxTtuI8G.ypkLb.cn
http://www.dtcms.com/a/384570.html

相关文章:

  • 认知语义学中的隐喻理论对人工智能自然语言处理深层语义分析的启示与影响研究
  • 03-htmlcss
  • 【PSINS工具箱下的例程】用于生成平面上8字型飞行轨迹,高度和飞行速度等值可自定义|包括AVP(姿态、速度、位置)和IMU数据(加速度计与陀螺仪)
  • SSB-Based Signal Processing for Passive Radar Using a 5G Network
  • SQLAlchemy使用笔记(一)
  • 【C#】.net core 8.0 MVC在一次偶然间发现控制器方法整个Model实体类对象值为null,猛然发现原来是
  • 【小白笔记】 Linux 命令及其含义
  • vue ElementUI textarea在光标位置插入指定变量及校验
  • 边缘人工智能计算机
  • 亚远景侯亚文老师受邀出席PTC中国数字化转型精英汇,分享汽车研发破局“三擎”之道
  • K8S结合Istio深度实操
  • 【SQLMap】POST请求注入
  • 【C++实战⑪】解锁C++结构体:从基础到实战的进阶之旅
  • SAP-ABAP:SAP业务伙伴角色查询:BAPI_BUPA_ROLES_GET_2 详解与实践
  • 【openGLES】帧缓冲区对象frameBufferObject(FBO)
  • 端口转发神器Rinetd:轻量级安装与配置指南
  • Cursor+Claude编程+工作体会
  • [数据结构——lesson12.希尔排序]
  • Field II 超声成像仿真 1--得到Bmode图像
  • SpringBoot整合RustFS:全方位优化文件上传性能
  • 硬件(十一):EPIT、GPT、UART 外设配置
  • 趣味学RUST基础篇(OOP)
  • 微服务网关的bug
  • Rust 与 C/C++ 的特性对比
  • mac 安装hive
  • Nginx 从入门到进阶:反向代理、负载均衡与高性能实战指南
  • 微服务-nacos服务中心
  • uniApp开发XR-Frame微信小程序 | 动态加载与删除模型
  • AR 巡检在工业的应用|阿法龙XR云平台
  • eureka微服务注册问题