第6章 数组
6.1 数组的概念及定义
数组是用来存储数据的集合,通常我们会发现把数组看作一个存储具有相同类型 的变量集合会更有用,无须声明单个变量
数组主要用于解决大量数据计算与存储的问题
每一个数组而言,都是存在堆内存当中,每一个数组都是一个对象
创建数组:
创建数组变量未赋值时会有默认值,默认值时元素类型的0值;
当给数组分配空间时,必须指定该数组能够存储的元素个数,从而确定数组大小;
创建数组之后就不能再修改它的大小
//创建一个指定长度且指定数据类型的一维数组,名称为数组名,虽然没有指定元素,但是会有默认值
数据类型[] 数组名 = new 数据类型[长度];
//创建一个指定元素且指定数据类型的一维数组,名称为数组名,虽然有指定元素,还是有默认初始化这个步骤的!
数据类型[] 数组名 = new 数据类型[]{数据1,数据2,...,数据n};
数据类型[] 数组名 = {数据1,数据2,...,数据n};
数组定义以及初始化:
int []s={2,3};
int a[]={3,2};
int [][]arg1={{1,2},{3,4}};
int arg2[][]={{1,2},{3,4}};
int []arg3[]={{1,2},{3,4}};
//变量名位置不影响
int arg4[][]=new int[5][];
int arg5[]=new int[]{2};
int arg6[][]={{},{}};//定义不赋值
int arg7[][]=new int[][]{{1},{1,2},{9}};//定义且赋初值
System.out.println(arg7.length+","+arg7[0].length+","+arg7[1].length+","+arg7[2].length);//3,1,2,1
int arg8[][]=new int[][]{{100,200,300,400},{500,600,700,800},{900,1000,1100,1200,1300}};//定义且赋初值
int [][]atg9=new int[3][];//可只定义行不定义列
6.2 常用数组操作
数组遍历问题
数组元素可以通过下标访问。
数组下标是基于 0的,也就是说,其范围从 0 开时直到array.length - 1结束
for循环通过下标遍历
public class Sor {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
//String str str.length()-调用length函数获取srt的大小
//int[] arr arr.length-这是arr的数组长度,是属性
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 10;
System.out.println(arr[i]);
}
//通过角标遍历 可以在遍历的过程中对指定的元素进行修改
}
}
foreach遍历(不使用下标)
public class Sor {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
//foreach遍历 主要针对的是一些可迭代对象 Iterable
/*
for (数据类型 变量名 : 可迭代容器) {
}
*/
for (int num : arr) {
//num -> arr[i]
num = num / 10;
System.out.println(num);
}
//这种遍历方式 只能获取元素,不能修改元素
}
}
数组扩容问题
数组本身大小不能被改变,但是往数组中添加元素可以进行,
在堆中创建新的数组比原数组长度增加一个,
再遍历原数组,将原来的数据赋值到新数组,再将要添加的数据放入增加的空间中
最后让数组的地址指向新数组,这就完成了数组的扩容
public class Sor {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
arr = add(arr,6);
arr = add(arr,6);
arr = add(arr,6);
arr = add(arr,6);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
//在指定的数组arr中添加元素element
public static int[] add(int[] arr, int element) {
int[] newArr = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
newArr[newArr.length - 1] = element;
return newArr;
}
}
选择排序算法
解: 从第一位开始,用第一位依次与后面每一位比较最终将最小放到第一位
再从第二位开始,用第二位依次与后面每一位比较最终将第二小放到第二位
最终完成所有数字排序
public class Sample {
//选择排序
public static void main(String[] args) {
int[] arr = {8,9,2,6,7,1,4,5,3};
for (int i = 0; i < arr.length - 1; i++) { //-1 n个数字没有第n轮
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
swap(arr,i,j);
}
}
}
print(arr);
}
//[1, 2, 3, 4, 5]
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
冒泡排序算法
思路: 每次将第一个数字与后面的数字比较,比后面的大就交互位置,
反之不交换一直两两比较,冒出最大值到最后一位,
下一次循环减少一次,因为每一次循环比较都会找到当前的最大值,
最终会使最大的数在最后面去,形成升序
public class Sample {
//冒泡排序
public static void main(String[] args) {
int[] arr = {8,9,2,6,7,1,4,5,3};
for (int i = 0; i <arr.length - 1; i++) {//-1 表示n个数字只有n-1轮
for (int j = 0; j < arr.length - 1 - i; j++) {//-1 避免重复比较(当前最大和上一
轮最大)
if (arr[j] > arr[j + 1]) {
swap(arr,j,j+1);
}
}
}
print(arr);
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
插入排序算法
思路:
从数组的第二个数据开始往前比较,即一开始用第二个数和他前面的一个比较,如果比前面小,则让他们交换位置。
然后再用第三个数和第二个比较,如果比前面小则交换,然后在让这个现在处于第三位的数继续往前比较,如果小则交换,一直比较到左边的数不比它小为止
后面的依次重复
最终,后一个数总是比前一个大就得到升序
public class Sample {
//插入排序
public static void main(String[] args) {
int[] arr = {8,9,2,6,7,1,4,5,3};
for (int i = 1; i < arr.length; i++) {
int e = arr[i];
int j = 0;
for (j = i; j > 0 && arr[j - 1] > e; j--) {
arr[j] = arr[j - 1];
}
arr[j] = e;
}
print(arr);
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
}
二分查找算法
二分查找法前提是有序数组
思路: 在有序数组(这里默认升序)中,每次找到最小值,最大值,中间值,
比较中间值和当前寻找值的大小,大于则在有序数组的右边,重新赋值最大值,最小值,
一直重复直到找到元素,返回下标
如果当最小值的下标大于最大值的下标时表示没找到返回-1.
public class Sample {
//二分查找
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9};
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;
int key = 10;
while (arr[mid] != key) {
if (key < arr[mid]) {
max = mid - 1;
}
if (arr[mid] < key) {
min = mid + 1;
}
if (min > max) {
mid = -1;
break;
}
mid = (min + max) / 2;
}
System.out.println(mid);
}
}
计数排序:
解:
计数排序就是 把每个数出现的次数值 放入 标有和它一样数字的容器,一般采用数组来作为存这些数出现次数的容器。容器的标号就是数组的下标。排序时只需要顺序遍历一遍容器,把 有数据的容器 输出下标 数据中 下标值出现次数 次。
排序的数据为:2 - 1 1 2 3 6 2 6
我们根据数据的最大值,需要开一个数组int c[7],这样我们就有了容器c[0] ,c[1] ,c[2], c[3],c[4],c[5],c[6],c[7];计数之前令每个容器的初值为 0;
把 每个数的 出现次数 放入对应标号容器:
c[0] = 1;
c[2] = 1;
c[3] = 3;
c[4] = 1;
c[7] = 2;
public class Sample {
//计数排序
public static void main(String[] args) {
int[] arr = {-2,9,-1,12,8,-3,6,7,4,5,2,1,0,8,6,7,4,-3,-2,-1,-1,7};
int min = arr[0];
int max = arr[0];
//O(n)
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
int[] temp = new int[max - min + 1];
//对应关系 index = number - min number = index + min
//O(n)
for (int i = 0; i < arr.length; i++) {
temp[arr[i] - min]++;
}
//temp[index] 表示index对应的数字number出现的次数
int k = 0;
//O(n)
for (int index = 0; index < temp.length; index++) {
while (temp[index] != 0) {
arr[k] = index + min;
k++;
temp[index]--;
}
}
print(arr);
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
}
基数排序:
import java.util.LinkedList;
public class Sample {
//基数排序
public static void main(String[] args) {
int[] arr = {102,203,321,13,12,78,96,34,37,28,6,8,5,6};
//1.先找到最大值 决定轮数
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
int radex = (max + "").length();
//2.创建十个桶 每一个桶是LinkedList
LinkedList<Integer>[] queues = new LinkedList[10];
for (int i = 0; i < queues.length; i++) {
queues[i] = new LinkedList<Integer>();
}
//3.进行数字分类和规整
//r=0个位 r=1十位 r=2百位...
for (int r = 0; r < radex; r++) {
//先按照r进行分类
for (int i = 0; i < arr.length; i++) {
int index = getIndex(arr[i],r);//获取数字的r位 返回该数字要去的桶的角标0~9
queues[index].offer(arr[i]);
}
//然后在重新规整到arr里
int k = 0;
for (int index = 0; index < queues.length; index++) {
while(!queues[index].isEmpty()) {
arr[k++] = queues[index].poll();
}
}
}
print(arr);
}
public static int getIndex(int number, int r) {
//123 r=0
//123 r=1
//123 r=2
int index = 0;
for (int i = 0; i <= r; i++) {
index = number % 10;
number /= 10;
}
return index;
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
}
6.2 二维数组
二维数组:二维数组就是存储一维数组(内存地址/引用)的数组
二维数组的定义
-
int[][] intA={{1,2},{2,3},{3,4,5}};
-
int[][] intB=new int[3][5];
public class TestArray {
public static void main(String[] args) {
int[][] A ={{1,2},{2,3,4},{3,4,5,6}};
int[][] B = new int[3][4];
//二维数组不是规则的矩阵
int[][] C = {
{1},
{1,2,3},
{1,2,3,4},
{7,6,5,4,3,2,1}};
}
}
案例1
输入8个点坐标,然后计算这些点中,那两个点的距离是最近的?
解: 定义一个二维数组,来接受用户输入的八个点的坐标,
在循环判断两个坐标的距离,从而获取两点间的最短距离。
import java.util.Scanner;
public class Sample {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//定义一个的二维数组
double[][] points = new double[8][2];
//获取输入的点坐标
for (int i = 0; i < points.length; i++) {
System.out.print("请输入第" + (i + 1) + "个点坐标:");
points[i][0] = input.nextDouble();
points[i][1] = input.nextDouble();
}
//默认最短距离为点0和点1
double shortestDistance = getDistance(points,0,1);
int p1 = 0;
int p2 = 1;
for (int i = 0; i < points.length - 1; i++) {
for (int j = i + 1; j < points.length; j++) {
double distance = getDistance(points,i,j);
//比较判断最短距离,小于就获取该下标
if (distance < shortestDistance) {
shortestDistance = distance;
p1 = i;
p2 = j;
}
}
}
System.out.printf("(%.1f,%.1f)和(%.1f,%.1f)的距离为最短%.1f",points[p1]
[0],points[p1][1],points[p2][0],points[p2][1],shortestDistance);
}
public static double getDistance(double[][] m , int p1 , int p2) {
//计算两点间的距离
return Math.hypot(m[p1][0] - m[p2][0] , m[p1][1] - m[p2][1]);
}
}