目录
一、何为数组
- 数组是相同类型数据的有序集合
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
- 每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问
二、数组的声明和创建
1、数组的声明
- 类型 [ ] 数组名 (首选方法)
- 类型 数组名 [ ]
例:声明一个 int 类型的,名称为 nums 的数组
int[] nums; // Java的普遍写法
int nums[]; // 方便 c 或 c++ 程序员快速过渡到 Java 的写法
2、数组的创建
- 类型 [ ] 数组名 = new 类型[数组大小]; 声明+创建
- 数组名 = new 类型[数组大小]; 在已声明的条件下进行创建
例:定义大小为10的int数组
nums = new int[10];
注:数组下标从0开始
例:为大小为10的int数组赋值并计算总和
public class demo01 {
public static void main(String[] args) {
int [] nums = new int[10]; // 声明并创建
// 数组下标从0到9,用 i 来进行遍历
for(int i = 0; i < 10; i++){
nums[i] = i + 1; // 用“数组名[下标]”来访问数组元素
}
int sum = 0; //记录总和
// 遍历数组并依次相加
for (int i = 0; i < 10; i++) {
sum = sum + nums[i];
}
System.out.println(sum);
}
}
三、 内存分析及三种初始化
1、内存分析
(1)堆
- 存放 new 的对象和数组
- 可以被所有的线程共享,不会存放别的对象引用
(2)栈
- 存放基本变量类型(会包含这个基本类型的具体数值)
- 引用对象的变量(会存放这个引用在堆里面的具体地址)
(3)方法区
- 可以被所有的线程共享
- 包含了所有的 class 和 static 变量
2、三种初始化
(1)静态初始化
int [] a = {1, 2, 3, 4, 5};
Man [] man = {new Man(1,1), new Man(2,2)};
(2)动态初始化
int [] a = new int[2];
a[0] = 1;
a[1] = 2;
(3)默认初始化
- 数组属于引用类型,其元素相当于类的实例变量,故数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化
- int 型默认初始化为0,double 型默认初始化为0.0 , String 型默认初始化为null,Boolean 型默认初始化为false
四、数组的特点
- 其长度是确定的。数组一旦被创建,其大小就不能再被改变
- 其元素必须是相同类型,不允许出现混合类型
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量
- 数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本事是在堆中的
- 若数组长度为 n,则其下标从 0 到 n-1
五、数组的使用
1、普通的for循环
例:计算数组中的最大值并输出
public class demo02 {
public static void main(String[] args) {
// 计算数组中的最大值并输出
int[] num = {2,6,9,5,7};
int max = num[0]; // 假设 num[0] 是最大元素
// 进行“打擂台”算法
for(int i = 1; i < 5; i++){
if(num[i] > max)
max = num[i]; // 更新最大值
}
System.out.println("最大值为:" + max);
}
}
2、For-Each 循环
例:for-each增强循环遍历数组元素并输出
public class demo03 {
public static void main(String[] args) {
int [] num = {1,2,3,4,5};
// for-each 增强循环
for (int array : num) {
System.out.println(array);
}
}
}
3、数组作方法入参
例:将打印数组作为方法使用
public class demo04 {
public static void main(String[] args) {
int[] nums = {1,2,3,4,5};
arrayPrint(nums); //调用 打印数组方法
}
// 定义 打印数组方法
public static void arrayPrint(int[] num) {
for (int i = 0; i < num.length; i++) {
System.out.println(num[i]);
}
}
}
4 、数组作返回值
例:反转原数组
public class demo05 {
public static void main(String[] args) {
int[] num = {1,2,3,4,5};
reverse(num);
demo04.arrayPrint(num); //调用同一个包中的public方法
}
public static int[] reverse(int[] nums) {
int n = nums.length;
for(int i = 0, j = n - 1; i < n/2; i++, j--){
// 经典:交换两变量值的语句体
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
return nums;
}
}
六、多维数组
1、含义:多维数组可以看作是数组的数组
2、二维数组:可以看作是 i行j列 的数组
类型 数组名 [][] = new 类型 [i][j];
类型 [][] 数组名 = { {···},{···},{···}, ··· };
例:以数组为参数,打印二维数组
public class demo06 {
public static void main(String[] args) {
//二维数组的初始化
int[][] num = { {1,2}, {2,3}, {3,4} };
printArrays(num);
}
// 打印二维数组
public static void printArrays(int[][] nums) {
for (int i = 0; i < nums.length; i++) {
for(int j = 0; j < nums[i].length; j++)
System.out.print(nums[i][j] + "\t");
System.out.println();
}
}
}
七、Arrays类
1、java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
2、常用功能
- public static int binarySearch(Object[] a, Object key)
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作
- public static boolean equals(long[] a, long[] a2)
比较数组:通过 equals 方法比较数组中元素值是否相等
- public static void fill(int[] a, int val)
给数组赋相同的值:将 val 赋值给数组a 的每一个元素
- public static void sort(Object[] a)
对数组排序:通过 sort 方法,按升序对数组进行排序
import java.util.Arrays;
public class demo07 {
public static void main(String[] args) {
// 1、按升序对数组进行排序
int[] a = {5,9,87,67,34,2,1};
Arrays.sort(a); //升序 排序
System.out.println(Arrays.toString(a)); //打印
//[1, 2, 5, 9, 34, 67, 87]
// 2、用 val 赋值给数组中每个元素
int[] b = {1,1,1,1,1,1};
Arrays.fill(b,7);
System.out.println(Arrays.toString(b));; //打印
//[7, 7, 7, 7, 7, 7]
// 3、在数组中进行二分查找 key 值
System.out.println(Arrays.binarySearch(a,34));
//4
// 4、比较两个数组是否相等
long[] c = {1,2,3,4,5};
long[] d = {5,4,3};
long[] e = {5,4,3};
System.out.println(Arrays.equals(c,d));
//false
System.out.println(Arrays.equals(e,d));
//true
}
}
八、稀疏数组
1、含义
2、适用情况
- 当一个数组中大部分元素为0,或者为同一值的数组时,可以用稀疏数组来保存该数组
public class demo09 {
public static void main(String[] args) {
// 定义数组
int[][] array1 = new int [10][10];
array1[2][4] = 5;
array1[4][5] = 7;
demo06.printArrays(array1); // 使用同一包类的public方法打印数组,具体方法在上述方法 demo06 中
// 1、转化为稀疏数组
int sum = 0;
//记录非零元素个数
for (int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++){
if(array1[i][j] != 0)
sum++;
}
}
//定义稀疏数组,并初始化表头
int[][] array2 = new int[sum+1][3];
array2[0][0] = 10;
array2[0][1] = 10;
array2[0][2] = sum;
// 初始化其他元素
int col = 1;
for (int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++){
if(array1[i][j] != 0){
array2[col][0] = i;
array2[col][1] = j;
array2[col][2] = array1[i][j];
col++;
}
}
}
System.out.println("===================");
demo06.printArrays(array2);
// 2、还原为原数组
int[][] array3 = new int[ array2[0][0] ][ array2[0][1] ];
for (int i = 1; i < sum+1; i++) {
array3[ array2[i][0] ][ array2[i][1] ] = array2[i][2];
}
System.out.println("===================");
demo06.printArrays(array3);
}
}
九、数组与排序
1、基本查找
- 遍历整个数组,逐一与目标值比较
public class BasicLookup {
public static void main(String[] args) {
int[] a = {10,57,8,34,67,48,66};
System.out.println(basicLookup(a,34)); // 3
}
//查找算法
private static int basicLookup(int[] array, int i) {
for(int j = 0; j < array.length; j++)
if(i == array[j])
return j;
return -1;
}
}
2、二分查找
(1)使用前提:数组元素是有序的
(2)算法思想:每一次都查找、比较中间的元素;若目标值比中间值小,则在其左侧再进行相同的步骤,否则在右侧进行
public class TwoPointLookup {
public static void main(String[] args) {
int[] a = {10,20,30,40,50,60,70,80};
System.out.println(twoPointLookup(a,70));
}
// 二分查找算法
private static int twoPointLookup(int[] array, int target) {
int low = 0;
int high = array.length - 1;
while (low < high){
int mid = low + (high - low) / 2; //定义中间值下标
// 将目标值与中间值进行比较
if ( target == array[mid] )
return mid; // 如果相等则直接返回
else if ( target < array[mid])
high = mid - 1; //更改查找范围,在 原low ~ 原mid 间查找
else
low = mid + 1; //更改查找范围,在 原mid ~ 原high 间查找
}
return -1; //数组中没有目标值时返回-1
}
}
3、冒泡排序
核心
(1)比较数组中相邻的两个元素,若前一个数比后一个数大,则交换二者位置
(2)每一次比较,都会产生一个最大或最小的值
(3)第一轮从0到n,第二轮从1到n-1,以此类推,直至结束
public static void pSort(int[] array){
for(int i = 0; i < array.length-1; i++){
for(int j = 0; j < array.length-1-i;j++){
if(array[j+1] < array[j]){
int temp = array[j+1];
array[j+1] = array[j];
array[j] = temp;
}
}
}
}
4、选择排序
(1)思想:每轮选择出一个最大/最小的元素,放在每轮的起始位置
(2)算法
- 第一轮从0下标开始,选择出一个最大/最小的元素与array[0]交换
- 第二轮从1下标开始,选择出一个最大/最小的元素与array[1]交换
· · · · · ·
- 第(n-1)轮从 n-1 开始,选择出一个最大/最小的元素与array[n-1]交换
// 选择排序
private static int[] selectSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
int min = i; //假设每轮开头的元素为最小值,随后进行打擂台
for(int j = i+1; j < array.length; j++){
if(array[min] > array[j])
min = j; // 更新最小值下标
}
// 将最小值放在每轮开头的位置
int temp = array[i];
array[i] = array[min];
array[min] = temp;
}
return array;
}
5、直接插入排序
(1)思想:将一个数插入到原数组(有序)中,时其仍然保持有序状态
(2)算法
- 从1下标开始(一个元素的数组总是有序的)进行循环
- 比较后一个元素与前一个元素,若 array[j] < array[j-1] 则交换两者位置,再向前找比它更小的元素
//直接插入排序
private static int[] insertSort(int[] array) {
// 一个元素的排序总是有序的,故从索引1处开始插入
for (int i = 1; i < array.length; i++) {
// 依次往前,寻找一个比它小(最小)的数
for(int j = i; j > 0; j--){
if(array[j] < array[j-1]){
int temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
}
}
}
return array;
}
6、希尔排序
(1)思想: 以增量h为间隔比较两个元素,排成有序元素,随后减少h再进行相同的操作,直至h=1时完成对所有数的排序
(2)算法
- 根据克努特序列寻找最佳增量h
- 从下标h开始,直至 array.length-1 为止,进入循环
- 排列此元素和与之相隔 h 距离的元素(向前寻找)
- 对 h 进行 h = (h-1) / 3 操作,减少 h 的值,再次进行以上操作,直至 h=1 为止
// 希尔算法
private static int[] hillSort(int[] array) {
// 根据克努特序列,寻找最佳增量
int interval = 1;
while(interval < array.length/3)
interval = interval * 3 + 1;
// 插入算法的进阶
// 增量 h 需不断变化,直至为 1 时完成排序后结束
for (int h = interval; h > 0; h = (h-1)/3) {
// 从 h 索引处开始排序
for (int i = h; i < array.length; i++) {
// 将 h 索引值 与前面元素进行比较
for (int j = i; j > h-1; j = j-h) {
if(array[j] < array[j-h]){
int temp = array[j];
array[j] = array[j-h];
array[j-h] = temp;
}
}
}
}
return array;
}
7、快速排序
(1)思想:寻找一个基准数,将数组中大于它的数放右边,小于它的数放左边,再对左右两边进行相同的操作
(2)算法
- 一般地,选择 array[0] 为基准数
- 设定 j,从后往前找小于 array[0] 的数,设定 i,从前往后找大于 array[0] 的数,交换两者位置
- 直至 j =i 为止(总是 j 先寻找),将 array[0] 与 array[i] 交换位置
- 对原基准数的左右两侧再进行相同的操作
//快速排序
private static void quickSort(int[] array, int low, int high) {
if(low > high)
return;
int i = low;
int j = high;
int base = array[low];
while (i < j) {
//先对右侧进行查找
while (base <= array[j] && i < j) {
j--;
}
//再对左侧进行查找
while (base >= array[i] && i < j) {
i++;
}
//交换两者的值
if (i < j) {
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
//将基准数与二者重合处的数进行交换
array[low] = array[i];
array[i] = base;
//对左右两侧再进行相同的操作
quickSort(array,low,j-1);
quickSort(array,j+1,high);
}
8、2路归并排序
(1)思想:对数组进行对半拆分直至分为一个个的,再两两排序至有序状态
(2)算法
- 对数组进行对半拆分,再进行递归的左右两侧拆分至单个元素
- 定义一个临时数组
- 比较左右数组,谁小谁先放入临时数组中,最后再把剩余元素全部放入临时数组中
//2路归并排序
// 拆分
private static void sort(int[] array, int startIndex, int endIndex) {
//计算中间索引
int centerIndex = (startIndex + endIndex) / 2;
if(startIndex < endIndex){
sort(array, startIndex, centerIndex);
sort(array, centerIndex+1, endIndex);
merge(array, startIndex, centerIndex, endIndex);
}
}
// 合并
private static void merge(int[] array, int startIndex, int centerIndex, int endIndex) {
//定义一个临时数组
int[] tempArray = new int [endIndex - startIndex + 1];
//定义左边数组的起始索引
int i = startIndex;
//定义右边数组的起始索引
int j = centerIndex + 1;
//定义临时数组的起始索引
int index = 0;
//放入元素至临时数组
while (i <= centerIndex && j <= endIndex) {
//左边小时,先放左边
if(array[i] <= array[j]){
tempArray[index] = array[i];
i++;
}
//右边小时,先放右边
else{
tempArray[index] = array[j];
j++;
}
index++;
}
//处理剩余元素
while (i <= centerIndex) {
tempArray[index] = array[i];
i++;
index++;
}
while (j <= endIndex) {
tempArray[index] = array[j];
j++;
index++;
}
// 将临时数组的值赋值到原数组中
for (int k = 0; k < tempArray.length; k++)
array[k + startIndex] = tempArray[k];
}