数组
-
- 数组
-
-
- 一、数组的概述
-
- [1.1 数组的定义](#1.1 数组的定义)
- [1.2 数组的常见概念](#1.2 数组的常见概念)
- [1.3 数组的特点](#1.3 数组的特点)
- [1.4 数组的分类](#1.4 数组的分类)
- 二、一维数组的使用
-
- [2.1 一维数组的声明和初始化](#2.1 一维数组的声明和初始化)
- [2.2 数组的基本使用](#2.2 数组的基本使用)
- [2.3 数组元素的默认初始化值](#2.3 数组元素的默认初始化值)
- [2.4 数组的内存解析](#2.4 数组的内存解析)
- 三、多维数组的使用
-
- [3.1 二维数组的理解](#3.1 二维数组的理解)
- [3.2 二维数组的声明](#3.2 二维数组的声明)
- [3.3 二维数组的引用](#3.3 二维数组的引用)
- [3.4 二维数组的默认初始化值](#3.4 二维数组的默认初始化值)
- [3.5 二维数组的内存分析](#3.5 二维数组的内存分析)
- [3.6 二维数组的练习](#3.6 二维数组的练习)
- 四、数组中常见的算法
-
- [4.1 数组中涉及的常见算法](#4.1 数组中涉及的常见算法)
- [4.2 二分法查找算法](#4.2 二分法查找算法)
- [4.3 排序算法](#4.3 排序算法)
- [4.4 冒泡排序](#4.4 冒泡排序)
- [4.5 快速排序](#4.5 快速排序)
- [4.6 各种内部排序方法性能比较](#4.6 各种内部排序方法性能比较)
- [4.7 排序算法的选择](#4.7 排序算法的选择)
- [4.8 Arrays工具类的使用](#4.8 Arrays工具类的使用)
-
数组
一、数组的概述
1.1 数组的定义
数组(Array) ,是多个相同类型数据
按一定顺序排列
的集合,并使用一个名字命名
,并通过编号
的方式对这些数据进行统一管理。
1.2 数组的常见概念
- 数组名
- 下标(或索引)
- 元素
- 数组的长度
1.3 数组的特点
- 数组本身是
引用数据类型
,而数组中的元素可以是任何数据类型
,包括基本数据类型和引用数据类型
。 - 创建数据对象会在内存中开辟一整块
连续的空间
,而数组名中引用的是这块连续空间的首地址
。 - 数组的
长度一旦确定,就不能修改
。 - 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
1.4 数组的分类
- 按照维度:一维数组、二维数组、三维数组、...
- 按照元素的数据类型分:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
二、一维数组的使用
2.1 一维数组的声明和初始化
- 静态初始化:数组的初始化和数组元素的赋值操作同时进行
- 动态初始化:数组的初始化和数组元素的赋值操作分开进行
java
//1. 一维数组的声明和初始化
int num; //声明
num = 10;
int id = 1001;// 声明+ 初始化
int[] ids;//声明
//1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行
ids = new int[]{1001, 1002, 1003, 1004};
int id2[] = new int[]{1001, 1002, 1003, 1004};
//1.2 动态初始化:数组的初始化和数组元素的赋值操作分开进行
String[] names = new String[5];
- 错误的写法
java
int[] arr1 = new int[];
int[5] arr2 = new int[5];
int[] arr3 = new int[3]{1, 2,4};
2.2 数组的基本使用
- 定义并用运算符new为之分配空间后,才可以引用数组中的每个元素;
- 数组元素的引用方式:数组名[数组元素下标 ]
- 数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
- 数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 --->n-1;如int a[]=new int[3]; 可引用的数组元素为a[0]、a[1]、a[2]
- 每个数组都有一个属性length指明它的长度,例如:a.length 指明数组a的长度(元素个数)
- 数组一旦初始化,其长度是不可变的
java
//总结:数组一旦初始化完成,其长度就确定了
//(2)如何调用数组指定位置的元素:通过索引的方式调用
//数组的索引是从0开始的,到数组的长度-1结束
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
names[3] = "赵六";
names[4] = "孙七";
//3、如何获取数组的长度
//属性 length
System.out.println(ids.length);
System.out.println(names.length);
//4、如何遍历数组
for (int i = 0; i < ids.length; i++){
System.out.println(ids[i]);
}
for (int i = 0; i < names.length; i++){
System.out.println(names[i]);
}
2.3 数组元素的默认初始化值
数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化。
数组元素类型 | 元素默认初始值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | 0或写:'\u0000' (表现为空) |
boolean | false |
引用数据类型 | null |
java
System.out.println("----------byte数组初始化值-----------");
byte[] byteArray = new byte[1];
for (int i = 0; i < byteArray.length; i++) {
System.out.println(byteArray[i]);
}
System.out.println("----------short数组初始化值-----------");
short[] shortArray = new short[1];
for (int i = 0; i < shortArray.length; i++) {
System.out.println(shortArray[i]);
}
System.out.println("----------int数组初始化值-----------");
int[] intArray = new int[1];
for (int i = 0; i < intArray.length; i++) {
System.out.println(intArray[i]);
}
System.out.println("----------long数组初始化值-----------");
long[] longArray = new long[1];
for (int i = 0; i < longArray.length; i++) {
System.out.println(longArray[i]);
}
System.out.println("----------float数组初始化值-----------");
float[] floatArray = new float[1];
for (int i = 0; i < floatArray.length; i++) {
System.out.println(floatArray[i]);
}
System.out.println("----------double数组初始化值-----------");
double[] doubletArray = new double[1];
for (int i = 0; i < doubletArray.length; i++) {
System.out.println(doubletArray[i]);
}
System.out.println("----------char数组初始化值-----------");
char[] charArray = new char[1];
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}
System.out.println("----------boolean数组初始化值-----------");
boolean[] booleanArray = new boolean[1];
for (int i = 0; i < booleanArray.length; i++) {
System.out.println(booleanArray[i]);
}
System.out.println("----------引用类型数组初始化值-----------");
String[] stringArray = new String[1];
for (int i = 0; i < stringArray.length; i++) {
System.out.println(stringArray[i]);
}
java
----------byte数组初始化值-----------
0
----------short数组初始化值-----------
0
----------int数组初始化值-----------
0
----------long数组初始化值-----------
0
----------float数组初始化值-----------
0.0
----------double数组初始化值-----------
0.0
----------char数组初始化值-----------
----------boolean数组初始化值-----------
false
----------引用类型数组初始化值-----------
null
2.4 数组的内存解析
- jvm示意图
- 数组内存解析示例:
java
int[] arr = new int[]{1,2,3};
String[] arr1 = new String[4];
arr1[1] = "刘德华";
arr1[2] = "张学友";
arr1 = new String[3];
三、多维数组的使用
3.1 二维数组的理解
- Java 语言里提供了支持多维数组的语法
- 如果说可以把一维数组当成几何中的线性图形,那么二维数组就相当于是一个表格,像右图Excel中的表格一样。
- 对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组。
- 二维数组:数组中的数组。
3.2 二维数组的声明
java
//1.二维数组的声明和初始化
int[] arr = new int[]{1, 2, 3};//一维数组
//静态初始化
int[][] arr1 = new int[][]{{1,2,3}, {4,5,6}, {7,8}};
//动态初始化
String[][] arr2 = new String[3][2];
String arr3[][] = new String[3][2];
String[] arr4[] = new String[3][2];
- 非法
java
int[][]arr = new int[][3]; //非法
3.3 二维数组的引用
java
//静态初始化
int[][] arr1 = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8}};
//动态初始化1
int[][] arr2 = new int[3][2];
//动态初始化2
int[][] arr3 = new int[3][];
//这样也是正确的 但是不推荐
// int arr4[][];
// int[] arr4[];
//2、如何调用数组指定位置的元素
System.out.println(arr1[0][1]);//2
System.out.println(arr2[1][1]);//0
arr3[1] = new int[2];
System.out.println(arr3[1][1]);//0
//3、获取数组的长度
System.out.println(arr1.length);//3
System.out.println(arr1[0].length);//3
System.out.println(arr1[1].length);//3
//4、如何遍历二维数组
for(int i = 0; i < arr1.length; i++){
for (int j = 0; j < arr1[i].length; j++){
System.out.print(arr1[i][j] + " ");
}
System.out.println();
}
3.4 二维数组的默认初始化值
java
//int类型
int[][] arr = new int[3][];
System.out.println(arr[0]);// null
// System.out.println(arr[0][0]); //NullPointerException
System.out.println(arr); //[[I@1b6d3586
int[][] arr1 = new int[3][4];
System.out.println(arr1[0]);// [I@1b6d3586 地址值
System.out.println(arr1[0][0]); //0
System.out.println(arr1); //[[I@74a14482
//float类型
float[][] arr3 = new float[3][4];
System.out.println(arr3[0]);// [F@1540e19d 地址值
System.out.println(arr3[0][0]); //0.0
//String类型
String[][] arr4 = new String[3][4];
System.out.println(arr4[0]);// [Ljava.lang.String;@677327b6 地址值
System.out.println(arr4[0][0]); //null
3.5 二维数组的内存分析
java
int[][] arr1 = int[4][];
arr1[1] = new int[]{1,2,3};
arr1[2] = new int[4];
arr1[2][1] = 30;
int[4][] 中int[0]的值为null, 是因为此时的int[]引用类型没有赋值 所以是null
3.6 二维数组的练习
- x 表示int[] y 表示int[][] y[0] 表示int[]
- 一维数组:int[] x 或者int x[];
- 二维数组:int[][] y 或者int[] y[]或者int y[][]
java
注意:x 表示int[] y 表示int[][] y[0] 表示int[]
声明:int[] x,y[];在给x,y变量赋值以后,以下选项允许通过编译的是:
a) x[0] = y;//错误 类型不一致 x是一维数组 y是二维数组
b) y[0] = x; //编译通过
c) y[0][0] = x;//y[0][0]表示的是值, x表示的是地址值
d) x[0][0] = y;//错误
e) y[0][0] = x[0];//编译通过
f) x = y; //错误 x表示int[],y表示int[][]
四、数组中常见的算法
4.1 数组中涉及的常见算法
- 数组元素的赋值(杨辉三角、回形数等)
- 求数值型数组中的最大值、最小值、平均数、总和等。
- 数组的复制、反转、查找(线性查找、二分法查找)
- 数组元素的排序算法
4.2 二分法查找算法
java
public static void main(String[] args) {
int[] array = {1,4,5,23,45,67};
int targetNum = -67;
binarySearch(array, targetNum);
}
public static void binarySearch(int[] source, int targetNum) {
int head = 0;
int end = source.length -1;
while(head <= end) {
int middle = (head+end)/2;
if (source[middle] > targetNum) {
end = middle - 1;
} else if (source[middle] < targetNum) {
head = middle + 1;
} else {
System.out.println("数据找到了" + source[middle] + "当前位置为" + middle);
break;
}
}
if (head > end) {
System.out.println("数据未找到" + targetNum);
}
}
4.3 排序算法
- 排序:假设含有n个记录的序列为{R1,R2,...,Rn},其相应的关键字序列为{K1,K2,...,Kn}。将这些记录重新排序为{Ri1,Ri2,...,Rin},使得相应的关键
字值满足条Ki1<=Ki2<=...<=Kin,这样的一种操作称为排序。 - 通常来说,排序的目的是快速查找。
- 衡量排序算法的优劣:
- 时间复杂度:分析关键字的比较次数和记录的移动次数
- 空间复杂度:分析排序算法中需要多少辅助内存
- 稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
- 排序算法分类
- 内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
- 外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
- 十大内部排序算法
- 选择排序:直接选择排序、堆排序
- 交换排序: 冒泡排序、快速排序
- 插入排序:直接插入排序、折半插入排序、Shell排序
- 归并排序
- 桶式排序
- 基数排序
- 算法的5大特征
4.4 冒泡排序
-
介绍 :
冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
-
排序思想:
- 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。
java
//冒泡排序
public static void bubbleSort(int[] source) {
for (int i = 0; i < source.length; i++) {
for (int i1 = i + 1; i1 < source.length; i1++) {
if (source[i] < source[i1]) {
int temp = source[i];
source[i] = source[i1];
source[i1] = temp;
continue;
}
}
}
for (int i : source) {
System.out.print(i + " ");
}
}
//改进
public static void bubbleSortUpgrade(int[] source) {
for (int i = 0; i < source.length-1; i++) {//最后一个数不用在继续比较啦
for (int i1 = i + 1; i1 < source.length-1-i; i1++) {//不用在和之前的数据去进行比较啦
if (source[i] < source[i1]) {
int temp = source[i];
source[i] = source[i1];
source[i1] = temp;
continue;
}
}
}
for (int i : source) {
System.out.print(i + " ");
}
}
//数组反转 方式一
public static void arrayInversion(int[] source) {
int[] newSource = new int[source.length];
for (int i = source.length - 1,j=0; i >= 0; i--,j++) {
newSource[j] = source[i];
}
for (int i : newSource) {
System.out.print(i + " ");
}
}
//数组反转 方式二
public static void arrayInversionTwo(int[] source) {
for (int i = 0; i < source.length/2; i++) {
int temp = source[i];
source[i] = source[source.length-i-1];
source[source.length-i-1] = temp;
}
for (int i : source) {
System.out.print(i + " ");
}
}
//获取最大值
public static void getArrayMax(int[] source) {
int max = source[0];
for (int i = 0; i < source.length; i++) {
if (max < source[i]) {
max = source[i];
}
}
System.out.println("max = " + max);
}
//获取最小值
public static void getArrayMin(int[] source) {
int min = source[0];
for (int i = 0; i < source.length; i++) {
if (min > source[i]) {
min = source[i];
}
}
System.out.println("min = " + min);
}
4.5 快速排序
- 介绍
快速排序通常明显比同为O(nlogn)的其他算法更快,因此常被采用,而且快排采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。可见掌握快排的重要性。
快速排序(Quick Sort)由图灵奖获得者Tony Hoare发明,被列为20世纪十大算法之一,是迄今为止所有内排序算法中速度最快的一种。冒泡排序的升级版,交换排序的一种。快速排序的时间复杂度为O(nlog(n))。 - 排序思想:
从数列中挑出一个元素,称为"基准"(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
4.6 各种内部排序方法性能比较
- 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归并排序。
- 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序。
- 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排序、快速排序、 Shell排序和堆排序是不稳定排序
- 从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采用改进排序。
4.7 排序算法的选择
- 若n较小(如n≤50),可采用直接插入或直接选择排序。当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直
接插入,应选直接选择排序为宜。 - 若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;
- 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
4.8 Arrays工具类的使用
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。
方法 | 作用 |
---|---|
boolean equals(boolean[] a, boolean[] a2) | 判断两个数组是否相等 |
String toString(int[] a) | 输出数组信息 |
void fill(int[] a, int val) | 将指定值填充到数组中 |
void sort(int[] a) | 对数组进行排序 |
int binarySearch(int[] a, int key) | 对排序后的数组进行二分法检索指定的值 |
java
int[] array = {99,76,64,43,33,32,32,21,0,-98};
int[] array1 = {99,76,64,43,33,32,32,21,0,-98};
int[] array3 = {99};
// System.out.println(Arrays.equals(array3, array2));
System.out.println(Arrays.equals(array1, array));
System.out.println(Arrays.toString(array));
System.out.println("------------填充之前--------------");
Arrays.fill(array, 156);
Arrays.fill(array, 0, 3, 157);
System.out.println(Arrays.toString(array));
System.out.println("------------填充之后--------------");
System.out.println("------------排序前--------------");
Arrays.sort(array1);
Arrays.sort(array1);
System.out.println(Arrays.toString(array1));
System.out.println("------------排序之后--------------");
// int[] array2 = {99,76,64,43,33,32,21,0,-98};
int[] array2 = {-98,0,21,32,33,43,64,76,99};
System.out.println(Arrays.binarySearch(array2, 33));//有序的数组,且从小到大排列
System.out.println(Arrays.binarySearch(array2, 64));
//System.out.println(Arrays.binarySearch(array2, 3));