Jelajahi Sumber

feat: 新增Demo04冒泡排序代码及笔记更新

- 新增Demo04.java(冒泡排序基础版+优化版)
- 更新20260716笔记:新增冒泡排序章节(算法图解、基础版、优化版)
- 补充随堂练习要点(冒泡排序相关要点)
- 补充拓展阅读(冒泡排序概念及复杂度)
- 完整代码汇总新增Demo04.java代码
WanJL 3 hari lalu
induk
melakukan
be2fcc5c48

+ 67 - 0
授课代码/c260716/src/Demo04.java

@@ -0,0 +1,67 @@
+import java.util.Arrays;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo04
+ * @description 数组排序-冒泡排序
+ * @create 2026/7/16
+ */
+public class Demo04 {
+    /*
+        我们现在已经学了最基础的  数据结构与算法
+        数据结构:数组结构--属于线性结构的一种
+        算法:比如排序算法(冒泡、选择、插入...)
+        冒泡排序,是所有排序【算法】最直观的,最基础的一种。
+        冒泡排序就像水里的泡泡一样,大的元素会逐渐的冒到数组的末端。
+
+        冒泡排序的核心思想:
+            重复遍历数组,依次比较相邻的两个元素,如果顺序错误(假如是从小到大排序,但a比b大)就交换。
+            每轮遍历把当前未排序的部分的最大值“冒泡”到正确位置。
+     */
+    public static void main(String[] args) {
+        /*
+            冒泡排序的基础版
+            每一轮把未排序部分的最大值“冒泡”到数组末尾
+         */
+        int[] arr={1,456,782,5,8,4,598,1,46,68,465,74};
+        //冒泡排序需要两层循环
+        for (int i = 0; i <arr.length-1; i++) {  //外层循环,控制的一共冒泡多少轮
+            //内层循环 在未排序的区间[0,arr.length-1-i] 中相邻比较
+            //因为 i 后面的元素已经有序了,不需要再比较了
+            for (int j = 0; j < arr.length-1-i; j++) {
+                if (arr[j]>arr[j+1]){
+                    //交换位置
+                    arr[j]=arr[j]+arr[j+1];
+                    arr[j+1]=arr[j]-arr[j+1];
+                    arr[j]=arr[j]-arr[j+1];
+                }
+            }
+        }
+        System.out.println(Arrays.toString(arr));
+
+        // 优化版
+        int[] arr2={1,456,782,5,8,4,598,1,46,68,465,74};
+        //冒泡排序需要两层循环
+        for (int i = 0; i <arr2.length-1; i++) {  //外层循环,控制的一共冒泡多少轮
+            boolean b=false;    //定义boolean类型的变量,用来标记本轮是否有交换
+            //内层循环 在未排序的区间[0,arr.length-1-i] 中相邻比较
+            //因为 i 后面的元素已经有序了,不需要再比较了
+            for (int j = 0; j < arr2.length-1-i; j++) {
+                if (arr2[j]>arr2[j+1]){
+                    //交换位置
+                    arr2[j]=arr2[j]+arr2[j+1];
+                    arr2[j+1]=arr2[j]-arr2[j+1];
+                    arr2[j]=arr2[j]-arr2[j+1];
+                    b=true; //本轮有交换
+                }
+            }
+            //判断,如果本轮没有发生任何交换,说明数组已经有序,提前结束
+            if (b==false){
+                break;
+            }
+            System.out.println("第"+(i+1)+"轮冒泡后:"+Arrays.toString(arr2));
+        }
+
+    }
+}

+ 217 - 17
课堂笔记/01-JavaSE基础-笔记/20260716-笔记.md

@@ -200,7 +200,129 @@ System.out.println(index);  // 输出:10(排序后 689 在索引 10)
 
 ---
 
