数组应该是每个学习编程的人第一个会接触到的数据结构,但是其并没有大多数人想象的那么简单,数组算法需要花很多精力去处理边界问题,通常算法写出来了,但是执行起来总是出问题。
数组作为最基础,最常用的数据结构,确实需要我们多花时间来掌握,所以本节我们讲讲数组最基础的部分,即增删改查如何实现。
增加一个元素
增加和删除元素是数组最基本的操作,看别人的代码非常容易,但是自己写的时候经常bug满天飞。能准确处理游标和边界等情况是数组算法题最基础最重要的问题之一。
不要感觉挺简单就不写,其中涉及的问题在所有与数组有关的算法题中都会遇到。
数组增加元素没有链表那么简单,直接插入即可,而是需要将待插入元素后面的数组元素全部后移,再放入待插入的元素。
方法1(推荐使用,比较好理解)
要将给定元素插入到有序数组的对应位置中,我们可以先找位置,再将其后的元素整体右移,最后插入到空位置上。
这里必须注意的是,要保证算法在数组首部、尾部和中间部分都能运行成功。
/**
* @param arr
* @param size 数组已经存储的元素数量
* @param element 待插入的元素
* @return
*/
public static int addByElementSequence1(int[] arr, int size, int element) {
//问题①:是否应该是size>arr.length
if (size >= arr.length) {
throw new IllegalArgumentException("Add fail, the Arr is full");
}
//问题②想想这里是否是index=0或者size-1?
int index = size;
//找到新元素的插入位置,问题③ 这里是否应该是size-1?
for (int i = 0; i < size; i++) {
if (element < arr[i]) {
index = i;
break;
}
}
//元素后移,问题④想想这里为什么不是size-1
for (int j = size; j > index; j--) {
arr[j] = arr[j - 1];//index下标开始的元素后移一个位置
}
arr[index] = element;//插入数据
return index;
}
}
以上各问题的个人理解:
1. size应该小于arr.length,原因是要保证数组中至少有一个未初始化的空位
2. 先默认插入数组尾部,由于原下标到size-1,添加元素后最大下标就为size
3. 遍历查找新元素插入位置,所以判断范围为原数组的元素,则取size
4. 元素后移,初始化j,应该为添加元素后的数组最大下标size
方法2 (一次遍历,效率较高)
我们还可以一开始就从后向前一边移动一边对比查找,找到位置直接插入。
具体实现如下:
/**
* @param arr
* @param size 数组已经存储的元素数量
* @param element 待插入的元素
* @return
*/
public static int addByElementSequence2(int[] arr, int size, int element){
if (size >= arr.length){
throw new IllegalArgumentException("Add fail, the Arr is full");
}
int index = 0;
//从后往前进行遍历,由于element也可能插入数组末尾,因此i初始化为size
for (int i = size; i > 0; i--) {
if (element >= arr[i - 1] ){
//与其前一个元素进行比较,如果>=前一个元素,则element插入该元素位置
arr[i] = element;
index = i;
break;
}
//若element不在该元素位置,将对应数组元素后移
arr[i] = arr[i-1];
//i等于1时,说明element比全部元素都小,应置于数组首部
if (i == 1){
arr[i-1] = element;
}
}
return index;
}
删除一个元素
对于删除,不能一边从后向前移动一边查找了,因为元素可能不存在。所以要分为两个步骤,先查是否存在元素,存在再删除。 这个方法和增加元素一样,必须自己亲自写才有作用,该方法同样要求删除序列最前、中间、最后和不存在的元素都能有效,下面给一个参考实现:
/**
* 遍历数组,如果发现目标元素,则将其删除,
* 数组的删除就是从目标元素开始,用后续元素依次覆盖前继元素
*
* @param arr 数组
* @param size 数组中的元素个数
* @param key 要删除的目标值
*/
public static int removeByElement(int[] arr, int size, int key) {
int index = -1;
//遍历查找key位置,用index保存其索引下标
for (int i = 0; i < size; i++) {
if (arr[i] == key) {
index = i;
break;
}
}
//index不为-1时,则说明key存在
if (index != -1) {
遍历数组,将下标为index的元素后面的的数组元素全部前移
for (int i = index; i < size - 1; i++) {
arr[i] = arr[i + 1];
}
//取得删除后的size值
size--;
}
return size;
}
}
修改、查找一个元素
数组的改查比较简单,只需通过基本的遍历就能实现,因此不过多进行分析,看下代码大概就能够理解了。
/** 修改元素
* @param arr
* @param size 已经存放的元素容量
* @param key 待查找的元素
* @param new 待替换的元素
* @return
*/
public static int changeByElement(int[] arr, int size, int key, int new){
int index = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == key){
arr[i] = new;
index = i;
break;
}
}
return 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;
}