摘要
本文首先介绍了数组的基础知识,然后通过分析LeetCode上的两道问题,LC-704. 二分查找和LC-27. 移除元素,演示了数组在算法中的灵活应用。
1、数组理论基础
1.1 概念
在Java中,数组是一种基本的数据结构,用于存储相同数据类型的元素集合。以下是关于Java中数组的一些重要信息:
-
声明和初始化数组: 你可以通过以下方式声明和初始化数组:
// 声明一个整数数组 int[] intArray; // 初始化数组并分配内存 intArray = new int[5]; // 或者一步完成声明和初始化 int[] intArray = new int[5];这将创建一个包含5个整数的数组。
-
访问数组元素: 你可以使用索引来访问数组中的元素,索引从0开始:
intArray[0] = 10; // 设置数组的第一个元素为10 int element = intArray[1]; // 获取数组的第二个元素 -
数组长度: 使用
length属性可以获取数组的长度,即数组中元素的个数:int length = intArray.length; -
多维数组: Java支持多维数组,例如二维数组,可以看作是数组的数组:
int[][] twoDArray = new int[3][4];这将创建一个3行4列的二维数组。
-
数组的遍历: 你可以使用循环来遍历数组中的元素,如
for或foreach循环。 -
数组的初始化: 你可以在声明数组时初始化它们:
int[] intArray = {1, 2, 3, 4, 5};这将创建一个包含1到5的整数的数组。
-
动态数组: 如果需要一个可变长度的数组,可以使用Java集合框架中的
ArrayList或LinkedList等。 -
数组的限制: Java中的数组有固定的大小,一旦创建,其大小不能更改。这就是为什么有时候使用动态数组更灵活。
-
数组的注意事项: 在使用数组时要注意边界检查,以防止数组越界异常。
总之,Java中的数组是一种常见且有用的数据结构,用于存储和处理数据集合。它们具有固定的大小,因此在使用时需要小心管理数组的大小和索引以避免错误。如果需要更灵活的数据结构,可以考虑使用Java的集合类。
1.2 Arrays类常用API
java.util.Arrays 类提供了一系列用于操作数组的静态方法。以下是一些常用的 Arrays 类中的数组操作方法:
-
sort(T[] a): 对数组进行升序排序。可以用于各种数据类型的数组,例如整数、字符串等。int[] intArray = {5, 2, 8, 1, 3}; Arrays.sort(intArray); // 对 intArray 进行升序排序 -
binarySearch(T[] a, T key): 在排序后的数组中使用二分查找来查找指定元素。如果找到元素,返回其索引;否则返回负数。int[] sortedArray = {1, 2, 3, 4, 5}; int index = Arrays.binarySearch(sortedArray, 3); // 在 sortedArray 中查找 3 的索引,返回 2 -
copyOf(T[] original, int newLength): 复制数组的前newLength个元素,生成一个新的数组。int[] originalArray = {1, 2, 3, 4, 5}; int[] copyArray = Arrays.copyOf(originalArray, 3); // 复制 originalArray 的前 3 个元素 -
toString(T[] a): 将数组转换为字符串表示。int[] intArray = {1, 2, 3, 4, 5}; String arrayString = Arrays.toString(intArray); // 转换为字符串表示,例如 "[1, 2, 3, 4, 5]" -
fill(T[] a, T val): 将数组的所有元素设置为指定的值。int[] intArray = new int[5]; Arrays.fill(intArray, 0); // 将 intArray 的所有元素设置为 0 -
equals(T[] a, T[] b): 比较两个数组是否相等(包含相同的元素并具有相同的顺序)。int[] array1 = {1, 2, 3}; int[] array2 = {1, 2, 3}; boolean areEqual = Arrays.equals(array1, array2); // 检查两个数组是否相等,返回 true
这些方法只是 java.util.Arrays 类中的一部分。根据你的需求,还可以使用其他方法来操作数组。这些方法可以帮助简化数组的处理和操作,提高编码效率。
1.3 数组和List互转
在Java中,可以通过不同的方法进行数组和List之间的转换。
数组转List:
-
使用
Arrays.asList(T... a)方法,它接受一个变长参数,可以将数组转换为List。String[] array = {"apple", "banana", "cherry"}; List<String> list = Arrays.asList(array); -
使用Java 8+的Streams,将数组转换为Stream,然后使用
Collectors.toList()将其收集为List。String[] array = {"apple", "banana", "cherry"}; List<String> list = Arrays.stream(array).collect(Collectors.toList());
List转数组:
-
使用
toArray(T[] a)方法,可以将List转换为数组。List<String> list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry"); String[] array = list.toArray(new String[0]); -
使用Java 8+的Streams,将List转换为Stream,然后使用
toArray()将其转换为数组。List<String> list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry"); String[] array = list.stream().toArray(String[]::new);
需要注意的是,使用toArray(T[] a)方法时,如果传入的数组长度小于List的大小,会创建一个新的数组并返回。如果传入的数组长度大于等于List的大小,将会使用传入的数组进行存储。上述代码中的new String[0]或String[]::new是创建一个新数组的示例。
另外,需要注意的是,Arrays.asList()返回的List是一个固定大小的List,不支持添加或删除操作。如果需要对List进行修改操作,可以将其转换为new ArrayList<>(Arrays.asList(array)),这样就可以使用add和remove等方法了。
2、LC-704. 二分查找
2.1 左闭右闭
思路
双指针,left、right 分别指向头尾节点并逐步向中间移动,直到找到目标值或退出循环为止
- 左闭右闭,保持循环不变量,
- 防止溢出,才是常识的体现。
- target大于mid,left = mid + 1,
- target小于mid,right = mid - 1。
代码
// 双指针,left、right 分别指向头尾节点并逐步向中间移动,直到找到目标值或退出循环为止
public static int search(int[] nums, int target) {
int left =0, right = nums.length-1;
while(left <= right) {
int mid = (right - left) / 2 + left;
if(target > nums[mid]) {
left = mid + 1;
} else if(target < nums[mid]) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
2.2 左闭右开
思路
- 当二分查找要开启, 右边界需规划好(right = nums.length)
- 左小于右条件成立(left < right)
- 目标小于中值,右宽松(right = mid)
代码
// 左闭右开解法
public static int search2(int[] nums, int target) {
int left =0, right = nums.length;
while(left < right) {
int mid = (right - left) / 2 + left;
if(target > nums[mid]) {
left = mid + 1;
} else if(target < nums[mid]) {
right = mid;
} else {
return mid;
}
}
return -1;
}
3、LC-27. 移除元素
3.1 思路
双指针,newIdx 代表新数组的元素的下标,oldIdx 代表旧数组元素的下标
- 双指针擅舞动,newIdx和oldIdx,来开路
- 删除元素,oldIdx前进
- 正常元素,新数组填充,newIdx、oldIdx齐步走
3.2 代码
// 双指针,newIdx 代表新数组的元素的下标,oldIdx 代表旧数组元素的下标
public static int removeElement(int[] nums, int val) {
int newIdx = 0;
for (int oldIdx = 0; oldIdx < nums.length; oldIdx++) {
if (nums[oldIdx] == val) {
continue;
}
nums[newIdx] = nums[oldIdx];
newIdx++;
}
return newIdx;
}