数组的定义
之前在定义数据的时候,大部分都是用变量来存储数据。可是当我们的程序中需要大量的数据,我们需要连续输入多个数字,连续输入多个坐标点,一般而言会创建多个变量存储这些数据,显得比较麻烦。当这些变量基本上类型是共通的,那我们就可以用一个容器将所有的数字进行管理。类似于字符串,字符串其实就是若干个字符的容器而已,“abc”可以通过索引/角标来获取其中某一个字符。[1,2,3,4,5]类似字符串也可以通过索引/角标来获取其中某一个数字,那么这个容器我们称之为数组。
数组的本质
数组就是一片地址连续且空间大小一致的存储空间(但是每个空间存的还是其他数据的地址)
声明数组变量
声明数组:就是告诉计算机数组的类型是什么。有两种形式:
dataType[] array; // 首选的方法
或
dataType array[]; // 效果相同,但不是首选方法
实例
下面是这两种语法的代码示例:
double[] myList; // 首选的方法
或
double myList[]; // 效果相同,但不是首选方法
分配空间:告诉计算机需要给该数组分配多少连续的空间,记住是连续的。
array = new int[10];
赋值:赋值就是在已经分配的空间里面放入数据。
array[0] = 1 、array[1] = 2……
Java中我们定义的元素是默认被赋予初值的
- int类型:0
- double类型:0.0
- 布尔类型:false
- 引用数据类型:null
import java.util.Scanner;
public class Text08{
public static void main(String []args) {
int []num = new int[10];
Scanner in = new Scanner(System.in);
for (int i = 0;i<10;i++) {
num[i] = in.nextInt();
}
for (int i = 0;i<10;i++) {
System.out.println(num[i]);
}
}
}
数组通过角标来访问元素的具体计算方式是 所要访问数据的地址=首元素地址+角标*数据类型大小
其实分配空间和赋值是一起进行的,也就是完成数组的初始化。有如下三种形式:
int x[] = new int[9]; //默认为0,如果是引用数据类型就为null
int y[] = new int[] {1,4,5};
int z[] = {1,2,3};
创建数组
Java语言使用new操作符来创建数组,语法如下:
array = new dataType[arraySize];
上面的语法语句做了两件事:
- 使用 dataType[arraySize] 创建了一个数组。
- 把新创建的数组的引用赋值给变量 array。
创建数组时必须明确规定大小或内容。数组变量的声明,和创建数组可以用一条语句完成,如下所示:
dataType[] array = new dataType[arraySize];
数据类型[] 数组名=new 数据类型[长度];
创建数组只指定长度但不指定内容
数据类型[] 数组名=new 数据类型[]{1,2,3,4,5};
创建数组指定内容(指定长度)
另外,你还可以使用如下的方式创建数组。
dataType[] array = {value0, value1, ..., valuek};
数据类型[] 数组名={1,2,3,4,5};
创建数组指定内容(指定长度)
[]表示是一维数组
[][]表示二维数组
数组的元素是通过下标访问的。数组下标从 0 开始,所以下标值从 0 到 arrayRefVar.length-1。
实例
下面的语句首先声明了一个数组变量 myList,接着创建了一个包含 8 个 double 类型元素的数组,并且把它的引用赋值给 myList 变量。
public class TestArray {
public static void main(String[] args) {
// 数组大小
int size = 10;
// 定义数组
double[] myList = new double[size];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
myList[5] = 34.33;
myList[6] = 34.0;
myList[7] = 45.45;
myList[8] = 99.993;
myList[9] = 11123;
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < size; i++) {
total += myList[i];
}
System.out.println("总和为: " + total);
}
}
以上实例输出结果为:
总和为: 11367.373
下面的图片描绘了数组 myList。这里 myList 数组里有 10 个 double 元素,它的下标从 0 到 9。
处理数组
数组的元素类型和数组的大小都是确定
该实例完整地展示了如何创建、初始化和操纵数组
public class TestArray {
public static void main(String[] args) {
double[] myList = {1.9, 2.9, 3.4, 3.5};
// 打印所有数组元素
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i] + " ");
}
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < myList.length; i++) {
total += myList[i];
}
System.out.println("Total is " + total);
// 查找最大元素
double max = myList[0];
for (int i = 1; i < myList.length; i++) {
if (myList[i] > max) max = myList[i];
}
System.out.println("Max is " + max);
}
}
以上实例编译运行结果如下:
1.9 2.9 3.4 3.5 Total is 11.7 Max is 3.5
数组常见错误:
ArrayIndexOutOfBoundsException 数组角标越界
NullPointerException 空指针异常
数组内存
数组存在于堆内存中,数组变量存的就是数组在堆内存中首元素的地址
- 但凡在堆中存储的数据都称之为对象
- 但凡在堆内存中创建的对象都会有默认初始值
数组操作中,在栈内存中保存的永远是数组的名称,只开辟了栈内存空间数组是永远无法使用的,必须有指向的堆内存才可以使用,要想开辟新的堆内存则必须使用new关键字,之后只是将此堆内存的使用权交给了对应的栈内存空间,而且一个堆内存空间可以同时被多个栈内存空间指向,即:一个人可以有多个名字,人就相当于堆内存,名字就相当于栈内存。
遍历
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环,尤其是for循环。JDK 1.5 引进了一种新的循环类型,被称为 for-each 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
语法格式如下:
for(type element: array) { System.out.println(element); }
该实例用来显示数组 myList 中的所有元素:
public class TestArray {
public static void main(String[] args) {
double[] myList = {1, 2, 3, 5};
// 打印所有数组元素
for (double element: myList) {
System.out.println(element);
}
}
}
以上实例编译运行结果如下:
1 2 3 5
最大值/最小值
先给数组赋值,然后调用函数求最大值:
import java.util.Scanner;
public class Text08{
public static int max(int []a){
int max = a[0];
for (int i =0;i<a.length;i++) {
if (a[i]>max) max = a[i];
}
return max;
}
public static void main(String []args) {
int []num = new int[]{1,2,3,4,5,6,7,8,9,10};
Scanner in = new Scanner(System.in);
int max= max(num);
System.out.println(max);
}
}
我们通过函数将num数组中最大值通过遍历找出来,最后返回主函数输出
再给数组赋值,然后调用函数求最小值:
import java.util.Scanner;
public class Text08{
public static int min(int []a){
int min = a[0];
for (int i =0;i<a.length;i++) {
if (a[i]<min) min = a[i];
}
return min;
}
public static void main(String []args) {
int []num = new int[]{1,2,3,4,5,6,7,8,9,10};
Scanner in = new Scanner(System.in);
int min= min(num);
System.out.println(min);
}
}
查找操作
线性查找
定义:
在一列给定的值中进行搜索,从一端开始逐一检查每个元素,直到找到所需元素的过程。
线性查找又称为顺序查找。如果查找池是某种类型的一个表,比如一个数组,简单的查找方法是从表头开始,一次将每一个值与目标元素进行比较。最后,或者查找到目标,或者达到表尾,而目标不存在于组中,这个方法称为线性查找
import java.util.Scanner;
class Text08{
public static void main(String []args) {
int []num = new int[]{1,2,3,4,5,6,7,8,9,10};
Scanner in = new Scanner(System.in);
int key = 11;
int m = 0 ;
boolean find = false;
for (int i = 0;i<num.length;i++) {
if (num[i]==key) {
find = true;
m = i+1;
}
}
if(find) {
System.out.println("第"+m+"个");
}else {
System.out.println("没找到");
}
}
}
二分查找
定义:
二分查找又称折半查找,它是一种效率较高的查找方法。
二分查找要求:
- 必须采用顺序存储结构
- 必须按关键字大小有序排列。
优缺点
折半查找法的优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
import java.util.Scanner;
class Text08{
public static void main(String []args) {
int []num = new int[]{1,2,3,4,5,6,7,8,9,10};
Scanner in = new Scanner(System.in);
int key = 2;
int left = 0;
int right = num.length-1;
int mid = (left+right)/2;
boolean flag = true;
while (num[mid]!=key) {
if (num[mid]>key) {
right = mid-1;
mid = (left+right)/2;
}
if (num[mid]<key) {
left = mid+1;
mid = (left+right)/2;
}
if (left>right) {
flag = false ;
break;
}
}
if (flag) {
System.out.print(mid);
}else {
System.out.print("no find");
}
}
}
*斐波那契查找
黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。0.618被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。因此被称为黄金分割。
斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和)。然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
斐波那契搜索是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为F[n](如果要补充元素,则补充重复最后一个元素,直到满足F[n]个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。