Преглед изворни кода

feat: 新增Demo06插入排序代码及笔记更新

- 新增Demo06.java(插入排序实现)
- 更新Demo05.java注释(补充优缺点说明)
- 更新20260716笔记:新增插入排序章节(算法图解、代码实现、三种排序对比表)
- 更新完整代码汇总新增Demo06.java代码
- 补充随堂练习要点(插入排序相关要点)
- 补充拓展阅读(插入排序概念、复杂度及三种排序对比)
WanJL пре 2 дана
родитељ
комит
f919ec5a9c

+ 2 - 0
授课代码/c260716/src/Demo05.java

@@ -13,6 +13,8 @@ public class Demo05 {
         选择排序的思路要比冒泡排序更聪明一点,冒泡排序是不断的交换相邻的元素,而选择排序,是每轮只做一次交换。
         选择的思路是 每一轮从剩余未排序的元素中找出最小的元素,直接把它放到未排序部分的第1位。
         核心思路:每一轮从未排序区间选出最小值,与未排序区间的第1个元素进行交换。
+        优点:交换次数比冒泡更少
+        缺点:不稳定,可能会破坏相对顺序
      */
     public static void main(String[] args) {
         /*

+ 46 - 0
授课代码/c260716/src/Demo06.java

@@ -0,0 +1,46 @@
+import java.util.Arrays;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo06
+ * @description 数组排序-插入排序
+ * @create 2026/7/16
+ */
+public class Demo06 {
+    /*
+        插入排序
+        是三种基础排序中最实用的一种,也是使用最广泛的一种O(n2)排序算法,他的思想非常符合人整理东西的习惯。
+        把数组分为“已排序”和“未排序”两部分,每次从未排序部分 取一个元素,插入到已排序部分的正确位置。
+        就类似于 打扑克 时,抓牌然后 插入到手中已排序好的牌中正确的位置。
+        核心思想:
+            把数组视为“已排序”(前部分)和未排序(后部分)。每次从未排序部分中拿取第1个元素,
+            从后往前在已排序部分中找到合适的位置(比较)并插入
+     */
+    public static void main(String[] args) {
+        /*
+            插入排序
+            把数组分为两部分:已排序、未排序
+            每次取未排序的第1个元素插入到已排序的正确位置
+         */
+
+        int[] arr={1,456,782,5,8,4,598,1,46,68,465,74};
+        //我们要从第2个元素开始,到长度减1,这个区间,暂时假定为未排序区间
+        //从第2个元素开始,假设 arr[0]已经是已排序部分
+        for (int i = 1; i <arr.length; i++) {
+            //获取当前要插入的元素,未排序的第1个
+            int current=arr[i];
+            //已排序部分的最后一个索引值
+            int j=i-1;
+            //从后往前扫描已经排序部分,寻找插入位置
+            //如果已排序的元素大于current,就向后移动一位。
+            while (j>=0&&arr[j]>current){
+                arr[j+1]=arr[j];
+                j--;
+            }
+            //这个时候 j+1 就是current应该插入到的位置
+            arr[j+1]=current;
+            System.out.println("第"+(i)+"轮插入后:"+ Arrays.toString(arr));
+        }
+    }
+}

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

@@ -437,7 +437,107 @@ for (int i = 0; i < arr.length - 1; i++) {
 
 ---
 
-## 六、课堂练习详解
+## 六、插入排序(Insertion Sort)
+
+### 1. 插入排序概述
+
+**插入排序**是三种基础排序(冒泡、选择、插入)中最实用、使用最广泛的一种 O(n²) 排序算法。它的思想非常符合人类整理东西的习惯——就像**打扑克牌时抓牌插入到手牌的正确位置**。
+
+#### 核心思想
+
+> 把数组分为"**已排序**"和"**未排序**"两部分。每次从**未排序部分**取出第 1 个元素,在**已排序部分**从后往前找到合适的位置并插入。
+
+#### 算法步骤图解
+
+以数组 `{5, 3, 8, 6, 4}` 为例,从小到大排序:
+
+```
+初始: [5 | 3, 8, 6, 4]   竖线左边为已排序,右边为未排序
+
+第1轮(i=1):取未排序第1个元素 current=3,与已排序部分 [5] 比较
+  5 > 3 → 5 向后移 → [5, 5 | 8, 6, 4]
+  找到插入位置 j+1=0,插入 current=3
+  → [3, 5 | 8, 6, 4]  ← 3 和 5 有序
+
+第2轮(i=2):取 current=8,与已排序部分 [3, 5] 比较
+  5 < 8 → 无需移动,直接插入在末尾
+  → [3, 5, 8 | 6, 4]  ← 8 已在正确位置
+
+第3轮(i=3):取 current=6,与已排序部分 [3, 5, 8] 比较
+  8 > 6 → 8 向后移 → [3, 5, 8, 8 | 4]
+  5 < 6 → 停止,找到插入位置 j+1=2,插入 current=6
+  → [3, 5, 6, 8 | 4]  ← 6 插入到 5 和 8 之间
+
+第4轮(i=4):取 current=4,与已排序部分 [3, 5, 6, 8] 比较
+  8 > 4 → 8 向后移 → [3, 5, 6, 8, 8]
+  6 > 4 → 6 向后移 → [3, 5, 6, 6, 8]
+  5 > 4 → 5 向后移 → [3, 5, 5, 6, 8]
+  3 < 4 → 停止,找到插入位置 j+1=1,插入 current=4
+  → [3, 4, 5, 6, 8] ✅ 排序完成
+```
+
+---
+
+### 2. 插入排序代码实现
+
+```java
+import java.util.Arrays;
+
+int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
+
+// 从第2个元素开始,假设 arr[0] 已经是已排序部分
+for (int i = 1; i < arr.length; i++) {
+    // 获取当前要插入的元素(未排序的第1个)
+    int current = arr[i];
+    // 已排序部分的最后一个索引值
+    int j = i - 1;
+    // 从后往前扫描已排序部分,寻找插入位置
+    // 如果已排序的元素大于 current,就向后移动一位
+    while (j >= 0 && arr[j] > current) {
+        arr[j + 1] = arr[j];
+        j--;
+    }
+    // 此时 j+1 就是 current 应该插入的位置
+    arr[j + 1] = current;
+    System.out.println("第" + i + "轮插入后:" + Arrays.toString(arr));
+}
+```
+
+**代码要点**:
+
+| 部分 | 说明 |
+|------|------|
+| **外层循环** `i` | 从索引 1 开始,`i` 是当前要处理的"未排序的第 1 个元素"位置 |
+| **`current`** | 暂存当前要插入的元素值,防止被覆盖 |
+| **内层 while 循环** | 从后往前扫描已排序部分,比 `current` 大的元素逐一后移 |
+| **`arr[j+1] = current`** | 在找到的正确位置插入当前元素 |
+| **循环条件** | `j >= 0 && arr[j] > current` — 未越界且当前比较值比 `current` 大 |
+
+---
+
+### 3. 三种排序算法对比
+
+| 特性 | 冒泡排序 | 选择排序 | 插入排序 |
+|------|---------|---------|---------|
+| **核心思路** | 相邻比较,大的往后冒 | 每轮选最小值交换 | 将元素插入已排序区的正确位置 |
+| **每轮交换次数** | 多次(可能 n 次) | **最多 1 次** | 多次(元素后移) |
+| **平均时间复杂度** | O(n²) | O(n²) | O(n²) |
+| **最好时间复杂度** | O(n) — 优化版 | O(n²) | **O(n)** — 已有序 |
+| **最坏时间复杂度** | O(n²) | O(n²) | O(n²) |
+| **空间复杂度** | O(1) | O(1) | O(1) |
+| **稳定性** | ✅ 稳定 | ❌ 不稳定 | ✅ 稳定 |
+| **实用性** | 低(教学用) | 中(交换成本高时适用) | **高**(小规模数据首选) |
+
+> **三种排序的实用性排序**:插入排序 > 选择排序 > 冒泡排序
+>
+> **为什么插入排序最实用?**
+> - 当数据**基本有序**时,插入排序的时间复杂度可达到 **O(n)**
+> - 插入排序是**稳定的**,不会破坏相等元素的相对顺序
+> - 插入排序的代码简洁,实际运行效率在 O(n²) 级别中最高
+
+---
+
+## 七、课堂练习详解
 
 ### 练习 01:首尾对称交换数组元素
 
@@ -586,7 +686,7 @@ for (int i = 0; i < newArr.length; i++) {
 
 ---
 
-## 、课后作业讲解(7月15日作业)
+## 、课后作业讲解(7月15日作业)
 
 ### 作业 07:数组元素求和
 
@@ -674,7 +774,7 @@ System.out.println("奇数个数:" + count + ",偶数个数:" + (arr.lengt
 
 ---
 
-## 、随堂练习要点
+## 、随堂练习要点
 
 1. **多维数组本质**:数组的数组,二维数组的元素是一维数组的引用地址
 2. **二维数组定义**:`int[][] arr = new int[3][2];`(3 行 2 列)
@@ -698,21 +798,28 @@ System.out.println("奇数个数:" + count + ",偶数个数:" + (arr.lengt
 20. **选择排序-minIndex**:记录最小元素的索引值,初始为 `i`
 21. **选择排序-交换条件**:`minIndex != i` 时才交换,每轮最多交换 1 次
 22. **选择排序 vs 冒泡排序**:选择排序每轮最多交换 1 次,冒泡排序可能交换多次
-23. **变量交换-临时变量法**:`tmp = a; a = b; b = tmp;`
-24. **变量交换-加减法**:`a = a + b; b = a - b; a = a - b;`
-25. **首尾交换**:利用对称性,第 i 位与第 length-1-i 位交换
-26. **循环右移**:从后往前逐个移动,保存最后一个元素放到开头
-27. **取模优化**:`k = k % arr.length` 解决 k 大于数组长度的问题
-28. **数组去重思路**:统计不重复个数 → 创建新数组 → 填充不重复元素
-29. **布尔标记法**:使用 `boolean` 变量标记元素是否重复
-30. **打擂法找最大值**:假设第一个是最大值,遇到更大的就更新
-31. **数组求和**:遍历数组,累加每个元素
-32. **倒序输出**:从 `length-1` 到 `0` 递减遍历
-33. **奇偶判断**:`n % 2 == 0` 偶,`n % 2 != 0` 奇
+23. **插入排序**:将未排序的第 1 个元素插入到已排序部分的正确位置
+24. **插入排序-分治思想**:把数组分为"已排序"和"未排序"两部分
+25. **插入排序-外层循环**:`i` 从 1 开始,标记当前要处理的未排序元素
+26. **插入排序-current**:暂存当前要插入的元素值,防止被覆盖
+27. **插入排序-内层 while**:从后往前扫描已排序部分,比 current 大的元素逐一后移
+28. **插入排序-实用性**:三种基础排序中最实用,数据基本有序时效率可达 O(n)
+29. **三种排序实用性排序**:插入排序 > 选择排序 > 冒泡排序
+30. **变量交换-临时变量法**:`tmp = a; a = b; b = tmp;`
+31. **变量交换-加减法**:`a = a + b; b = a - b; a = a - b;`
+32. **首尾交换**:利用对称性,第 i 位与第 length-1-i 位交换
+33. **循环右移**:从后往前逐个移动,保存最后一个元素放到开头
+34. **取模优化**:`k = k % arr.length` 解决 k 大于数组长度的问题
+35. **数组去重思路**:统计不重复个数 → 创建新数组 → 填充不重复元素
+36. **布尔标记法**:使用 `boolean` 变量标记元素是否重复
+37. **打擂法找最大值**:假设第一个是最大值,遇到更大的就更新
+38. **数组求和**:遍历数组,累加每个元素
+39. **倒序输出**:从 `length-1` 到 `0` 递减遍历
+40. **奇偶判断**:`n % 2 == 0` 偶,`n % 2 != 0` 奇
 
 ---
 
-## 九、知识拓展
+## 、知识拓展
 
 ### 1. 数组的地址值
 
@@ -771,7 +878,7 @@ System.out.println(a);  // 输出类似:[I@b4c966a
 
 ---
 
-## 十、完整代码汇总
+## 十、完整代码汇总
 
 ### Demo01.java — 多维数组
 
@@ -1003,6 +1110,8 @@ public class Demo05 {
         选择的思路是每一轮从剩余未排序的元素中找出最小的元素,
         直接把它放到未排序部分的第1位。
         核心思路:每一轮从未排序区间选出最小值,与未排序区间的第1个元素进行交换。
+        优点:交换次数比冒泡更少
+        缺点:不稳定,可能会破坏相对顺序
      */
     public static void main(String[] args) {
         int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
@@ -1028,6 +1137,53 @@ public class Demo05 {
 }
 ```
 
+### Demo06.java — 插入排序
+
+```java
+import java.util.Arrays;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo06
+ * @description 数组排序-插入排序
+ * @create 2026/7/16
+ */
+public class Demo06 {
+    /*
+        插入排序
+        是三种基础排序中最实用的一种,也是使用最广泛的一种O(n²)排序算法,
+        他的思想非常符合人整理东西的习惯。
+        把数组分为"已排序"和"未排序"两部分,每次从未排序部分取一个元素,
+        插入到已排序部分的正确位置。
+        就类似于打扑克时,抓牌然后插入到手中已排序好的牌中正确的位置。
+        核心思想:
+            把数组视为"已排序"(前部分)和未排序(后部分)。
+            每次从未排序部分中拿取第1个元素,
+            从后往前在已排序部分中找到合适的位置(比较)并插入
+     */
+    public static void main(String[] args) {
+        int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
+        //从第2个元素开始,假设 arr[0] 已经是已排序部分
+        for (int i = 1; i < arr.length; i++) {
+            //获取当前要插入的元素,未排序的第1个
+            int current = arr[i];
+            //已排序部分的最后一个索引值
+            int j = i - 1;
+            //从后往前扫描已经排序部分,寻找插入位置
+            //如果已排序的元素大于current,就向后移动一位
+            while (j >= 0 && arr[j] > current) {
+                arr[j + 1] = arr[j];
+                j--;
+            }
+            //此时 j+1 就是current应该插入到的位置
+            arr[j + 1] = current;
+            System.out.println("第" + i + "轮插入后:" + Arrays.toString(arr));
+        }
+    }
+}
+```
+
 ### Exercises01.java — 首尾对称交换
 
 ```java
@@ -1250,7 +1406,7 @@ public class HomeWork10 {
 
 ---
 
-## 十、拓展阅读
+## 十、拓展阅读
 
 | 概念 | 说明 |
 |------|------|
@@ -1279,6 +1435,16 @@ public class HomeWork10 {
 | **选择排序-空间复杂度** | O(1),原地排序 |
 | **选择排序-稳定性** | ❌ 不稳定(交换可能改变相等元素的相对顺序) |
 | **冒泡 vs 选择** | 冒泡多次交换,选择最多 1 次;冒泡稳定,选择不稳定 |
+| **插入排序** | 将未排序的第 1 个元素插入到已排序部分的正确位置,类似打扑克理牌 |
+| **插入排序-分治思想** | 把数组分为"已排序"和"未排序"两部分 |
+| **插入排序-外层循环** | `i` 从 1 开始,标记当前要处理的未排序元素 |
+| **插入排序-current** | 暂存当前要插入的元素值,防止被覆盖 |
+| **插入排序-内层 while** | 从后往前扫描,比 current 大的元素逐一后移,找到插入位置 |
+| **插入排序-时间复杂度** | 平均 O(n²),最好 O(n)(数据基本有序时),最坏 O(n²) |
+| **插入排序-空间复杂度** | O(1),原地排序 |
+| **插入排序-稳定性** | ✅ 稳定 |
+| **插入排序-实用性** | 三种基础 O(n²) 排序中最实用,小规模数据首选 |
+| **三种排序对比** | 实用性:插入 > 选择 > 冒泡;稳定性:冒泡 ✅、插入 ✅、选择 ❌ |
 | **变量交换-临时变量** | `tmp = a; a = b; b = tmp;` 最直观的交换方式 |
 | **变量交换-加减法** | `a = a + b; b = a - b; a = a - b;` 不借助第三个变量 |
 | **数组循环右移** | 从后往前逐位移动,保存最后一位到第一位,外层套循环控制移动次数 |