3.1 数组的概述
- 数组(Array),是多个相同数据类型按一定顺序排列的集合,使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
- 数组的常见概念
- 数组名
- 下标(或索引)
- 元素
- 数组的长度
- 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
- 创建数组对象会在内存中开辟一整块连续的空间。
- 数组的长度一旦确定,就不能修改。
- 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- 数组的分类
- 按照维度:一维数组、二维数组……
- 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
3.2 一维数组
3.2.1 一维数组的声明和初始化
- 数组一旦初始化完成,其长度就确定了。
1、静态初始化
- 数组的初始化和数组元素的赋值操作同时进行。
int[] arr1 = new int[]{1, 2, 3, 4}; int[] arr2 = {1, 2, 3, 4}; /* 报错 int[] arr3; arr3 = {1, 2, 3, 4}; */
2、动态初始化
- 数组的初始化和数组元素的赋值操作分开进行。
int[] arr3 = new int[4]; arr3[0] = 1; arr3[1] = 2; arr3[2] = 3; arr3[3] = 4;
3.2.2 如何调用数组的指定位置的元素
通过索引的方式调用。
数组的索引从0开始,到数组的长度-1结束。
String[] names = {"张三", "李四", "王五", "赵六"}; String name1 = names[0]; String name2 = names[1]; String name3 = names[2]; String name4 = names[3];
3.2.3 如何修改数组的指定位置的元素
通过索引的方式调用。
数组的索引从0开始,到数组的长度-1结束。
String[] names = {"张三", "李四", "王五", "赵六"}; names[0] = "一一"; names[1] = "二二"; names[2] = "三三"; names[3] = "四四";
3.2.4 如何获取数组的长度
- 属性:length。
String[] names = {"张三", "李四", "王五", "赵六"}; int length = names.length; // 4
3.2.5 如何遍历数组
- 循环。
String[] names = {"张三", "李四", "王五", "赵六"}; // for循环 for(int i = 0; i < names.length; i++) { System.out.println(names[i]); } // 增强for循环 for(String name : names) { System.out.println(name); }
3.2.6 数组元素的默认初始化值
- 基本数据类型
- 整型(byte、short、int、long):0
- 浮点型(float、double):0.0
- 字符型(char):‘\u0000’
- 布尔型(boolean):false
- 引用数据类型:null
3.2.6 数组的内存解析
3.2.7 例题
从键盘读入学生成绩,找出最高分,并输出学生成绩等级。
import java.util.Scanner; public class Test { public static void main(String[] args) { // 1、使用Scanner,读取学生个数 Scanner scan = new Scanner(System.in); System.out.println("请输入学生人数:"); int num = scan.nextInt(); // 2、创建数组,存储学生成绩(动态初始化) int[] scores = new int[num]; // 3、给数组中的元素赋值 System.out.println("请输入" + num + "个学生成绩:"); for(int i = 0; i < scores.length; i++) { scores[i] = scan.nextInt(); } scan.close(); // 4、获取数组中的元素的最大值(最高分) int maxScore = scores[0]; for(int i = 1; i < scores.length; i++) { if(scores[i] > maxScore) { maxScore = scores[i]; } } // 5、根据每个学生成绩与最高分的差值,得到每个学生的等级,并输出等级和成绩 for(int i = 0; i < scores.length; i++) { switch((maxScore-scores[i])/10) { case 0: System.out.println("等级:A,成绩:" + scores[i]); break; case 1: System.out.println("等级:B,成绩:" + scores[i]); break; case 2: System.out.println("等级:C,成绩:" + scores[i]); break; default: System.out.println("等级:D,成绩:" + scores[i]); } } } }
3.3 数组
- Java语言里提供了支持***数组的语法。
- 如果说可以把一维数组当成几何中的线性图形,那么二维数组就相当于是一个表格。
- 对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。
- 其实,从数组底层的运行机制来看,并没有***数组。
3.3.1 二维数组的声明和初始化
1、静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5,6},{7,8,9}}; int[][] arr2 = {{1,2,3},{4,5,6},{7,8,9}};
2、动态初始化
String[][] arr1 = new String[3][2]; for(int i = 0; i < arr1.length; i++) { for(int j = 0; j < arr1[0].length; j++) { arr1[i][j] = "name"; } } String[][] arr2 = new String[3][]; for(int i = 0; i < arr2.length; i++) { arr2[i] = new String[2]; }
3.3.2 如何调用二维数组的指定位置的元素
String[][] names = new String[]{{"张一","张二","张三"},{"李一","李二","李三"}; names[0][0]; // "张一" names[1][1]; // "李二"
3.3.3 获取数组的长度
String[][] arr = new String[3][2]; int len1 = arr.length; // 3 int len2 = arr[0].length; // 2
3.3.4 如何遍历二维数组
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}}; for(int i = 0; i < arr.length; i++) { for(int j = 0; j < arr[i].length; j++) { System.out.print(arr[i][j] + " "); } System.out.println(); } /* 1 2 3 4 5 6 7 8 9 */
3.3.5 二维数组元素的默认初始化值
int[][] arr1 = new int[4][3]; arr1[0]; // 地址 arr1[0][0]; // 0 int[][] arr2 = new int[4][]; arr2[0]; // null arr2[0][0]; // 报错:java.lang.NullPointerException
3.3.6 二维数组内存解析
3.3.7 练习
- 练习1
声明:
int[] x, y[];
在给变量赋值以后,以下选项允许通过编译的是:
x[0] = y; // × y[0] = x; // √ y[0][0] = x; // × x[0][0] = y; // × y[0][0] = x[0]; // √ x = y; // ×
- 练习2
使用二维数组打印一个10行杨辉三角。
提示:
第一行有1个元素,第n行有n个元素
每一行的第一个元素和最后一个元素都是1
从第三行开始,对于非第一个元素和最后一个元素的元素:yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j]
import java.util.Arrays; public class Test { public static void main(String[] args) { // 1、声明并初始化二维数组 int[][] yanghui = new int[10][]; // 2、给数组元素赋值 for(int i = 0; i < yanghui.length; i++) { yanghui[i] = new int[i+1]; yanghui[i][0] = 1; yanghui[i][i] = 1; if(i > 1) { for(int j = 1; j < yanghui[i].length - 1; j++) { yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j]; } } } // 3、遍历二维数组 for(int[] arr : yanghui) { System.out.println(Arrays.toString(arr)); } } }
- 练习3
创建一个长度为6的int型数组,要求数组元素的值都在1~30之间,且是随机赋值。同时,要求元素的值各不相同。
import java.util.Arrays; public class Test { public static void main(String[] args) { int[] arr = new int[6]; int num; boolean flag; for (int i = 0; i < arr.length; i++) { flag = true; while (true) { num = (int) (Math.random() * 30 + 1); for (int j = 0; j < i; j++) { if(num == arr[j]) { flag = false; } } if(flag) { break; } } arr[i] = num; } System.out.println(Arrays.toString(arr)); } }
3.4 数组中涉及到的常见算法
- 数组元素的赋值(杨辉三角、回形数等)
- 求数值型数组中元素的最大值、最小值、平均数、总和等
- 数组的赋值、反转、查找(线性查找、二分查找)
- 数组元素的排序算法
- 练习1
定义一个int型的一维数组,包含10个元素,分别赋予一些随机整数,然后求出所有元素的最大值、最小值、和值、平均值,并输出出来。
要求:所有随机数都是两位数。
import java.util.Arrays; public class Test { public static void main(String[] args) { int[] arr = new int[10]; int num; for(int i = 0; i < arr.length; i++) { num = (int)(Math.random()*90+10); arr[i] = num; } int max = arr[0]; int min = arr[0]; int sum = 0; int avg; for(int i = 0; i < arr.length; i++) { if(arr[i] > max) { max = arr[i]; } if(arr[i] < min) { min = arr[i]; } sum += arr[i]; } avg = sum/arr.length; System.out.println("数组:" + Arrays.toString(arr)); System.out.println("最大值:" + max); System.out.println("最小值:" + min); System.out.println("和值:" + sum); System.out.println("平均值:" + avg); } }
- 练习2
使用简单数组
- 创建两个int型一维数组:arr1、arr2
- 使用大括号{},把arr1初始化为8个素数:2,3,5,7,11,13,17,19
- 显示arr1的内容
- 赋值arr2变量等于arr1,修改arr2中的偶索引元素,使其等于索引值(如arr[0],arr[2]=2),打印出arr1
思考:arr1和arr2是什么关系?——arr1和arr2指向同一个数组地址。
import java.util.Arrays; public class Test { public static void main(String[] args) { int[] arr1, arr2; arr1 = new int[]{2,3,5,7,11,13,17,19}; for(int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + "\t"); } System.out.println(); arr2 = arr1; for(int i = 0; i < arr2.length; i++) { if(i %2 == 0) { arr2[i] = i; } } for(int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + "\t"); } System.out.println(); } }
- 练习2
实现arr2对arr1数组的复制。
public class Test { public static void main(String[] args) { int[] arr1, arr2; arr1 = new int[]{2,3,5,7,11,13,17,19}; for(int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + "\t"); } System.out.println(); arr2 = new int[arr1.length]; for (int i = 0; i < arr2.length; i++) { arr2[i] = arr1[i]; } for(int i = 0; i < arr2.length; i++) { if(i %2 == 0) { arr2[i] = i; } } for(int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + "\t"); } System.out.println(); } }
- 练习3
数组的复制、反转、查找(线性查找、二分查找)。
import java.util.Arrays; public class Test { public static void main(String[] args) { String[] arr = new String[]{"AA","BB","CC","DD","EE"}; // 数组的复制 String[] arr1 = new String[arr.length]; for(int i = 0; i < arr1.length; i++) { arr1[i] = arr[i]; } System.out.println(Arrays.toString(arr1)); // 数组的反转 String[] arr2 = new String[arr.length]; for(int i = 0; i < arr2.length; i++) { arr2[i] = arr[arr.length-1-i]; } System.out.println(Arrays.toString(arr2)); String temp; for(int i = 0; i < arr.length/2; i++) { temp = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = temp; } System.out.println(Arrays.toString(arr)); // 线性查找 String target = "BB"; boolean isFlag = true; for(int i = 0; i < arr.length; i++) { if(target.equals(arr[i])) { System.out.println("找到了指定的元素,位置为:" + i); isFlag = false; break; } } if(isFlag) { System.out.println("很遗憾,没有找到!"); } // 二分查找 int[] nums = new int[]{1,2,3,4,5,6,7,8,9}; int target2 = 3; isFlag = true; int left = 0; int right = arr.length - 1; int mid = (left+right)/2; while(left <= right) { if(target2 == nums[mid]) { System.out.println("找到了指定的元素,位置为:" + mid); isFlag = false; break; } else if(target2 < nums[mid]) { right = mid - 1; } else { left = mid + 1; } } if(isFlag) { System.out.println("很遗憾,没有找到!"); } } }
3.4.1 排序算法
1、排序
假设含有n个记录的序列为{R1, R2, ……, Rn},其相应的关键字序列为{K1, K2, ……, Kn}。将这些记录重新排列为{Ri1, Ri2, ……, Rin},使得相应的关键字值满足条件:Ki1 ≤ Ki2 ≤ …… ≤ Kin,这样的操作称为排序。
- 通常来说,排序的目的是快速查找。
2、衡量排序算法的优劣
- 时间复杂度:分析关键字的比较次数和记录的移动次数。
- 空间复杂度:分析排序算法中需要多少辅助内存。
- 稳定性:若两个记录A和B的关键值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
3、排序算法分类
- 内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
- 外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
4、十大内部排序算法
- 选择排序
- 直接选择排序
- 堆排序
- 交换排序
- 冒泡排序
- 快速排序
- 插入排序
- 直接插入排序、折半插入排序、Shell排序
- 归并排序
- 桶排序
- 基数排序
5、算法的5大特征
- 输入:有0个或多个输入数据,这些输入必须有清楚的描述和定义。
- 输出:至少有1个或多个输出结果,不可以没有输出结果。
- 有穷性:算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成。
- 确定性:算法中的每一步都是有确定的含义,不会出现二义性。
- 可行性:算法的每一步都是清楚且可行的,能让用户用纸笔计算而求出答案。
说明:满足确定性的算法也称为“确定性算法”。现在人们也关注更加广泛的概念。例如:考虑各种非确定性的算法,如并行算法、概率算法等。另外,人们也关注并不要求终止的计算描述,这种描述有时被称为过程(Procedure)。
3.4.2 冒泡排序
import java.util.Arrays; public class Test { public static void bubbleSort(int[] arr) { int length = arr.length; int temp; for(int i = 1; i < length; i++) { for(int j = 0; j < length - i; j++) { if(arr[j] > arr[j+1]) { temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } public static void main(String[] args) { int[] arr = new int[]{49,38,65,97,76,13,27,49}; bubbleSort(arr); System.out.println(Arrays.toString(arr)); } }
3.4.3 快速排序
import java.util.Arrays; public class Test { public static void quickSort(int[] arr) { quickSort(arr, 0, arr.length-1); } public static void quickSort(int[] arr, int left, int right) { int l = left; int r = right; int pivot = arr[(l+r)/2]; int temp; while(l < r) { while(l < r && arr[l] <= pivot) { l++; } while(l < r && arr[r] >= pivot) { r--; } if(l < r) { temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; } } if(l > left) { quickSort(arr, left, r-1); } if(l < right) { quickSort(arr, l, right); } } public static void main(String[] args) { int[] arr = new int[]{49,38,65,97,76,13,27,49}; quickSort(arr); System.out.println(Arrays.toString(arr)); } }
3.4.4 排序算法性能比较
3.5 Arrays工具类
import java.util.Arrays; public class Test { public static void main(String[] args) { // 1、Arrays.equals(int[] a, int[] b):判断两个数组是否相等 int[] arr1 = new int[]{1,3,2,4}; int[] arr2 = new int[]{1,3,2,4}; boolean isEqual = Arrays.equals(arr1, arr2); System.out.println(isEqual); // 2、Arrays.toString(int[] a):输出数组信息 System.out.println(Arrays.toString(arr1)); // 3、Arrays.fill(int[] a, int val):将指定值填充到数组之中 Arrays.fill(arr1, 10); System.out.println(Arrays.toString(arr1)); // 4、Arrays.sort(int[] a):对数组进行排序 Arrays.sort(arr2); System.out.println(Arrays.toString(arr2)); // 5、Arrays.binarySearch(int[] a, int key):二分查找 int[] arr3 = new int[]{-78,46,7,9,2,95,79}; int index = Arrays.binarySearch(arr3, 9); System.out.println(index); } }