java练习本
Big Fish Lv1

练习:杨辉三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Arrays;

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
int numberOfRows = 9;
int[][] yangHuiTriangle = new int[numberOfRows][];
yangHuiTriangle[0] = new int[1];
yangHuiTriangle[0][0] = 1;

for (int i = 1; i < numberOfRows; i++) {
yangHuiTriangle[i] = new int[i + 1]; // 初始化当前行
for (int j = 0; j < i; j++) {
if (j == 0 || j == i - 1) {
yangHuiTriangle[i][j] = 1;
continue;
}
yangHuiTriangle[i][j] = yangHuiTriangle[i - 1][j - 1] + yangHuiTriangle[i - 1][j];
}
}
for (int[] row : yangHuiTriangle) {
for (int element : row) {
if (element == 0) continue;
System.out.print(element + " ");
}
System.out.println();
}
}
}

在写杨辉三角案例的时候发现了一个二维数组初始化的问题。

如果只初始化行的话, 二维的数组默认值是null

1
2
3
int numberOfRows = 5; // 例如5行
int[][] yangHuiTriangle = new int[numberOfRows][]; // 只初始化行
yangHuiTriangle[0][0] = 1;

如果不初始化就进行赋值的话就会报错

1
2
Exception in thread "main" java.lang.NullPointerException: Cannot store to int array because "yangHuiTriangle[0]" is null
at Main.main(Main.java:10)

所以必须初始化之后在进行赋值

1
2
3
4
int numberOfRows = 5; // 例如5行
int[][] yangHuiTriangle = new int[numberOfRows][]; // 只初始化行
yangHuiTriangle[0] = new int[1];
yangHuiTriangle[0][0] = 1;

数组初始化并赋值的2种方式

1
2
3
4
5
6
// 方式一
int[] arr = []{1,2,3,4}

//方式二
int[] arr = {1,2,3,4}

练习:随机赋值

创建一个长度为6的int型数组,要求数组元素的值都在1-30之间, 且是随机赋值。同时,要求元素的值各不相同。

1
2
3
4
5
6
7
8
9
10
int[] arr = new int[6];

for(int i = 0; i < arr.length; i++){
for(int j = 0; j < i; j++){
if(arr[i] == arr[j]){
i--;
break;
}
}
}

练习:遍历扑克牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.Arrays;


public class Main {
public static void main(String[] args) {
String[] suit = new String[]{"♣", "♦", "♥", "♠"};
String[] scores = new String[]{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
String[] poker = new String[56];

int total = 0;
for (int i = 0; i < suit.length; i++) {
for (int j = 0; j < scores.length; j++) {
poker[total++] = suit[i] + scores[j];
}
}

poker[total++] = "Big Joker";
poker[total++] = "Little Joker";


for (int i = 0; i < total; i++) {
if (i % 13 == 0 && i != 0) {
System.out.println();
}
System.out.print(poker[i] + " ");

}

}
}

练习: 螺旋矩阵

思路分析

1. 螺旋矩阵的特点

螺旋矩阵是指数字从外向内顺时针螺旋排列方阵.

我们来拿4 * 4的方阵举例

1
2
3
4
5
6
7
1234

121314 5
↑ ↓ ↓
11 1615 6
↑ ↓
10987

2. 实现思路

我们采用”边界收缩法”来实现

  • 定义四个边界 top, bottom, left, right
  • 按照”上边 -> 右边 -> 下边 -> 左边”的顺序来填充
  • 每填充完一条边,对应的边界像内搜索
  • 重复此过程直到所有数字填完
  1. 我们先来定义一个nxn的二维数组来存放数据
1
2
3
n = 4
int num = 1;
int[][] arr = new int[n][n]
  1. 定义4条边界
1
int top = 0, bottom = n -1, left = 0, right = n -1

top = 0 代表上边界的第0行

bottom = 0 代表下边界的 最后一行

left = 0 代表左边界的第 0 列

right = n -1 代表 右边的最后一列

  1. 定义循环控制条件
1
while (num <= n * n) {}
  1. 从左向右填充
1
2
3
4
for(int i = left; i <= right;i++){
arr[left][i] = num++;
}
top++

此时我们就完成了从左到右的填充

1
2
3
4
5
6
7
8
9
10
11
12
13
[

​ [1, 2, 3, 4],

​ [0, 0, 0, 0], ← top++ 将移动到这行

​ [0, 0, 0, 0],

​ [0, 0, 0, 0]

]

top++
  1. 从上到下填充
1
2
3
4
for(int i = top; i <= bottom;i++){
arr[i][right] = num++;
}
right--

此时就完成了从上到下的填充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[

​ [1, 2, 3, 4],

​ [0, 0, 0, 5],

​ [0, 0, 0, 6],

​ [0, 0, 0, 7]

right-- 此时就指向了这一列
]


  1. 从右到左填充
1
2
3
4
for(int i = right; i >= left; i--){
arr[bottom][i] = num++;
}
bottom--;

此时就完成了从右到左的填充

1
2
3
4
5
6
7
8
9
10
11
12
[

​ [1, 2, 3, 4],

​ [0, 0, 0, 5],

​ bottom--之后指向的位置 → [0, 0, 0, 6],

​ [10, 9, 8, 7]

]