-## 四、课堂练习详解
+## 四、冒泡排序(Bubble Sort)
+
+### 1. 冒泡排序概述
+
+**冒泡排序**是所有排序算法中最直观、最基础的一种。它的名字来源于排序过程就像水里的泡泡一样,大的元素会逐渐"冒"到数组的末端。
+
+#### 核心思想
+
+> 重复遍历数组,依次比较**相邻的两个元素**,如果顺序错误(如从小到大排序时前一个比后一个大)就交换。每轮遍历把当前未排序部分的最大值"冒泡"到正确位置。
+
+#### 算法步骤图解
+
+以数组 `{5, 3, 8, 6, 4}` 为例,从小到大排序:
+
+```
+第1轮(i=0):将最大值 8 冒泡到最后
+  [5, 3, 8, 6, 4] → 比较 5>3 → 交换 → [3, 5, 8, 6, 4]
+  [3, 5, 8, 6, 4] → 比较 5<8 → 不交换
+  [3, 5, 8, 6, 4] → 比较 8>6 → 交换 → [3, 5, 6, 8, 4]
+  [3, 5, 6, 8, 4] → 比较 8>4 → 交换 → [3, 5, 6, 4, 8]  ← 8 到位
+
+第2轮(i=1):将最大值 6 冒泡到倒数第二位
+  [3, 5, 6, 4, 8] → 比较 3<5 → 不交换
+  [3, 5, 6, 4, 8] → 比较 5<6 → 不交换
+  [3, 5, 6, 4, 8] → 比较 6>4 → 交换 → [3, 5, 4, 6, 8]  ← 6 到位
+
+第3轮(i=2):将最大值 5 冒泡到倒数第三位
+  [3, 5, 4, 6, 8] → 比较 3<5 → 不交换
+  [3, 5, 4, 6, 8] → 比较 5>4 → 交换 → [3, 4, 5, 6, 8]  ← 5 到位
+
+第4轮(i=3):将最大值 4 冒泡到倒数第四位
+  [3, 4, 5, 6, 8] → 比较 3<4 → 不交换  ← 4 已在正确位置
+
+排序完成 ✅
+```
+
+---
+
+### 2. 基础版冒泡排序
+
+```java
+int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
+
+// 冒泡排序需要两层循环
+for (int i = 0; i < arr.length - 1; i++) {
+    // 外层循环:控制冒泡的轮数(共 arr.length-1 轮)
+    // 内层循环:在未排序区间 [0, arr.length-1-i] 中相邻比较
+    // 因为 i 后面的元素已经有序,不需要再比较了
+    for (int j = 0; j < arr.length - 1 - i; j++) {
+        if (arr[j] > arr[j + 1]) {
+            // 交换位置(加减法交换)
+            arr[j] = arr[j] + arr[j + 1];
+            arr[j + 1] = arr[j] - arr[j + 1];
+            arr[j] = arr[j] - arr[j + 1];
+        }
+    }
+}
+System.out.println(Arrays.toString(arr));
+```
+
+**代码要点**:
+| 部分 | 说明 |
+|------|------|
+| **外层循环** `i` | 控制冒泡的**轮数**,`arr.length - 1` 轮 |
+| **内层循环** `j` | 在未排序区间 **相邻比较**,范围 `[0, arr.length-1-i)` |
+| **`arr.length-1-i`** | 每轮结束后,末尾 `i` 个元素已有序,无需再比较 |
+| **相邻比较** | `arr[j] > arr[j+1]` 表示前大后小,需要交换 |
+
+> **为什么外层循环是 `arr.length-1` 轮?**
+> 因为 n 个元素的数组,最多需要 n-1 轮冒泡就可以完成排序(最后一轮只剩一个元素,无需再排)。
+
+> **为什么内层循环是 `arr.length-1-i`?**
+> 第 i 轮结束后,数组末尾的 i 个元素已经是有序的(每轮冒泡出一个最大值),所以内层只需要比较前 `arr.length-1-i` 个元素。
+
+---
+
+### 3. 优化版冒泡排序
+
+如果某轮冒泡过程中**没有发生任何交换**,说明数组已经有序,可以**提前结束排序**。
+
+```java
+int[] arr2 = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
+
+for (int i = 0; i < arr2.length - 1; i++) {
+    boolean b = false;  // 标记本轮是否有交换
+    for (int j = 0; j < arr2.length - 1 - i; j++) {
+        if (arr2[j] > arr2[j + 1]) {
+            // 交换位置
+            arr2[j] = arr2[j] + arr2[j + 1];
+            arr2[j + 1] = arr2[j] - arr2[j + 1];
+            arr2[j] = arr2[j] - arr2[j + 1];
+            b = true;   // 本轮有交换
+        }
+    }
+    // 如果本轮没有发生任何交换,说明数组已经有序,提前结束
+    if (b == false) {
+        break;
+    }
+    System.out.println("第" + (i + 1) + "轮冒泡后:" + Arrays.toString(arr2));
+}
+```
+
+**优化点**:引入 `boolean` 标记变量,如果某一轮没有发生交换,说明数组已有序,直接 `break` 退出循环。
+
+---
+
+### 4. 冒泡排序总结
+
+| 特性 | 说明 |
+|------|------|
+| **算法类型** | 比较排序、交换排序 |
+| **时间复杂度(平均)** | O(n²) |
+| **时间复杂度(最好)** | O(n) — 数组已有序(优化版) |
+| **时间复杂度(最坏)** | O(n²) — 数组逆序 |
+| **空间复杂度** | O(1) — 原地排序,不需要额外空间 |
+| **稳定性** | ✅ 稳定(相等元素的相对位置不变) |
+| **适用场景** | 小规模数据排序,教学演示 |
+
+> **数据结构与算法初探**:数组是最基础的**线性结构**之一,冒泡排序是最基础的**排序算法**之一。后续还会学习选择排序、插入排序、快速排序等更多算法。
+
+---
+
+## 五、课堂练习详解
 
 ### 练习 01:首尾对称交换数组元素
 
