Java数组(二)
1、多维数组
- 多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
- 二维数组
int a[][] = new int[2][5];
解析:以上二维数组a可以看成一个两行五列的数组。
画图理解
可以理解为一维数组是一个线,每个元素是线上的一个点,二维数组是一个面,此时,每个元素依旧是面上的一个点,相对于线,你就需要有两个坐标去确定它
思考:多维数组的使用?
package com.xxsml.array;
public class ArrayDemo05 {
public static void main(String[] args) {
//[4][2]
/*
1,2 array[0]
2,3 array[1]
3,4 array[2]
4,5 array[3]
*/
int[][] array = {{1,2},{2,3},{3,4},{4,5}};
System.out.println("=====输出外层数组长度=====");
System.out.println(array.length);
System.out.println("=====输出array[0]数组长度=====");
System.out.println(array[0].length);
System.out.println("=====直接输出array[0]=====");
System.out.println(array[0]); //[I@1540e19d 输出地址的原因因为array[0]代表的是{1,2}
System.out.println("=====输出array[0][0]的值=====");
System.out.println(array[0][0]);
System.out.println("=====输出array[0][1]的值=====");
System.out.println(array[0][1]);
System.out.println("=====循环输出外层内层数组的长度=====");
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.println(array[i][j]);
}
}
}
}
输出结果
=====输出外层数组长度=====
4
=====输出array[0]数组长度=====
2
=====直接输出array[0]=====
[I@1540e19d
=====输出array[0][0]的值=====
1
=====输出array[0][1]的值=====
2
=====循环输出外层内层数组的长度=====
1
2
2
3
3
4
4
5
2、Arrays类
- 数组的工具类java.util.Arrays
- 由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
- 查看JDK帮助文档
- Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而"不用"使用对象来调用(注意:是"不用”而不是“不能")
- 具有以下常用功能:
- 给数组赋值:通过fill方法。
- 对数组排序:通过sot方法,按升序。
- 比较数组:通过equals方法比较数组中元素值是否相等。
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作。
例1:
package com.xxsml.array;
import java.util.Arrays;
public class ArrayDemo06 {
public static void main(String[] args) {
int[] a = {1,2,54,34,6,4,3,43,3,5,867};
//打印数组元素Arrays.toString
System.out.println(Arrays.toString(a));
//数组进行排序:升序
Arrays.sort(a);
System.out.println(Arrays.toString(a));
//2~4用填充
Arrays.fill(a,2,4,0);
System.out.println(Arrays.toString(a));
//数组填充
Arrays.fill(a,0);
System.out.println(Arrays.toString(a));
}
}
输出结果
[1, 2, 54, 34, 6, 4, 3, 43, 3, 5, 867]
[1, 2, 3, 3, 4, 5, 6, 34, 43, 54, 867]
[1, 2, 0, 0, 4, 5, 6, 34, 43, 54, 867]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
toString是说以字符串的形式表示出来
总结和拓展
-
转换为字符串(Converting to String):
Arrays.toString()
方法用于将数组转换为字符串,便于打印输出。java复制代码int[] arr = {1, 2, 3}; System.out.println(Arrays.toString(arr)); // 输出:[1, 2, 3]
-
排序(Sorting):
Arrays.sort()
方法用于对数组进行排序。默认情况下,它会按升序排序数组元素。java复制代码int[] arr = {3, 1, 4, 2}; Arrays.sort(arr); System.out.println(Arrays.toString(arr)); // 输出:[1, 2, 3, 4]
-
填充(Filling):
Arrays.fill()
方法用指定的值填充数组的所有元素。java复制代码int[] arr = new int[5]; Arrays.fill(arr, 10); System.out.println(Arrays.toString(arr)); // 输出:[10, 10, 10, 10, 10]
-
复制(Copying):
Arrays.copyOf()
方法用于复制数组。可以指定要复制的长度。java复制代码int[] source = {1, 2, 3, 4, 5}; int[] target = Arrays.copyOf(source, 3); System.out.println(Arrays.toString(target)); // 输出:[1, 2, 3]
-
比较(Comparing):
Arrays.equals()
方法用于比较两个数组是否相等。java复制代码int[] arr1 = {1, 2, 3}; int[] arr2 = {1, 2, 3}; System.out.println(Arrays.equals(arr1, arr2)); // 输出:true
-
搜索(Searching):
Arrays.binarySearch()
方法用于在排序数组中搜索指定的元素。如果找到了该元素,则返回其索引;否则返回负数。java复制代码int[] arr = {1, 2, 3, 4}; int index = Arrays.binarySearch(arr, 3); System.out.println("Index of 3: " + index); // 输出:2
3、冒泡排序
冒泡排序无疑是最为出名的排序算法之一,总共有八大排序!
-
冒泡的代码还是相当简单的,两层循济,外层冒泡轮数,里层依次比较,江湖中人人尽皆知。
-
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(2)。
-
冒泡排序
- 比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置;
- 每一次比较,都会产生出一个最大,或者最小的数字;
- 下一轮则可以少一次排序!
- 依次循环,直到结束!
package com.xxsml.array;
import java.util.Arrays;
public class ArrayDemo07 {
public static void main(String[] args) {
int[] a = {1,3,43,5,63,2,54,28,6,12,43,22};
//调用完我们自己写的排序方法以后,返回一个排序后的数组
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] array){
//临时变量
int temp = 0;
//外层循环,判断我们这个要走多少次
for (int i = 0;i < array.length - 1;i++){
//内层循环:比较这两个数,如果后一个大于前面一个,则交换位置
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j+1]>array[j]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
return array;
}
}
输出结果
[63, 54, 43, 43, 28, 22, 12, 6, 5, 3, 2, 1]
思考:如何优化?
package com.xxsml.array;
import java.util.Arrays;
public class ArrayDemo07 {
public static void main(String[] args) {
int[] a = {1,3,43,5,63,2,54,28,6,12,43,22};
//调用完我们自己写的排序方法以后,返回一个排序后的数组
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] array){
//临时变量
int temp = 0;
//外层循环,判断我们这个要走多少次
for (int i = 0;i < array.length - 1;i++){
boolean flag = false; //通过flag标识位减少没有意义的比较
//内层循环:比较这两个数,如果后一个大于前面一个,则交换位置
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j+1]>array[j]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true;
}
}
if(flag == false){
break;
}
}
return array;
}
}
修改位置:第20行:boolean flag = false; //通过flag标识位减少没有意义的比较
第30~32行:
if(flag == false){
break;
}
第二段代码相对于第一段代码在性能上进行了优化,具体优化点在于使用了一个boolean
类型的flag
变量来减少不必要的比较。这是经典的冒泡排序算法的一个改进。
优化点如下:
- 减少不必要的比较:在冒泡排序中,如果某一趟遍历过程中没有进行任何交换(即数组已经有序或局部有序),那么之后的遍历将不会改变数组的顺序。因此,在第二段代码中,如果在某一趟遍历中没有发生交换(即
flag
仍为false
),则直接跳出内层循环,结束这一趟的遍历。 - 时间复杂度的潜在降低:虽然冒泡排序的最坏时间复杂度仍然是O(n^2),但使用
flag
变量后,当数组已经有序或大部分有序时,算法会提前结束,从而减少了一些不必要的比较和交换操作,提高了效率。
当数组已经有序时,第一段代码仍然会进行完整的n-1趟遍历,而第二段代码在第一趟遍历后就会检测到没有发生交换,从而直接结束排序。
第二段代码通过引入flag
变量来减少不必要的比较,提高了冒泡排序算法在数组已经有序或大部分有序时的性能。这是一个有效的优化,虽然不改变算法的最坏时间复杂度,但在实际应用中可能会带来显著的性能提升。
4、稀疏数组
需求:编写五子棋游戏中,有存盘退出和续上盘的功能。
分析问题:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据。
解决:稀疏数组
稀疏数组介绍
- 当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
- 稀硫数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
- 如下图:左边是原始数组,右边是稀疏数组
数组的应用
package com.xxsml.array;
public class ArrayDemo08 {
public static void main(String[] args) {
//1、创建一个二维数组 11*11 0:没有棋子 1:黑棋 2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
//输出原始数组
System.out.println("输出原始数组");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
System.out.println("=============");
//转换为稀疏数组保存
//获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (array1[i][j] != 0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
//2、创建一个稀疏数组的数组
int[][] array2 = new int[sum+1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum; //把稀疏数组的头(第一行)打出来了
//遍历二维数组,将非零的值,存放稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0){
count++;
//把第2至最后一行也打出来了
array2[count][0] = i; //第count行的第1个数存它的横坐标
array2[count][1] = j; //第count行的第2个数存它的纵坐标
array2[count][2] = array1[i][j]; //第count行的第3个数存它的值
}
}
}
//输出稀疏数组
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0]+" "
+array2[i][1]+" "
+array2[i][2]+" ");
}
System.out.println("=============");
System.out.println("还原为原始的数组");
//1、读取叙述数组的值
int[][] array3 = new int[array2[0][0]][array2[0][1]]; //行为array2[0][0],列为array2[0][1]
//2、给其中的元素还原值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]] = array2[i][2]; //array2[i][0]为这个数的横坐标,array2[i][1]为这个数的纵坐标,array2[i][2]为这个数的值
}
//3、打印还原数组
System.out.println("输出还原的数组");
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
}
}
输出结果
输出原始数组
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
=============
有效值的个数:2
11 11 2
1 2 1
2 3 2
=============
还原为原始的数组
输出还原的数组
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0