  1. 从下到上填充
1
2
3
4
for(int i = bottom; i >= top; i--){
arr[i][left] = num++;
}
left++;

此时就填充完了一圈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[

​ [1, 2, 3, 4],
↓ 此时的left ++

​ [12, 0, 0, 5],

​ [11, 0, 0, 6],

​ [10, 9, 8, 7]

]


在填充 n轮后 到达 边界检测条件后就完成了填充

1
while (num <= n * n) {}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Main {

public static void main(String[] args) {

int n = 4;
int[][] arr = new int[n][n];
int num = 1;
int top = 0, bottom = n - 1, left = 0, right = n - 1;

while (num <= n * n) {
for (int i = left; i <= right; i++) {
arr[top][i] = num++;
}
top++;

for (int i = top; i <= bottom; i++) {
arr[i][right] = num++;
}
right--;

for (int i = right; i >= left; i--) {
arr[bottom][i] = num++;
}
bottom--;

for (int i = bottom; i >= top; i--) {
arr[i][left] = num++;
}
left++;

}
}
}

这套实现方法有局限性只能 做固定起点、固定顺时针的矩阵填充,不能做动态方向/中心扩展/不规则矩阵/变化版螺旋布局

第二套实现方案

方向数组 + 步长控制

实现思路和步骤

初始化一个 3×3 的二维整型数组 matrix,用于存储结果,所有元素初始值为 0

定义方向数组 directions,表示顺时针的移动方向:分别为「向右 (0,1)」、「向下 (1,0)」、「向左 (0,-1)」和「向上 (-1,0)」的行列偏移量。

设置起点位置为 (row, col) = (0, 0)(矩阵左上角),并将当前方向索引 dir 初始化为 0(对应「向右」方向)

使用 for 循环依次生成数字 19:在每次循环中,先将当前数字填入 matrix[row][col]

在循环内计算下一步的位置:newRow = row + directions[dir][0]newCol = col + directions[dir][1]

如果 newRownewCol 超出边界(< 0 或 ≥ 3)或 matrix[newRow][newCol] 已经不为 0(意味着该位置已被填充),则顺时针切换方向:dir = (dir + 1) % 4,并基于新的方向重新计算 newRownewCol

更新当前坐标:将 row = newRowcol = newCol,继续下一次循环

循环结束后,使用双重 for 循环遍历并打印矩阵 matrix,即可得到完整的 3×3 螺旋矩阵结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class SpiralMatrix {
public static void main(String[] args) {
// 矩阵的大小(3×3)
int n = 3;
// 初始化一个 3×3 的矩阵,默认元素值为 0
int[][] matrix = new int[n][n];
// 定义方向数组:按顺时针方向依次为「右、下、左、上」
// 对应的行列偏移量分别是 (0,1)、(1,0)、(0,-1)、(-1,0)
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 当前方向索引,0 表示向右
int dir = 0;
// 初始化当前坐标为左上角 (0,0)
int row = 0, col = 0;

// 使用 for 循环依次填充 1 到 n*n 的数字
for (int num = 1; num <= n * n; num++) {
// 将当前数字填入矩阵的当前坐标
matrix[row][col] = num;
// 计算按当前方向移动一步后的新坐标
int newRow = row + directions[dir][0];
int newCol = col + directions[dir][1];
// 如果新坐标越界或已经填充,则需要顺时针改变方向
if (newRow < 0 || newRow >= n || newCol < 0 || newCol >= n
|| matrix[newRow][newCol] != 0) {
// 顺时针切换方向
dir = (dir + 1) % 4;
// 重新计算改变方向后的新坐标
newRow = row + directions[dir][0];
newCol = col + directions[dir][1];
}
// 更新当前坐标为新的位置
row = newRow;
col = newCol;
}

// 输出填充好的 3×3 螺旋矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 每个数字后跟一个制表符对齐输出
System.out.print(matrix[i][j] + "\t");
}
System.out.println();
}
}
}

练习 数组反转

双指针交换法

  • 循环条件:i < arr.length / 2 确保只遍历前半部分
  • 索引计算:arr.length - i - 1 动态计算对称位置
  • 交换逻辑:通过temp变量实现三数交换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9,10};

for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}

System.out.println(Arrays.toString(arr));
}
}

练习 数组的扩容与缩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.util.Arrays;

public class MyArrayList<E> {
private Object[] data;
private int size;
private static final int INIT_CAPACITY = 10;

public MyArrayList() {
data = new Object[INIT_CAPACITY];
size = 0;
}

public void add(E element) {
if (size == data.length) {
resize(data.length * 2); // 扩容到2倍
}
data[size++] = element;
}

public E remove(int index) {
checkIndex(index);
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(data, index + 1, data, index, numMoved);
}
data[--size] = null; // 让GC回收

if (size > 0 && size == data.length / 4) {
resize(data.length / 2); // 缩容到一半
}
return oldValue;
}

private void resize(int newCapacity) {
data = Arrays.copyOf(data, newCapacity);
}

@SuppressWarnings("unchecked")
private E elementData(int index) {
return (E) data[index];
}

private void checkIndex(int index) {
if (index < 0 || index >= size) throw new IndexOutOfBoundsException();
}

public int size() {
return size;
}
}

练习 获取一个字符串在另一个字符串中出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 案例2:获取一个字符串在另一个字符串中出现的次数,
* 如获取ab在abababkkcadkabkebfkabkskab中出现的次数是6。
*/

String str = "abababkkcadkabkebfkabkskab";
String subStr = "ab";
int index = 0;
while(true){
index = str.indexOf(subStr);
if(index != -1){
str = str.substring(index + 1);
count++
}else break;
}

找出字符串中的最大公共前缀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public class PrefixFinder {
public static String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";

String prefix = strs[0];
for (int i = 1; i < strs.length; i++) {
while (!strs[i].startsWith(prefix)) {
prefix = prefix.substring(0, prefix.length() - 1);
if (prefix.isEmpty()) return "";
}
}
return prefix;
}

public static void main(String[] args) {
String[] inputs = {"com.example.user", "com.example.admin", "com.example.config"};
System.out.println("公共前缀: " + longestCommonPrefix(inputs)); // 输出: com.example.
}
}