@@ -349,7 +471,7 @@ for (int i = 0; i < newArr.length; i++) {
 
 ---
 
-## 、课后作业讲解(7月15日作业)
+## 、课后作业讲解(7月15日作业)
 
 ### 作业 07:数组元素求和
 
@@ -437,7 +559,7 @@ System.out.println("奇数个数:" + count + ",偶数个数:" + (arr.lengt
 
 ---
 
-## 、随堂练习要点
+## 、随堂练习要点
 
 1. **多维数组本质**:数组的数组,二维数组的元素是一维数组的引用地址
 2. **二维数组定义**:`int[][] arr = new int[3][2];`(3 行 2 列)
@@ -451,21 +573,25 @@ System.out.println("奇数个数:" + count + ",偶数个数:" + (arr.lengt
 10. **`Arrays.copyOf(arr, n)`**:拷贝数组,指定新长度,多余补默认值
 11. **`Arrays.copyOfRange(arr, from, to)`**:拷贝指定范围 [from, to)
 12. **`Arrays.binarySearch(arr, key)`**:二分查找(必须先排序)
-13. **变量交换-临时变量法**:`tmp = a; a = b; b = tmp;`
-14. **变量交换-加减法**:`a = a + b; b = a - b; a = a - b;`
-15. **首尾交换**:利用对称性,第 i 位与第 length-1-i 位交换
-16. **循环右移**:从后往前逐个移动,保存最后一个元素放到开头
-17. **取模优化**:`k = k % arr.length` 解决 k 大于数组长度的问题
-18. **数组去重思路**:统计不重复个数 → 创建新数组 → 填充不重复元素
-19. **布尔标记法**:使用 `boolean` 变量标记元素是否重复
-20. **打擂法找最大值**:假设第一个是最大值,遇到更大的就更新
-21. **数组求和**:遍历数组,累加每个元素
-22. **倒序输出**:从 `length-1` 到 `0` 递减遍历
-23. **奇偶判断**:`n % 2 == 0` 偶,`n % 2 != 0` 奇
+13. **冒泡排序**:重复遍历数组,相邻元素两两比较,大的往后冒
+14. **冒泡排序-外层循环**:控制冒泡轮数,共 `arr.length - 1` 轮
+15. **冒泡排序-内层循环**:在未排序区间 `[0, arr.length-1-i]` 相邻比较
+16. **冒泡排序-优化**:引入 `boolean` 标记,本轮无交换则提前结束
+18. **变量交换-临时变量法**:`tmp = a; a = b; b = tmp;`
+19. **变量交换-加减法**:`a = a + b; b = a - b; a = a - b;`
+20. **首尾交换**:利用对称性,第 i 位与第 length-1-i 位交换
+21. **循环右移**:从后往前逐个移动,保存最后一个元素放到开头
+22. **取模优化**:`k = k % arr.length` 解决 k 大于数组长度的问题
+23. **数组去重思路**:统计不重复个数 → 创建新数组 → 填充不重复元素
+24. **布尔标记法**:使用 `boolean` 变量标记元素是否重复
+25. **打擂法找最大值**:假设第一个是最大值,遇到更大的就更新
+26. **数组求和**:遍历数组,累加每个元素
+27. **倒序输出**:从 `length-1` 到 `0` 递减遍历
+28. **奇偶判断**:`n % 2 == 0` 偶,`n % 2 != 0` 奇
 
 ---
 
-## 七、知识拓展
+## 、知识拓展
 
 ### 1. 数组的地址值
 
@@ -524,7 +650,7 @@ System.out.println(a);  // 输出类似:[I@b4c966a
 
 ---
 
-## 、完整代码汇总
+## 、完整代码汇总
 
 ### Demo01.java — 多维数组
 
@@ -668,6 +794,74 @@ public class Demo03 {
 }
 ```
 
+### Demo04.java — 冒泡排序
+
+```java
+import java.util.Arrays;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo04
+ * @description 数组排序-冒泡排序
+ * @create 2026/7/16
+ */
+public class Demo04 {
+    /*
+        我们现在已经学了最基础的  数据结构与算法
+        数据结构:数组结构--属于线性结构的一种
+        算法:比如排序算法(冒泡、选择、插入...)
+        冒泡排序,是所有排序【算法】最直观的,最基础的一种。
+        冒泡排序就像水里的泡泡一样,大的元素会逐渐的冒到数组的末端。
+
+        冒泡排序的核心思想:
+            重复遍历数组,依次比较相邻的两个元素,如果顺序错误就交换。
+            每轮遍历把当前未排序的部分的最大值"冒泡"到正确位置。
+     */
+    public static void main(String[] args) {
+        /*
+            冒泡排序的基础版
+            每一轮把未排序部分的最大值"冒泡"到数组末尾
+         */
+        int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
+        //冒泡排序需要两层循环
+        for (int i = 0; i < arr.length - 1; i++) {  //外层循环,控制的一共冒泡多少轮
+            //内层循环 在未排序的区间[0,arr.length-1-i] 中相邻比较
+            //因为 i 后面的元素已经有序了,不需要再比较了
+            for (int j = 0; j < arr.length - 1 - i; j++) {
+                if (arr[j] > arr[j + 1]) {
+                    //交换位置(加减法交换)
+                    arr[j] = arr[j] + arr[j + 1];
+                    arr[j + 1] = arr[j] - arr[j + 1];
+                    arr[j] = arr[j] - arr[j + 1];
+                }
+            }
+        }
+        System.out.println(Arrays.toString(arr));
+
+        // 优化版
+        int[] arr2 = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
+        for (int i = 0; i < arr2.length - 1; i++) {
+            boolean b = false;    //定义boolean类型的变量,用来标记本轮是否有交换
+            for (int j = 0; j < arr2.length - 1 - i; j++) {
+                if (arr2[j] > arr2[j + 1]) {
+                    //交换位置
+                    arr2[j] = arr2[j] + arr2[j + 1];
+                    arr2[j + 1] = arr2[j] - arr2[j + 1];
+                    arr2[j] = arr2[j] - arr2[j + 1];
+                    b = true; //本轮有交换
+                }
+            }
+            //判断,如果本轮没有发生任何交换,说明数组已经有序,提前结束
+            if (b == false) {
+                break;
+            }
+            System.out.println("第" + (i + 1) + "轮冒泡后:" + Arrays.toString(arr2));
+        }
+    }
+}
+```
+
 ### Exercises01.java — 首尾对称交换
 
 ```java
@@ -890,7 +1084,7 @@ public class HomeWork10 {
 
 ---
 
-## 、拓展阅读
+## 、拓展阅读
 
 | 概念 | 说明 |
 |------|------|
@@ -904,6 +1098,12 @@ public class HomeWork10 {
 | **Arrays.copyOf()** | 数组拷贝,可指定新长度,扩展部分补默认值,截断部分丢弃 |
 | **Arrays.copyOfRange()** | 拷贝数组指定范围 `[from, to)`,包含 from 不包含 to |
 | **Arrays.binarySearch()** | 二分查找,数组必须先排序,返回索引值(未找到返回负数) |
+| **冒泡排序** | 重复遍历数组,相邻元素两两比较,大的往后冒,每轮把最大值冒泡到末尾 |
+| **冒泡排序-外层循环** | 控制冒泡轮数,n 个元素最多需要 n-1 轮 |
+| **冒泡排序-内层循环** | 在未排序区间 `[0, arr.length-1-i]` 相邻比较,比较范围随轮数递减 |
+| **冒泡排序-优化** | 引入 `boolean` 标记判断本轮是否有交换,无交换则提前结束排序 |
+| **冒泡排序-时间复杂度** | 平均 O(n²),最好 O(n)(优化版已有序),最坏 O(n²) |
+| **冒泡排序-空间复杂度** | O(1),原地排序,不需要额外空间 |
 | **变量交换-临时变量** | `tmp = a; a = b; b = tmp;` 最直观的交换方式 |
 | **变量交换-加减法** | `a = a + b; b = a - b; a = a - b;` 不借助第三个变量 |
 | **数组循环右移** | 从后往前逐位移动,保存最后一位到第一位,外层套循环控制移动次数 |