# 2026-07-15 数组进阶练习（课堂练习用）

> **说明**：以下题目为数组的进阶应用，作为课堂练习或学有余力的同学额外挑战使用。

---

### 练习 1：数组元素首尾交换

**难度**：⭐⭐  
**知识点**：数组索引操作、变量交换

定义一个整数数组 `{10, 20, 30, 40, 50}`，将数组中第 1 个元素与第 5 个元素交换，第 2 个元素与第 4 个元素交换（即首尾对称交换），然后输出交换后的数组。

**示例输出**：
```
交换前：10 20 30 40 50
交换后：50 40 30 20 10
```

> **提示**：可以用变量交换的经典三步法 `temp = a; a = b; b = temp;`。

**参考代码**：
```java
/**
 * @title 练习1：数组元素首尾交换
 * @create 2026/7/15
 */
public class Practice01 {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30, 40, 50};

        // 输出交换前的数组
        System.out.print("交换前：");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();

        // 首尾对称交换：第1位↔第5位，第2位↔第4位
        // 即 arr[0]↔arr[4], arr[1]↔arr[3]
        int temp = arr[0];
        arr[0] = arr[4];
        arr[4] = temp;

        temp = arr[1];
        arr[1] = arr[3];
        arr[3] = temp;

        // 输出交换后的数组
        System.out.print("交换后：");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}
```

---

### 练习 2：数组元素循环右移

**难度**：⭐⭐⭐  
**知识点**：数组操作、循环、下标运算

定义一个整数数组 `{1, 2, 3, 4, 5}`，将数组中的所有元素向右移动 2 个位置。超出数组范围的元素循环移到数组开头（循环右移）。

**示例**：
```
移动前：1 2 3 4 5
右移2位：4 5 1 2 3
```

**要求**：
- 在原数组上操作，不创建新数组
- 移动位数用一个变量 `k = 2` 控制
- 思考：如果 k 大于数组长度怎么办？（提示：`k = k % 数组长度`）

**参考代码**：
```java
/**
 * @title 练习2：数组元素循环右移
 * @create 2026/7/15
 */
public class Practice02 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int k = 2;  // 右移位数

        // 如果 k 大于数组长度，取模简化
        k = k % arr.length;

        // 输出移动前的数组
        System.out.print("移动前：");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();

        // 整体右移 k 位（循环右移）
        for (int i = 0; i < k; i++) {
            // 保存最后一个元素
            int last = arr[arr.length - 1];
            // 从后往前，每个元素往后移一位
            for (int j = arr.length - 1; j > 0; j--) {
                arr[j] = arr[j - 1];
            }
            // 把最后一个元素放到第一位
            arr[0] = last;
        }

        // 输出移动后的数组
        System.out.print("右移" + k + "位后：");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}
```

---

### 练习 3：数组去重

**难度**：⭐⭐⭐  
**知识点**：数组遍历、嵌套循环、if 判断、新数组

定义一个整数数组 `{5, 2, 8, 2, 5, 9, 1, 8, 3}`，去除数组中重复的元素，只保留第一次出现的元素，输出去重后的新数组。

**要求**：
- 不能使用 `Set` 等集合类
- 只能使用数组和循环实现
- 去重后的元素顺序保持原顺序

**示例输出**：
```
原数组：   5 2 8 2 5 9 1 8 3
去重后：   5 2 8 9 1 3
```

**提示**：
- 可以先统计不重复元素的个数，再创建新数组
- 或者创建一个足够大的新数组，遍历原数组判断是否已存在

**参考代码**：
```java
/**
 * @title 练习3：数组去重
 * @create 2026/7/15
 */
public class Practice03 {
    public static void main(String[] args) {
        int[] arr = {5, 2, 8, 2, 5, 9, 1, 8, 3};

        // 输出原数组
        System.out.print("原数组：");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();

        // 第一步：统计不重复元素的个数
        int count = 0;  // 不重复元素的个数
        for (int i = 0; i < arr.length; i++) {
            boolean isRepeat = false;
            // 检查 arr[i] 在它之前是否出现过
            for (int j = 0; j < i; j++) {
                if (arr[i] == arr[j]) {
                    isRepeat = true;
                    break;
                }
            }
            if (!isRepeat) {
                count++;  // 不重复，计数+1
            }
        }

        // 第二步：创建新数组，存放不重复的元素
        int[] newArr = new int[count];
        int index = 0;  // 新数组的当前索引
        for (int i = 0; i < arr.length; i++) {
            boolean isRepeat = false;
            // 检查 arr[i] 是否已放入新数组
            for (int j = 0; j < i; j++) {
                if (arr[i] == arr[j]) {
                    isRepeat = true;
                    break;
                }
            }
            if (!isRepeat) {
                newArr[index] = arr[i];
                index++;
            }
        }

        // 输出去重后的数组
        System.out.print("去重后：");
        for (int i = 0; i < newArr.length; i++) {
            System.out.print(newArr[i] + " ");
        }
        System.out.println();
    }
}
```

