1.一维数组的基本操作
一维数组是最基本的数据结构,常规的数组面试题目也很容易知道怎么做。主要挑战在于边界的处理。在leetcode中数组的题目有360多道,但是数组是大量高级算法的载体,难度差异很大,所以这里我们先梳理常规的题目,难的后面相应章节再讨论。
数组的基本操作是创建+增删改查,其实任何数据结构的基本操作都是这5个,将其搞清楚就掌握一半了。
在面试中,数组大部分情况下都是int类型的,所以我们就用int类型来实现这些基本功能。
1.1 创建和初始化
创建一维数组的方法如下:
int[] arr = new int[10];
这里有个点需要留意,面试时面试官会给若干示例,让你运行并且都要测试通过,例如给你两个数组[0,1,2,3,5,6,8] => [“0->3”,”5->6”,”8”] [1,4,5,6,7,9,10] => [“1”,”4->7”,”9->10”],要求都能跑过。那这时候该如何初始化呢?一种方式是用循环:
for(int i = 0 ; i < arr.length ; i ++)
arr[i] = i;
但是这里明显不是连续的呀,是否有点慌了呢?可以这么做:
int[] arr = new int[]{0,1,2,3,5,6,8};
如果要测试第二组,直接将其赋值到后面就行了。这种初始化方式很简单,在面试时特别实用,但是面试时可能慌了或者忘了,老写不对,这无形中就丢分了。
1.2 查找一个元素
查找是最基本的操作了。查找是数据结构和算法的核心问题,很多复杂的策略都是为了提高查找效率的,例如二分查找、二叉树、红黑树、B+树、Hash和堆等等。
另一方面很多算法问题本质上都是查找问题,例如leetcode里出现频率很高的nSum问题、滑动窗口问题和动态规划问题等很多都是在寻找最佳的那个数。
查找的实现策略也很多,有些还需要更复杂的策略,例如先排序再查找等等。这里先看两个简单的情况:根据索引查找和根据元素线性查找。复杂的情况我们在后面章节再梳理。
根据索引位置查找的实现为:
/**
* @param arr
* @param index 要查找的位置
* @return
*/
public static int findByIndex(int[] arr,int size, int index) {
if (index < 0 || index > size-1)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index < size.");
return arr[index];
}
更多的时候是需要查找目标元素所在的位置:
/**
* @param arr
* @param size 已经存放的元素容量
* @param key 待查找的元素
* @return
*/
public static int findByElement(int[] arr, int size, int key) {
for (int i = 0; i < size; i++) {
if (arr[i] == key)
return i;
}
return -1;
}
测试:
public static void main(String[] args) {
int[] arr = new int[10];
int size = 10;
for (int i = 0; i < size; i++)
arr[i] = i;
int a = findByIndex(arr, 5);
System.out.println("5号位置的元素为:" + a);
int b = findByElement(arr, 10, 7);
System.out.println("元素7所在的位置为:" + b);
}
1.3 增加一个元素
增加一个元素也有两种常见的情况:在指定位置添加,或者给出一个有序数组和一个元素在正确的位置添加。
如果是给出索引的,由于在数组中添加元素大部分情况下需要移动元素,所以干脆就从后向前一边遍历,一边移动,找到对应位置之后直接赋值就行了。
另外一定要检测是否越界了,这个是数组的基本功,如果考虑不周就直接玩完了。
(1)在指定位置添加元素的实现
这里需要知道数组的引用,数组中已存放元素数量,插入位置和待插入的元素四个参数。有些材料会自定义一个对象来将多个入参封装在一起,但是为了不给面试制造障碍,我们就用最基本的方式全部手写。
/**
* @param arr 数组
* @param size 已经存放元素的数量
* @param index 插入的位置
* @param key 插入的元素
*/
public static void add(int[] arr, int size, int index, int key) {
if (size >= arr.length) {
throw new IllegalArgumentException("Add failed. array is full.");
}
if (index < 0 || index > arr.length)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index < arr.length.");
for (int i = size - 1; i >= index; i--)
arr[i + 1] = arr[i];
arr[index] = key;
size++;
}
(2)将给定的元素插入到有序数组的对应位置中
由于数组的特性,我们少不了查找和移动元素,这就有两种处理方式:一种是先查找位置,再移动数据并插入。另一种是从后向前一边移动一边对比查找,找到位置直接插入。从效率上看后者是要好一些。我们这里手写前一种,后一种作为练习请读者自行实现。
/**
* @param arr
* @param size 数组已经存储的元素数量
* @param element 待插入的元素
* @return
*/
public static int addByElementSequence(int[] arr, int size, int element) {
if (size == arr.length)
throw new IllegalArgumentException("Add failed. Array is full.");
int index = size - 1;
//找到新元素的插入位置
for (int i = 0; i < size; i++) {
if (element < arr[i]) {
index = i;
break;
}
}
//元素后移
for (int j = size; j > index; j--) {
arr[j] = arr[j - 1]; //index下标开始的元素后移一个位置
}
arr[index] = element;//插入数据
return index;
}
测试:插入元素-9
public static void main(String[] args) {
int[] arr = new int[10];
int size = 9;
for (int i = 0; i < size; i++)
arr[i] = i;
add(arr, size, -9);
for (int i = 0; i < 10; i++){
System.out.print(arr[i] + " ");
}
}
输出结果为:-9 0 1 2 3 4 5 6 7 8
1.4 删除一个元素
删除同样存在根据索引位置直接删或者先查找元素位置再删除两种情况。
(1)根据索引位置
/**
* @param arr
* @param size 数组元素数量
* @param index 删除位置
* @return
*/
public static int remove(int[] arr, int size, int index) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");
int ret = arr[index];
for (int i = index + 1; i < size; i++)
arr[i - 1] = arr[i];
size--;
return ret;
}
测试代码:
public static void main(String[] args) {
int[] arr = new int[10];
int size = 9;
for (int i = 0; i < size; i++)
arr[i] = i;
remove(arr, 10, 3);
for (int i = 0; i < 10; i++)
System.out.print(arr[i] + " ");
}
输出结果为:0 1 2 4 5 6 7 8 9
(2)先查找元素再删除元素。
这里需要注意的是不能一边从后向前移动一边查找,因为元素可能不存在。所以要分为两个步骤,先查是否存在元素,存在再删除。可以直接复用前面的代码:
/**
* 从数组中删除元素e
*
* @param arr
* @param size 数组
* @param key
*/
public static void removeElement(int[] arr, int size, int key) {
int index = findByElement(arr, size, key);
if (index != -1)
remove(arr, size, index);
}
但是考虑到我们面试时一般只会直接写一个方法,所以我们合并在一起可以这么写:
/**
* 从数组中删除元素e
* @param arr
* @param size 数组已有元素数量
* @param key
*/
public static void removeElement(int[] arr, int size, int key) {
int index = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == key) {
index = i;
break;
}
}
if (index != -1) {
for (int i = index + 1; i < size; i++)
arr[i - 1] = arr[i];
size--;
}
}
测试方法:
public static void main(String[] args) {
int[] arr = new int[10];
int size = 10;
for (int i = 0; i < size; i++)
arr[i] = i;
removeElement(arr,10,4);
for (int i = 0; i < 9; i++)
System.out.print(arr[i] + " ");
}
输出结果为:0 1 2 3 5 6 7 8 9