1. 数组整体的概述
- 数组的理解:多个相同数据类型的变量按照一定顺序组织起来的集合
- 一维数组的使用
① 数组的声明与初始化
② 数组元素的调用
③ 数组的长度
④ 数组的遍历
⑤ 数组元素默认初始化值
⑥ 数组的内存解析
- 二维数组的使用
① 数组的声明与初始化
② 数组元素的调用
③ 数组的长度
④ 数组的遍历
⑤ 数组元素默认初始化值
⑥ 数组的内存解析
- 数组中常见算法的使用
- Arrays工具类的使用
- 数组中的常见异常
> ArrayIndexOutOfBoundsException
> NullPointerException
2. 数组中常见算法的使用
2.1 数组的定义及元素的赋值
- 练习
package com.atguigu.exer;
/**
* 使用二维数组打印一个 10 行杨辉三角。
* * 【提示】
* 1. 第一行有 1 个元素, 第 n 行有 n 个元素
* 2. 每一行的第一个元素和最后一个元素都是 1
* 3. 从第三行开始, 对于非第一个元素和最后一个元素的元素。即:
* yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
* * * 说明:笔试中数组是作为算法考察的一大门类(另一大门类就是考察String);
* 数组中的考察,其中一部分是创建特殊要求的数组。比如:杨辉三角,回形数等。
* * 拓展1:
* 创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。
* 同时,要求元素的值各不相同。
* 拓展2:
* 回形数
*/
public class YangHuiAngleTest {
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];
//3. 给数组元素赋值
//3.1 给每行的首末元素赋值为1
yangHui[i][0] = yangHui[i][i] = 1;
//3.2 给每行的非首末元素赋值:yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
//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];
}
//}
}
//4. 二维数组的遍历
for(int i = 0;i < yangHui.length;i++){
for(int j = 0;j < yangHui[i].length;j++){
System.out.print(yangHui[i][j] + "\t");
}
System.out.println();
}
}
}
- 练习:创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。
- 回形数
从键盘输入一个整数(1~20)
则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。例如: 输入数字2,则程序输出: 1 2 4 3
输入数字3,则程序输出: 1 2 3 8 9 4 7 6 5
输入数字4, 则程序输出:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
- 代码实现
package Test1;
import java.util.Scanner;
/*
* 从键盘输入一个整数(1~20)
则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。例如: 输入数字2,则程序输出: 1 2 4 3
输入数字3,则程序输出: 1 2 3 8 9 4 7 6 5
输入数字4, 则程序输出:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
*/
public class RectangleTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入一个数字");
int len = scanner.nextInt();
int[][] arr = new int[len][len];
int s = len * len;
/*
* k = 1:向右
* k = 2:向下
* k = 3:向左
* k = 4:向上
*/
int k = 1;
int i = 0,j = 0;
for(int m = 1;m <= s;m++){
if(k == 1){
if(j < len && arr[i][j] == 0){
arr[i][j++] = m;
}else{
k = 2;
i++;
j--;
m--;
}
}else if(k == 2){
if(i < len && arr[i][j] == 0){
arr[i++][j] = m;
}else{
k = 3;
i--;
j--;
m--;
}
}else if(k == 3){
if(j >= 0 && arr[i][j] == 0){
arr[i][j--] = m;
}else{
k = 4;
i--;
j++;
m--;
}
}else if(k == 4){
if(i >= 0 && arr[i][j] == 0){
arr[i--][j] = m;
}else{
k = 1;
i++;
j++;
m--;
}
}
}
//遍历
for(int m = 0;m < arr.length;m++){
for(int n = 0;n < arr[m].length;n++){
System.out.print(arr[m][n] + "\t");
}
System.out.println();
}
}
}
/*
01 02 03 04 05 06 07
24 25 26 27 28 29 08
23 40 41 42 43 30 09
22 39 48 49 44 31 10
21 38 47 46 45 32 11
20 37 36 35 34 33 12
19 18 17 16 15 14 13
*/
public class RectangleTest1 {
public static void main(String[] args) {
int n = 7;
int[][] arr = new int[n][n];
int count = 0; //要显示的数据
int maxX = n-1; //x轴的最大下标
int maxY = n-1; //Y轴的最大下标
int minX = 0; //x轴的最小下标
int minY = 0; //Y轴的最小下标
while(minX<=maxX) {
for(int x=minX;x<=maxX;x++) {
arr[minY][x] = ++count;
}
minY++;
for(int y=minY;y<=maxY;y++) {
arr[y][maxX] = ++count;
}
maxX--;
for(int x=maxX;x>=minX;x--) {
arr[maxY][x] = ++count;
}
maxY--;
for(int y=maxY;y>=minY;y--) {
arr[y][minX] = ++count;
}
minX++;
}
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr.length;j++) {
String space = (arr[i][j]+"").length()==1 ? "0":"";
System.out.print(space+arr[i][j]+" ");
}
System.out.println();
}
}
}
2.2 数值类型数组中特殊值的计算
/**
* 数组的常见算法一:求数值型数组中元素的最大值、最小值、平均数、总和等
*/
public class ArrayTest {
public static void main(String[] args) {
int[] arr = new int[]{3,42,2,53,65,24,65,87,-95,-84,0,45};
//获取最大值
int max = arr[0];
for(int i = 1;i < arr.length;i++){
if(max < arr[i]){
max = arr[i];
}
}
System.out.println("最大值为:" + max);
//获取最小值
int min = arr[0];
for(int i = 1;i < arr.length;i++){
if(min > arr[i]){
min = arr[i];
}
}
System.out.println("最小值为:" + min);
//获取总和
int sum = 0;
for(int i = 0;i <arr.length;i++){
sum += arr[i];
}
System.out.println("总和为:" + sum);
//求平均数
double avg = (double)sum / arr.length;
System.out.println("平均数为:" + avg);
}
}
2.3 数组的反转、复制、遍历、查找等
/**
* 数组的常见算法二:复制、反转、查找、遍历
*/
public class ArrayTest1 {
public static void main(String[] args) {
int[] arr = new int[]{3,42,2,53,65,24,65,87,-95,-84,0,45};
//复制
int[] arrCopy = new int[arr.length];
for (int i = 0; i < arrCopy.length; i++) {
arrCopy[i] = arr[i];
}
//遍历
for (int i = 0; i < arr.length; i++) {
if(i == 0){
System.out.print("{");
}else if(i == arr.length - 1){
System.out.print(arr[i] + "}");
break;
}
System.out.print(arr[i] +",");
}
//反转方式一:
// for(int i = 0,j = arr.length - 1; i < j ;i++,j--){
// int temp = arr[i];
// arr[i] = arr[j];
// arr[j] = temp;
// }
//反转方式二:
for(int i = 0;i < arr.length / 2;i++){
int temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
System.out.println();
//遍历arr1
for (int i = 0; i < arr.length; i++) {
if(i == 0){
System.out.print("{");
}else if(i == arr.length - 1){
System.out.print(arr[i] + "}");
break;
}
System.out.print(arr[i] +",");
}
//查找(或搜索):如果查找到了,打印其在数组中的索引;如果没找到,打印没有找到的提示
//线性查找
int target = 244;
boolean isFlag = true;
for(int i = 0;i < arr.length;i++){
if(target == arr[i]){
System.out.println("查找到了" + target +"元素,索引位置为:" + i);
isFlag = false;
break;
}
}
if(isFlag){
System.out.println("不好意思,没找到");
}
//二分法查找:了解
//二分法查找:要求此数组必须是有序的。
int[] arr3 = new int[]{-99,-54,-2,0,2,33,43,256,999};
boolean isFlag1 = true;
int number = 256;
number = 25;
int head = 0;//首索引位置
int end = arr3.length - 1;//尾索引位置
while(head <= end){
int middle = (head + end) / 2;
if(arr3[middle] == number){
System.out.println("找到指定的元素,索引为:" + middle);
isFlag1 = false;
break;
}else if(arr3[middle] > number){
end = middle - 1;
}else{//arr3[middle] < number
head = middle + 1;
}
}
if(isFlag1){
System.out.println("未找打指定的元素");
}
}
}
- 练习
使用简单数组
* (1)创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
* (2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。
* (3)显示array1的内容。
* (4)赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)。
* 打印出array1。 array2 = array1;
public class ArrayTest {
public static void main(String[] args) {
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//遍历array1
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
//赋值array2变量等于array1
array2 = array1;
//修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)
for(int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
}
System.out.println("**************");
//遍历array1
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
}
}
对应的内存结构:
- 拓展
* 思考:array1和array2是什么关系? 两个数组变量指向了堆空间中同一个数组结构
*
* 拓展:修改题目,实现array2对array1数组的复制
public class ArrayTest1 {
public static void main(String[] args) {
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//遍历array1
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
//实现array2数组对array1数组的复制
array2 = new int[array1.length];
for(int i = 0;i < array2.length;i++){
array2[i] = array1[i];
}
//修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)
for(int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
}
System.out.println("**************");
//遍历array1
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
}
}
对应的内存结构:
2.4 数组的排序
衡量排序算法的优劣:
1.时间复杂度:分析关键字的比较次数和记录的移动次数
2.空间复杂度:分析排序算法中需要多少辅助内存
3.稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
排序算法分类:内部排序和外部排序。
内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
- 十大经典排序算法
- 冒泡排序的实现
public class BubbleSortTest {
public static void main(String[] args) {
int[] arr = new int[]{3, 42, 2, 53, 65, 24, 65, 87, -95, -84, 0, 45};
//排序前的遍历
for (int i = 0; i < arr.length; i++) {
if (i == 0) {
System.out.print("{");
} else if (i == arr.length - 1) {
System.out.print(arr[i] + "}");
break;
}
System.out.print(arr[i] + ",");
}
//排序操作:冒泡排序
for(int i = 0;i < arr.length - 1;i++){
for(int j = 0;j < arr.length -1 - i;j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println();
//排序后的遍历
for (int i = 0; i < arr.length; i++) {
if (i == 0) {
System.out.print("{");
} else if (i == arr.length - 1) {
System.out.print(arr[i] + "}");
break;
}
System.out.print(arr[i] + ",");
}
}
}
快速排序的实现原理
- 第一轮
- 后续n轮:
- 快速排序代码实现:
public class QuickSort {
private static void swap(DataWrap[] data, int i, int j) {
DataWrap temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private static void subSort(DataWrap[] data, int start, int end) {
if (start < end) {
DataWrap base = data[start];
int i = start;
int j = end + 1;
while (true) {
while (i < end && data[++i].compareTo(base) <= 0)
;
while (j > start && data[--j].compareTo(base) >= 0)
;
if (i < j) {
swap(data, i, j);
} else {
break;
}
}
swap(data, start, j);
subSort(data, start, j - 1);
subSort(data, j + 1, end);
}
}
public static void quickSort(DataWrap[] data){
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
DataWrap[] data = { new DataWrap(9, ""), new DataWrap(-16, ""),
new DataWrap(21, "*"), new DataWrap(23, ""),
new DataWrap(-30, ""), new DataWrap(-49, ""),
new DataWrap(21, ""), new DataWrap(30, "*"),
new DataWrap(30, "") };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
quickSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
- 各种排序算法的时间复杂度、空间复杂度、稳定性的情况
2.5 其他的算法
待补充,
import java.util.Arrays;
/**
*
* 测试Arrays的使用
* * Arrays:操作数组的工具类,在java.util包下提供
*/
public class ArraysTest {
public static void main(String[] args) {
//1. boolean equals(int[] a,int[] b)
int[] arr1 = new int[]{1,2,3,4,5};
int[] arr2 = new int[]{1,2,4,3,5};
System.out.println(Arrays.equals(arr1, arr2));
//2. String toString(int[] a)
System.out.println(arr1);//[I@1540e19d
System.out.println(Arrays.toString(arr1));
//3. void fill(int[] a,int val)
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1));
//4. void sort(int[] a)
int[] arr3 = new int[]{3, 42, 2, 53, 65, 24, 65, 87, -95, -84, 0, 45};
Arrays.sort(arr3);
System.out.println(Arrays.toString(arr3));
//5. int binarySearch(int[] a,int key)
System.out.println(Arrays.binarySearch(arr3, 53));
}
}
4. 常见异常
- ArrayIndexOutOfBoundsException
//1. 数据角标越界的异常:ArrayIndexOutOfBoundsException
//角标的合法范围为:[0,数组长度-1]
int[] arr = new int[10];
// System.out.println(arr[10]);
//System.out.println(arr[-1]);
- NullPointerException
//2. 空指针异常:NullPointerException
//情况1:
int[] arr1 = new int[10];
arr1 = null;
//System.out.println(arr1[0]);
//情况2:
int[][] arr2 = new int[10][];
//System.out.println(arr2[0][0]);
//情况3:后面面向对象内容中最常见的情况
String[] arr3 = new String[]{"AA","BB"};
arr3 = new String[4];
System.out.println(arr3[0].toString());
5. 面向对象的理解
- 学习面向对象的三条主线
* 面向对象部分内容学习的三条主线:
* 1. 类及类的成员:属性、方法、构造器(或构造方法);代码块、内部类
* 2. 面向对象的三大特征:封装性、继承性、多态性、(抽象性)
* 3. 其它关键字的使用:this\super\static\final\package\import\abstract\interface...
- 面向对象 vs 面向过程
* 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
* 面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
* * * 举例:把大象装进冰箱。
* * 面向过程:
* 1. 把冰箱门打开
* 2. 拿起大象,放进冰箱
* 3. 把冰箱门关住
* * 面向对象:
* * 冰箱{
* 开开(){
* * }
* 合上(){
* * }
* }
* * 大象{
* 进入(冰箱){
* * }
* * }
* * 人{
* 打开(冰箱){
* 冰箱.开开();
* }
* 拿起(大象){
* 大象.进入(冰箱);
* }
* 关闭(冰箱){
* 冰箱.合上();
* }
* }
6. 类与对象
* 类是对一类事物的描述,是抽象的、概念上的定义
* 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)
7. 类的内部设计
/**
* 设计类,其实就是设计类的内部结构。
* * 1. 名词解释:
* 属性 = 成员变量 = field = 域、字段
* * 方法 = 成员方法 = method = 函数(function)
* * 对象 = 实例
* * 创建对象 = 类的实例化
*/
- 代码演示
class Person{ //类:人
//设计类,就是设计类的内部成员
//首当其中,就是设计类的属性、方法
//成员一:属性
String name;
int age;
//成员二:方法
public void eat(){
System.out.println("人吃饭");
}
public void sleep(){
System.out.println("人睡觉");
}
public void think(){
System.out.println("人思考");
}
}
使用上述设计的类:
public class PersonTest { //测试类
public static void main(String[] args) { //程序的入口
//创建Person类的对象
//数据类型 变量名 = 变量值
int num = 10;
int[] arr = new int[10];
Scanner scann = new Scanner(System.in);
Person per = new Person();//创建了Person类的对象
//通过对象调用属性:“对象.属性”
//per.name = "江学振";
System.out.println(per.name);
per.age = 23;
System.out.println(per.age);
//通过对象调用方法:“对象.方法()”
per.eat();
per.sleep();
per.think();
Person per1 = new Person();
per.age = 30;
System.out.println(per1.age);//30
Person per2 = per;//per 和 per2指向了堆空间中的同一个对象实体
per2.age = 40;
System.out.println(per2.age);//40
System.out.println(per.age);//40
}
}
- 总结
如何由类创建对象以及实现功能的调用?
- 第1步:创建类,并设计类的内部成员。(属性、方法)
- 第2步:创建类的对象 (或类的实例化)
- 第3步:通过“对象.属性” 或 “对象.方法()” 的方式,完成功能的实现
- 内存解析
- 总结:对象的内存解析
- 1. 对象名的变量保存在栈空间中
- 对象实体保存在堆空间中,对象的属性也保存在堆空间中。
- 2. 如果创建类的多个对象,则每个对象拥有一套类的属性。如果修改其中一个对象的属性a的值,不会影响
- 其它对象的属性a的值。
8. 类的成员变量
- 代码演示
class Phone{
//属性:成员变量
String phoneName;
double price;
//方法
public void call(){
int hour = 1; //局部变量
System.out.println("手机打了" + hour + "小时的电话");
}
public void sendMessage(String message){ //message:局部变量
System.out.println("发送短信:" + message);
}
public void playGame(String game){//game:局部变量
System.out.println("玩游戏:" + game);
}
}
//测试类
public class PhoneTest {
public static void main(String[] args) {
Phone p1 = new Phone();
System.out.println(p1.phoneName);
System.out.println(p1.price);
p1.sendMessage("有内鬼,终止交易");
}
}
总结:
* 1. 复习:变量的分类: ① 按照数据类型: 基本数据类型 vs 引用数据类型
* ② 按照在类中声明的位置: 成员变量 vs 局部变量
*
* 2. 对比成员变量 vs 局部变量
* 相同点:①声明的格式是相同的。格式:数据类型 变量 = 变量值
* ②变量,必须先声明后使用
③变量,都有作用域,在其声明的作用域内是有效的。
* 不同点:
* ① 类中声明的位置不同
* 成员变量:直接声明在类的内部
* 局部变量:方法内部、方法的形参、构造器内部、构造器形参、代码块内部等
* ② 在内存中分配的位置不同
* 成员变量:分配在堆空间中
* 局部变量:分配在栈空间中
* ③ 成员变量声明以后,如果没显式赋值。可以有默认赋值。
* 整型:0
* 浮点型:0.0
* 字符型:0 或 \u0000
* boolean型:false
* 引用类型:null
*
* 而局部变量在调用前,必须显式赋值(初始化),因为没有默认赋值。而对方法的形参而言,是在调用方法时,给形参赋值。
* ④ 成员变量可以在声明前,添加权限修饰符。
* 而局部变量不能使用权限修饰符进行修饰。
* (此时的权限修饰符有:public \ private \ protected \缺省)
* ——————————————————————————————----------------------------------------------
* 补充:回顾变量的分类:
* 方式一:按照数据类型:基本数据类型 vs 引用数据类型
* 方式二:照声明的位置:
* 成员变量 :直接声明在类的内部
* 实例变量:不使用static修饰
* 全局变量:不使用static修饰
* vs
*
* 局部变量:
* 方法内声明的变量
* 方法的形参、构造器的形参
* 代码块内声明的变量