1、什么是数组:
本质上让我们可以”批量“创建相同类型的变量
也是一种数据结构–顺序表
【注】数据结构即把一堆数据保存起来的结构,关心的是如何高效的读写数据,
2、数组的创建与初始化:
①数组的动态初始化
语法:数据类型[] 数组名称 = new 数组类型[]{}
数据类型[] 数组名称 = new 数组类型[number]
//eg
int[] arr = new int[]{1,2,3,4,5};
int[] arr = new int[5];
//创建数组时,若没有使用{}来初始化每个元素值,那么每个元素都是该数据类型的默认值
②数组的静态初始化
语法: 数据类型[] 数组名称 = {初始化数据};
【注】静态初始化时Java的语法糖,javac编译之后,就是动态初始化
语法糖:只存在编译期之前
//eg
int[] arr = {1,2,3,4,5};
3、数组的使用
3.1 获取一个数组的长度,使用数组名称.length
3.2 访问数组元素,使用数组名称[元素的索引],数组的索引从0开始,数组名.lengtg-1 结束。
【注】注意数组越界
4、数组的遍历
①for循环
②增强型for循环
public class Array{
public static void main (String[] args){
int[] arr1 = new int[]{1,2,3,4,5};
for(int i= 0;i < arr1.length;i++){
System.out.println(arr[i]+" ");
}
//增强型for循环
//此处的i指从数组的第一个元素开始取值,第一次把第一个元素复制一份给i,第二次循环把第二个元素的值复制一份给i,以此类推,直到把整个数组遍历结束。
//只能读取数组的元素值,无法修改,i是每个元素值的拷贝,并不是实实在在的数组元素。
for(int i : arr1){
System.out.println(i +" ");
}
}
}
//输出结果:1 2 3 4 5
5、数组和方法之间的关系:
①数组作为方法的参数
创建一个方法接受一个任意的整型数组并打印
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
printArr(arr);
}
private static void printArr(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
}
②关于引用数据类型的理解问题
实参:位于主方法的临时变量;形参:位于方法中的临时变量。
栈
方法的调用就是在栈区进行的,每个方法的调用过程,就是一个栈帧的入栈以及出栈过程,是一种先进后出的结构(LIFO),方法的局部变量和形参都在其中存储。当方法调用结束出栈时,临时变量都是被销毁。
堆:
所有对象都在堆中存储,数组对象,类的实例化对象,接口的对象。
引用就是起了个”别名“。数组的引用实际上就是保存数组的首元素地址。
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2};
printArr(arr);
System.out.println(arr[0] + " "+arr[1]);
}
private static void printArr(int[] arr) {
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
}
内存图:
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2};
printArr(arr);
System.out.println(arr[0] + " "+arr[1]);
}
private static void printArr(int[] arr) {
arr = new int[]{10,20};
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
}
内存图:
数组对象:
实实在在在堆中存储,所有new出来的都在堆中
数组的引用:
给这块数组起了个名字,保存这个数组对象的首地址。
数组练习:
1、数组对象转为字符串对象
Arrays.toString()方法
【注】dk中某些类后+s,这些是工具类,提供了大量有用的方法,直接调用即可。
import java.util.Arrays;
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3};
//JDK内置工具类
String str = Arrays.toString(arr);
System.out.println(str);
}
}
//输出结果
//[1, 2, 3]
2、数组的复制
①Arrays.copyOf()方法
import java.util.Arrays;
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3};
//新数组长度小于原数组
int[] arr1 = Arrays.copyOf(arr, 2);
//新数组长度等于原数组
int[] arr2 = Arrays.copyOf(arr, 3);
//新数组长度大于原数组
int[] arr3 = Arrays.copyOf(arr, 4);
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
System.out.println(Arrays.toString(arr3));
}
}
//输出结果
//[1, 2]
//[1, 2]
//[1, 2, 3, 0]
②Arrays.copyOfRange()方法
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6};
//Arrays.copyOfRange方法复制从索引2到索引5的数组元素值
int[] arr1 = Arrays.copyOfRange(arr, 2, 5);
System.out.println(Arrays.toString(arr1));
}
}
//输出结果
//[3, 4, 5]
3、给定一个数组找出这个数组的最大值
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6};
//假定数组第一个元素值为最大值,遍历比大小。
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
System.out.println("数组最大值为:" + max);
}
}
//输出结果
//数组最大值为:6
4、二分查找(仅升序或者降序)
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
System.out.println(binarySearch(arr,4));
System.out.println(binarySearch(arr,8));
System.out.println(binarySearch(arr,11));
}
//找到返回索引没找到返回-1;
public static int binarySearch(int[] arr, int toFind) {
//开始位置
int left = 0;
//结束位置
int rigth = arr.length - 1;
//终止循环条件:left>right;
while (left <= rigth) {
int mid = (left + rigth) / 2;
if (toFind < arr[mid]) {
rigth = mid - 1;
} else if (toFind > arr[mid]) {
left = mid + 1;
} else {
//arr[mid] = toFind;
System.out.print("找到该元素!");
return mid;
}
}
System.out.println("没有找到!");
return -1;
}
}
//输出结果
//找到该元素!3
//找到该元素!7
//没有找到!
//-1
递归写法:
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(binarySearch(arr,3,0, arr.length-1));
System.out.println(binarySearch(arr,6,0, arr.length-1));
System.out.println(binarySearch(arr,11,0, arr.length-1));
}
public static int binarySearch(int[] arr, int toFind, int left, int right) {
if (left > right) {
return -1;
}
//还有元素,我只知道中间元素
int mid = (left + right) / 2;
if (arr[mid] == toFind) {
return mid;
} else if (toFind < arr[mid]) {
return binarySearch(arr, toFind, left, mid - 1);
}
return binarySearch(arr, toFind, mid + 1, right);
}
//输出结果
//2
//5
//-1
【注】当面对乱序时,可先将数组排序,后使用二分查找某元素,效率较遍历仍然很快。
5、判断数组是否是一个顺序数组
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 4};
System.out.println(isSorteArray(arr));
}
public static boolean isSorteArray(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
}
//输出结果
//false
6、数组排序
①冒泡排序
核心思想:假设现在数组有n个元素,每遍历一次就将数组中最大值放在数组末尾,每进行一次遍历,就有一个元素到达了最终位置。
将整个数组分为两个子数组:
1、待排序的数组[0…n]
2、已经排好序的数组[]
每进行一次遍历,待排序的数组元素-1已排序好的数组元素+1。
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 3, 2, 5, 6, 9, 7, 8, 4};
System.out.println(Arrays.toString(bubbleSorting(arr)));
}
public static int[] bubbleSorting(int[] arr) {
int[] arr1 = null;
//表示遍历的次数,每进行一次就有一个较大值到达位置
for (int i = 0; i < arr.length; i++) {
//待排序的数组[0--i-1],已排序[]
//j+1<arr.length
//-i表示已经排列好的元素个数
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
//交换数据
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
}
//输出结果
//[1, 2, 3, 4, 5, 6, 7, 8, 9]
优化:
①最后一个元素已经有序不需要排序
②若本来就是升序就不需要排序
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7};
System.out.println(Arrays.toString(bubbleSorting(arr)));
}
public static int[] bubbleSorting(int[] arr) {
//表示遍历的次数,每进行一次就有一个较大值到达位置
//i<arr.length-1;表示剩最后一个元素就已经有序,不需要遍历
for (int i = 0; i < arr.length - 1; i++) {
boolean isWsaped = false;
//待排序的数组[0--i-1],已排序[]
//j+1<arr.length
//-i表示已经排列好的元素个数
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
isWsaped = true;
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!isWsaped) {
//内存循环没有元素交换,整个数组已经有序,提前退出循环
break;
}
}
return arr;
}
}
//输出结果
//[1, 2, 3, 4, 5, 6, 7]
7、升序或者降序的数组输出逆序
import java.util.Arrays;
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8};
reverse(arr);
}
public static void reverse(int[] arr) {
int i = 0;
int j = arr.length - 1;
while (i < j) {
int temp = 0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
System.out.println(Arrays.toString(arr));
}
}
//输出结果
//[8, 7, 6, 5, 4, 3, 2, 1]
8、数组的数字排列问题
题目:给定一个整型数组,将所有的偶数放在前半部分,将所有的奇数放在数组的后半部分
例{1,2,3,4,5,6}输出{6,2,4,3,5,1}
思路:
分别定义i,j从前从后找奇数和偶数,找到第一个奇数和第一个偶数,交换他们的值。
public class Thinking {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
transform(arr);
}
public static void transform(int[] arr) {
int i = 0;
int j = arr.length - 1;
while (i < j) {
//i从前向后找到第一个奇数为止
while (i < j && arr[i] % 2 == 0) {
i++;
}
//此时i一定是落在了第一个奇数的位置
//j从后向前找到第一个偶数位置
while (i < j && arr[j] % 2 != 0) {
j--;
}
//此时j一定是落在了第一个偶数的为位置
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
System.out.println(Arrays.toString(arr));
}
}
//输出结果
//[8, 2, 6, 4, 5, 3, 7, 1, 9]
9、给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标,你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
返回:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
import java.util.Arrays;
public class ClassPractic {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6};
System.out.println(Arrays.toString(twoSum(arr, 6)));
System.out.println(Arrays.toString(twoSum(arr, 9)));
System.out.println(Arrays.toString(twoSum(arr, 5)));
}
//双引用法--双指针
public static int[] twoSum(int[] arr, int target) {
int[] arr1=new int[2];
for (int i = 0; i < arr.length; i++) {
for (int j = arr.length - 1; j > i; j--) {
if (arr[i] + arr[j] == target) {
arr1[0] = i;
arr1[1] = j;
return arr1;
}
}
}
return arr1;
}
}
//输出结果
//[0, 4]
//[0, 4]
//[0, 3]
10、给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
//方法一:双指针、计数器法
public class ClassPractic {
public static void main(String[] args) {
int[] arr = new int[]{2, 2, 1};
int[] arr1 = new int[]{4, 1, 2, 1, 2};
System.out.println("只出现一次的元素是:");
System.out.println(findNum(arr));
System.out.println(findNum(arr1));
}
//给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。
// 找出那个只出现了一次的元素。
public static int findNum(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//定义一个计数器,由题可知只有一个元素出现了一次,那么出现相同的次数就是1
// 只有它符合这个计数,所以当计数出现就返回它的元素值。
int count = 0;
for (int j = 0; j < arr.length; j++) {
if (arr[i] == arr[j]) {
count++;
}
}
if (count == 1) {
return arr[i];
}
}
return -1;
}
}
//输出结果
//只出现一次的元素是:
//1
//4
//方法二:异或:同0异1
public class ClassPractic {
public static void main(String[] args) {
int[] arr = new int[]{2, 2, 1};
int[] arr1 = new int[]{4, 1, 2, 1, 2};
System.out.println("只出现一次的元素是:");
System.out.println(findNum(arr));
System.out.println(findNum(arr1));
}
//给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。
// 找出那个只出现了一次的元素。
public static int findNum(int[] arr) {
int num = 0;
for (int i : arr) {
num ^= i;
}
return num;
}
}
//输出结果
//只出现一次的元素是:
//1
//4
11、【注】:向下取整的运算称为Floor,用数学符号⌊⌋表示;向上取整的运算称为Ceiling,用数学符号⌈⌉表示。
//方法一:双指针计数器
public class ClassPractic {
public static void main(String[] args) {
int[] arr = new int[]{3, 2, 3};
int[] arr1 = new int[]{2, 2, 1, 1, 1, 2, 2};
System.out.println(majorityElement(arr));
System.out.println(majorityElement(arr1));
}
public static int majorityElement(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//设置一个计数器,由题目可只,当一个数出现次数大于n/2时它是多数元素
//此时返回该元素值
int count = 0;
for (int j = 0; j < arr.length; j++) {
if (arr[i] == arr[j]) {
count++;
}
}
if (count > arr.length / 2) {
return arr[i];
}
}
return -1;
}
}
//输出结果
//3
//2
//方法二:排序最中间的数必是最多元素。
public class ClassPractic {
public static void main(String[] args) {
int[] arr = new int[]{3, 2, 3};
int[] arr1 = new int[]{2, 2, 1, 1, 1, 2, 2};
System.out.println(majorityElement(arr));
System.out.println(majorityElement(arr1));
}
public static int majorityElement(int[] arr) {
//排序
Arrays.sort(arr);
//位运算。 >>右移,同意于除2
return arr[arr.length >> 1];
}
}
//输出结果
//3
//2
//摩尔投票法
//假设第一个元素为第一个候选人,相同则投赞成票,不同则投反对票,
//赞成票+1反对票-1,当票数为0时,换候选人为当前投票人。
public class ClassPractic {
public static void main(String[] args) {
int[] arr = new int[]{3, 2, 3};
int[] arr1 = new int[]{2, 2, 1, 1, 1, 2, 2};
System.out.println(majorityElement(arr));
System.out.println(majorityElement(arr1));
}
public static int majorityElement(int[] arr) {
int num = arr[0];
//票数
int count = 1;
for (int i = 1; i < arr.length; i++) {
if (num == arr[i]) {
count++;
} else {
count--;
if (count == 0) {
num = arr[i];
count =1;
}
}
}
return num;
}
}
//输出结果
//3
//2
若在一组数中,至多选择m个元素,则该元素的得票数 >⌊ n/(m+1) ⌋
核心步骤:
1、选侯选人,票数为0更换候选人,直到整个数组扫描完毕
2、统计候选人的票数,看他是否满足 >⌊ n/(m+1) ⌋
摩尔投票法之选两个人
二维数组:
1、语法:
数据类型[][] 数组名称 = new 数据类型[行数][列数]{可选的初始化数据};
public class Thinking {
public static void main(String[] args) {
int[][] arr = new int[][]{
{1, 2, 3},
{2, 3, 4},
};
//行
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 2 3
//2 3 4