---
### 练习 4：双色球号码生成器

**难度**：⭐⭐⭐⭐  
**知识点**：数组、随机数、嵌套循环、去重

模拟双色球彩票号码生成：

**规则**：
1. 红球：从 1~33 中随机选出 **6 个不重复** 的号码
2. 蓝球：从 1~16 中随机选出 **1 个** 号码

**要求**：
- 红球号码存入一个 int 数组
- 蓝球号码单独用一个 int 变量存储
- 红球号码不能重复（每次生成新号码时，需要检查是否已存在）

**示例输出**：
```
红球号码：05 12 18 23 27 31
蓝球号码：09
```

**提示**：
```java
// 生成 1~33 之间的随机整数
int num = (int)(Math.random() * 33) + 1;
```

**参考代码**：
```java
/**
 * @title 练习4：双色球号码生成器
 * @create 2026/7/15
 */
public class Practice04 {
    public static void main(String[] args) {
        int[] redBalls = new int[6];  // 红球数组，6个号码
        int blueBall;                 // 蓝球号码

        // 生成红球号码（6个不重复的 1~33）
        int index = 0;  // 已生成的红球个数
        while (index < 6) {
            // 生成一个候选号码
            int num = (int)(Math.random() * 33) + 1;

            // 检查是否已存在（去重）
            boolean isRepeat = false;
            for (int i = 0; i < index; i++) {
                if (redBalls[i] == num) {
                    isRepeat = true;
                    break;
                }
            }

            // 不重复则加入数组
            if (!isRepeat) {
                redBalls[index] = num;
                index++;
            }
        }

        // 生成蓝球号码（1个 1~16）
        blueBall = (int)(Math.random() * 16) + 1;

        // 输出结果
        System.out.print("红球号码：");
        for (int i = 0; i < redBalls.length; i++) {
            // 个位数补0输出，如 05
            if (redBalls[i] < 10) {
                System.out.print("0");
            }
            System.out.print(redBalls[i] + " ");
        }
        System.out.println();

        System.out.print("蓝球号码：");
        if (blueBall < 10) {
            System.out.print("0");
        }
        System.out.println(blueBall);
    }
}
```

---### 练习 5：基本类型 vs 引用类型（代码分析）

**难度**：⭐⭐  
**知识点**：基本数据类型 vs 引用数据类型

```java
public class ThinkDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = a;
        b = 20;
        System.out.println("a = " + a);     // ?
        System.out.println("b = " + b);     // ?

        int[] arr1 = {1, 2, 3};
        int[] arr2 = arr1;
        arr2[0] = 999;
        System.out.println("arr1[0] = " + arr1[0]);   // ?
        System.out.println("arr2[0] = " + arr2[0]);   // ?
    }
}
```

请问以上代码的运行结果是什么？为什么 `arr1[0]` 的值会被改变？结合课堂笔记中"基本数据类型 vs 引用数据类型"的知识，在注释中写出你的分析。

**参考代码**：
```java
/**
 * @title 练习5：基本类型 vs 引用类型
 * @create 2026/7/15
 */
public class Practice05 {
    public static void main(String[] args) {
        /*
            问题分析：

            ① int a = 10; int b = a;
              基本数据类型赋值，b 复制了 a 的值 10，两者独立。
              之后 b = 20，只改变 b，a 不受影响。
              所以 a = 10，b = 20。

            ② int[] arr1 = {1, 2, 3}; int[] arr2 = arr1;
              引用数据类型赋值，arr2 复制的是 arr1 的地址值，
              两者指向堆内存中的同一个数组对象。
              所以 arr2[0] = 999 修改的是它们共同指向的数组，
              arr1[0] 自然也跟着变成了 999。

            结论：
              基本数据类型存的是"值"本身，赋值时各玩各的。
              引用数据类型存的是"地址值"，赋值时多个引用指向同一块内存。
         */

        // ===== 基本数据类型测试 =====
        int a = 10;
        int b = a;   // b 复制 a 的值
        b = 20;      // 只改变 b
        System.out.println("a = " + a);  // 10（a 不受影响）
        System.out.println("b = " + b);  // 20

        // ===== 引用数据类型测试 =====
        int[] arr1 = {1, 2, 3};
        int[] arr2 = arr1;  // arr2 复制 arr1 的地址值
        arr2[0] = 999;      // 通过 arr2 修改数组
        System.out.println("arr1[0] = " + arr1[0]);  // 999（arr1 也变了！）
        System.out.println("arr2[0] = " + arr2[0]);  // 999
    }
}
```

---

> **参考笔记**：[20260715-笔记.md](../课堂笔记/01-JavaSE基础-笔记/20260715-笔记.md) 第三章「数组（Array）」