第六章:数组排序查找
总体内容
数组:array
数组
数组的介绍
- 数组是引用数据类型
- 数组用于存储一组同一类型的数据
- 数组的一个入门例子
3.1 定义一个数组:
3.2 获取数组中的数据
3.3 遍历整个数组,累计数组中的值
用3.4进行改进
3.4 获取数组的长度:数组.length
数组的具体使用
数组的定义和引用
- 数组定义的三种方法(1种静态,2种动态)
1.1 数据类型[] a = {元素值…}–>适用于元素个数有限(静态初始化–>元素值已经确定)
1.2 先声明后分配空间(动态初始化–>可以动态赋值)
int[] a; //声明
a = new int[3]; //分配空间
1.3 声明和分配空间同时进行(动态初始化–>可以动态赋值)
数据类型[] 数组名 = new 数据类型[大小] 等价为
数据类型 数组名[] = new 数据类型[大小]
int[] a = new int[3]; - 引用:数组名[下标]
2.1 数组的下标从0开始 - 循环输入5个数到double数组中,循环输出该数组
3.1 循环输入
3.2 循环输出
数组的细节
- 数组是多个相同类型数据的组合,或者数据间可以进行自动转换
- 数据中的数据类型可以是基本数据类型和引用数据类型,但不能混用
- 使用数组的步骤:①声明并开辟空间②赋值③使用
- 数组创建后,如果没有赋值,有默认值
3.1 整数类型:(byte,short,int,long)–>0
3.2 浮点数类型:(float,double)0.0
3.3 字符类型:(char)\u0000
3.4 布尔类型:(boolean)false
3.5 字符串类型:(String) null - 数组的下标必须在指定范围内使用,不然会报下标越界(长度-1)–>运行时会报错
- 数组是引用数据类型,数组型数据是对象(Object)
数组练习
练习1:字符数组存入A-Z(唯一注意:要强制类型转换)
'A’+1的返回值int类型,要强制转换为char类型
※练习2:求数组中的最大值,以及对应下标
- 思路:
1.1 假定数组第一个值是最大值,max=arr[0],maxIndex=0
1.2 从下标1开始遍历数组,如果max<当前元素,就max=当前元素,maxIndex=当前下标 - 代码
数组的赋值机制(内存布局)
- 基本类型的赋值和引用类型的赋值不同
1.1. 基本类型是传值(赋的值只是数值)–>值拷贝/传递
1.2 引用类型是传址(赋的值是地址)–>引用传递 - 基本类型指向值,引用类型指向地址(用内存解释)
数组拷贝(内容一致,地址独立)
使用new来开辟新空间
数组反转
1. 方法一:找规律交换
1.1重点是思路(找规律):
1.2. 代码(循环的次数,交换的两值以及如何交换)(缺少一个遍历,不写了)
2. 方法二:逆序赋值法(逆序拷贝+指向同一地址)
for循环定义了两个变量(一个向前一个向后)
数组扩容和缩减(动态添加或缩减元素)
数组扩容(对数组拷贝的应用)
- 数组扩容要求
1.1 动态给数组添加元素(数组扩容)
1.2 静态数组,添加的元素4放在数组最后
1.3 是否继续添加
- 思路:
2.1 大致就是arr数组拷贝->arrNew,但是长度是arr.length+1,前arr.length是arr原本的元素,最后一个是扩容的元素
2.2 定义一个新数组arrNew[],int[] arrNew = new int[arr.length+1]
2.3 遍历整个数组,依次将arr的元素拷贝到arrNew数组中
2.4 将4赋给arrNew[arrNew.length-1](最后一个元素)
2.5 让arr指向arrNew,原来的arr被销毁
2.6 最后一个动态添加的效果,使用do-while循环
注意:变量的作用域:
①在do循环体内定义的变量,在while中是不能使用的!!!
②全局变量在循环体内可以改变其值
③charAt(0),从字符串中取第一个字符
char key = 'n';
int[] arr = {1,2,3};
do{
int[] arrNew = new int[arr.length+1];
for(int i = 0;i < arr.length;i++){
arrNew[i] = arr[i];
}
System.out.println("请输入添加的数");
int num = scanner.nextInt();
arrNew[arrNew.length-1] = num;
arr = arrNew;
System.out.println("扩容后数组为:");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i]+"\t");
}
System.out.println("是否继续添加?y/n");
//第一种:在循环外定义变量,用while做出判断,跳出循环
key = scanner.next().charAt(0);
//第二种:在循环内定义变量,在循环内用if做判断,break跳出循环
//int key = scanner.next().charAt(0);
// if(key == 'n'){
// break;
// }
}while(key != 'n');
数组缩减
- 要求:
1.1 每次缩减数组最后一个元素
1.2 提示是否继续缩减?y/n
1.3 当数组中剩一个元素时,提示不能再缩减 - 思路
2.1是扩容的逆,新创建的数组长度是原先数组-1,然后数组拷贝到新数组,原数组指向新数组
2.2 解读要求3: 在数组拷贝完成后,对数组长度进行判断,如果长度=1,则使用break跳出循环
2.3 如果数组长度>1则不跳出循环,询问是否继续缩减,当输入为n时跳出循环
排序(主要介绍冒泡排序)
排序的介绍
冒泡排序
介绍
冒泡排序的案例
冒泡排序解析(从后向前)以及特点总结
- 第一轮排序
1.1 第一次比较 24,69,80,13,57
1.2 第二次比较 24,69,13,80,57
1.3 第三次比较 24,13,69,80,57
1.4 第四次比较 13,24,69,80,57 (此时最小的数已经冒出来了) - 第二轮排序
2.1 第一次比较 13,24,69,57,80
2.2 第二次比较 13,24,57,69,80
2.3 第三次比较 13,24,57,69,80 ** - 第三轮排序
3.1 第一次比较 13,24,57,69,80
3.2 第二次比较 13,24,57,69,80 - 第四轮排序
4.1 第一次比较 13,24,57,69,80
特点总结:
- 本案例一共5个元素
- 一共进行了4次排序(外循环次数)
- 每一次排序里比较的次数在减少(内循环次数)
- 每一次排序都能确定一个数的位置
- 第一次排序,内层循环4次,i=arr.length-1,i>0,i–; i=4 j=arr.length-1,j=4,3,2,1
i=3,j=4,3,2 arr.length-i
※冒泡排序代码实现(后到前和前到后)
- 从后到前的冒泡
1.1外层循环–>几轮排序
1.2 内层循环–>几次比较
1.3每一次比较都从最后一个元素开始,所以int j = arr.length-1
1.4 找规律得出j的临界
1.5 注意:j和j-1进行比较(和前一个进行比较),不能是j和j+1比较,因为那样会越界
int[] arr = {24,69,80,13,57};
int temp = 0;
//外层循环-->几轮排序
for(int i = arr.length-1;i > 0;i--){
//内层循环-->几次比较
//每一次比较都从最后一个元素开始,所以int j = arr.length-1
//找规律:i = 4时,j = 4,3,2,1
//i = 3时,j = 4,3,2
//以此类推发现:j最小的数 + i = 5 ,所以j的临界就是arr.length-i
for(int j = arr.length-1;j >= arr.length-i;j--){
//j和j-1进行比较,不能是j和j+1,那样会越界(和前一个进行比较)
if(arr[j - 1] > arr[j]){
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
System.out.println("=====排序后=====");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i]+"\t");
}
- 老师讲的从前到后的冒泡
int[] arr = {24,69,80,13,57};
int temp = 0;
for(int i = 0;i < arr.length - 1;i++){
for(int j = 0;j < arr.length - 1 - i;j++){
//和后一个进行比较
if(arr[j + 1] < arr[j]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("=====排序后=====");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i]+"\t");
}
查找(主要介绍顺序查找)
查找的介绍
二分查找:对有序数组进行查找,先让要查找的数和数组中间的数比较,若比中间数字大,则和数组右半边的中间的数进行比较,若比中间数字小,则和数组左半边的中间的数进行比较,依此类推
顺序查找案例
一个经典验证方法:
定义一个变量,判断查找有没有成功,如果成功,该变量值=下标,如果不成功,则保留该变量原先的数值
import java.util.Scanner;
public class HelloWorld {
public static void main(String []args) {
Scanner n = new Scanner(System.in);
String[] arr = {"王胖子","高瘦子","高梦梦"};
System.out.println("请输入一个名字");
String findName = n.next();
int index = -1;
for(int i = 0;i < arr.length;i++){
if(findName.equals(arr[i])){
System.out.println("找到了"+findName+"下标为:"+i);
index = i;
}
}
if(index == -1){
System.out.println("未找到"+findName);
}
}
}
多维数组-二维数组
二维数组介绍及快速入门案例
- 一维数组的每一个数组又是一个数组,称为二维数组
- 二维数组的定义
- 二维数组的遍历
3.1 arr[i]:下标为i的一维数组
3.2 arr.length:二维数组有多少个元素
3.3 arr[i].length:下标为i的一维数组长度
3.4 arr[i][j]:二维数组第(i+1)个一维数组的第(j+1)个的值
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr[i].length;j++){
System.out.print(arr[i][j]+"\t");
}
//换行
System.out.println();
}
二维数组的使用
动态初始化1(不写具体赋值)
1. 语法
数据类型 [][] 数组名 = new 数据类型[大小][大小]
1.1 第一个大小是一维数组的个数
1.2 第二个大小是一维数组中元素的个数
2. ※二维数组内存布局
二维数组在栈中指向堆中的一块地址,这个地址中保存了二维数组的两个元素(两个一维数组)的地址,这两个地址中保存了一维数组的元素
动态初始化2(不写具体赋值)
1. 语法
1.1 先声明:类型[][] 数组名;
1.2 再定义(开辟空间):数组名 = new 类型[大小][大小]
1.3 赋值(有默认值)
1.4 使用
2. 案例(※每个一维数组个数不一样)
- 先声明有3个一维数组,但是此时一维数组没有指向任何地址(没有开辟空间)
- 等到循环中,再为每一个一维数组开辟空间:arr[i]表示第几个一维数组,为一维数组开辟空间用到了new 类型[大小]:arr[i]=new int[i+1]
静态初始化(不写new)
- 每一个元素必须是一维数组!否则会报错,在这里100是一个int而不是数组。
- 使用
二维数组的细节
二维数组的案例–杨辉三角
- 要求
- 规律(重点是第三条规律)
- 代码
重点是:
如果是第一个和最后一个赋为1
如果是中间的(不是第一个和最后一个)就是arr[i - 1][j] + arr[i - 1][j - 1]
public class HelloWorld {
public static void main(String []args) {
int[][] arr = new int[10][];
for(int i = 0;i < arr.length;i++){
arr[i] = new int[i+1];
for(int j =0;j < arr[i].length;j++){
if(j == 0 || j == arr[i].length-1){
arr[i][j] = 1;
}else{
//重点是这一句:给不是第一个和最后一个元素赋值
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
}
}
}
for(int i = 0;i < arr.length;i++){
for(int j =0;j < arr[i].length;j++){
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}
}
}
- 运行结果
小练习
本章案例练习
一. 定义语法练习(好好看)
二. ※升序数组插入一个值,该数组仍升序(2种方法)
- 方法1:扩容 + 冒泡排序
Scanner scanner = new Scanner(System.in);
int[] arr = {10,12,45,90};
//数组扩容
//创建一个新数组,将arr数组拷贝到新数组中
//插入数字,并让arr指向新数组
int[] newArr = new int[arr.length+1];
System.out.println("请输入要插入的数字");
int findName = scanner.nextInt();
for(int i = 0;i < arr.length;i++){
newArr[i] = arr[i];
}
newArr[newArr.length - 1] = findName;
arr = newArr;
//进行冒泡排序
int temp = 0;
for(int i = arr.length - 1;i > 0;i--){
for(int j = arr.length - 1;j >= arr.length - i;j--){
if(arr[j - 1] > arr[j]){
System.out.println(arr[j-1]+"换成了"+arr[j]);
temp = arr[j];
arr[j] = arr[j - 1];
arr[j-1] = temp;
}
}
}
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i]+"\t");
}
- ※方法2:顺序查找 + 数组扩容
2.1 顺序查找,看添加的数要放在数组的哪个位置上(获取位置下标)
2.2 再进行扩容
顺序查找有两种情况
- 要插入的值比数组中的值都大,则插入到数组最后面(用一个index变量判断)
- 若数组中有比插入的值大的元素,那么插入值就插入到该元素的这个位置(index来接收下标值)
数组扩容
- 这里注意:通常的扩容在拷贝元素时,只定义一个变量i,使得旧数组的元素挨个拷贝进新数组
- 在这里要想把插入的数插入到对应的位置上,新旧两个数组的下标就不能同时增长,要定义两个变量i和j
2.1 当新数组下标值i不等于index时,i和j同时增长,旧数组的元素挨个拷贝进新数组
2.2 当新数组下标值i等于index时,j不增长,也就是旧数组中的下标不进行移动,把插入值赋给arrNew[i](下标:一个++,一个不动)
public class HelloWorld {
public static void main(String []args) {
int[] arr = {10,12,45,90};
//1. 顺序查找,看添加的数要放在数组的哪个位置上
int findName = 23;//可以变成输入,这里不变了
//这是一个接收/判断变量
int index = -1;
for(int i = 0;i < arr.length;i++){
/*1.1 输入的数和数组元素进行比较,将该数插到第一个大于该数的数组元素的位置上
findName <= arr[i]时,插入的下标为i */
if(findName <= arr[i]){
index = i;
break;
}
}
/*1.2 判断index的值,来判断输入的数是否找到了自己的位置
findName <= arr[i]不成立,则插入到最后位置*/
if(index == -1){
index = arr.length;
}
//2. 数组扩容
//将输入的数插到指定位置
int[] arrNew = new int[arr.length+1];
for(int i = 0,j = 0;i < arrNew.length;i++){
if(i != index){
arrNew[i] = arr[j];
j++;
}else{
arrNew[i] = findName;
}
}
arr = arrNew;
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i]+"\t");
}
}
}
三 . 随机生成10个数字存入数组,倒序打印,求平均值,max及其下标并查找里面有没有8
- 要求
- 代码
public class HelloWorld {
public static void main(String []args) {
//随机生成10个数字存入数组
int[] arr = new int[10];
double avg = 0;
double sum = 0;
for(int i = 0;i < arr.length;i++){
arr[i] = (int)(Math.random() * 100 + 1);
sum += arr[i];
}
System.out.println("======生成的数组为======");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i]+"\t");
}
System.out.println();
//倒序打印
int[] arrNew = new int[10];
for(int i = 0,j = arrNew.length - 1;i < arrNew.length;i++,j--){
arrNew[i] = arr[j];
}
System.out.println("======倒序后的数组为======");
for(int i = 0;i < arrNew.length;i++){
System.out.print(arrNew[i]+"\t");
}
System.out.println();
//计算平均值(注意精度)
avg = sum / arr.length;
System.out.println("======数组元素的平均值为======");
System.out.println("平均值 = " + avg);
//求最大值及其下标
int max = arr[0];
int index = 0;
for(int i = 1;i < arr.length;i++){
if(arr[i] > max){
max = arr[i];
index = i;
}
}
System.out.println("======数组元素的最大值及其下标为======");
System.out.println("最大值 = " + max);
System.out.println("最大值下标 = " + index);
//查找数组中是否有8(减少数据耦合,所以另外定义findNum而不是在程序中直接写8)
//为了让没找到时输出:没找到,所以定义了一个变量来判断(找到改变该值,没找到不改变)
int findNum = 8;
int index1 = -1;
for(int i = 1;i < arr.length;i++){
if(findNum == arr[i]){
System.out.println("======数组元素的是否有8======");
System.out.println("找到数"+findNum+"下标为"+index1);
break;
}
}
if(index1 == -1){
System.out.println("没有找到数"+findNum);
}
}
}