数组基础
1、初识数组
1.1 数组的基本概念
数组对应的英文是array,是有限个相同类型的变量所组成的有序集合,数组中的每一个变量被称为元素。数组是最简单、最常用的数据结构。
以整数数组为例,数组的存储形式如下:
数组中的每一个元素都有自己的下标(或者叫索引),只不过下标从0开始,一直到数组长度-1。
数组中的元素在内存中是连续存储的,且每个元素占用相同大小的内存。
在上图中,橙色的格子代表空闲的存储单元,灰色的格子代表已占用的存储单元,而红色的连续格子代表数组在内存中的位置。
不同类型的数组,每个元素所占的字节个数不同,本图只作简单示例。
1.2 数组存储元素的特征
在创建了一个大小为10的数组后,由于不同语言实现数组的方式是不一样的,所以在每种语言里面数组的初始值都不一样。例如在c语言中,每个位置都是一个随机数;在java中,默认初始化为0。
数组的初始化本质是覆盖数组已有的值,可以只初始化一部分,用你需要的值来去覆盖原来的0,比如可以将{0,0,0,0,0,0,0,0,0,0}初始化为{1,2,3,4,5,0,0,0,0,0}。如果此时需要知道有效元素的个数,就必须再使用一个额外的变量size来记录。
但是初始化之间的元素不可以空着,初始化必须从前向后的连续空间初始化,不可以出现空缺的情况。如果出现空缺是违背数组的原则的。正在进行某种运算期间可以先给部分位置赋值,而一旦稳定了,就不可以再出现空位置的情况。
如果需要的数据是数组中的某一段,需要使用两个变量left和right来表示,例如{0,0,3,4,5,6,7,0,0,0},需要使用left=2,right=6来表示区间[left,right]是有效的。
在删除数组中的某个值之后,原来位置仍然是原先的值,比如{1,2,3,4,5,6,7,8,0,0},删除4之后,数组会变为{1,2,3,5,6,7,8,8,0,0},此时表示元素数量的size会减1变为7,原来8的位置仍然是8。因为是通过size来标记元素数量的,所以最后一个8不会被访问到。
2、数组基本操作
数组一般是int类型,下面都以int数组为例。
2.1 数组的创建和初始化
java中创建数组:
int[] arr = new int[10];
初始化数组:
int[] arr = new int[]{0,1,2,3,4,5,6,7};
//或者
int[] nums = {2,5,0,1,3,7};
第二种初始化的方法是最简单最常用的。
2.2 查找一个元素
由于数组在内存中是顺序存储,所以只需要给出数组下标,就可以读取到对应的数组元素。
假设有一个名称为array的数组,要读取下标为3的元素,就写作array[3];读取下标为5的元素,就写作array[5]。需要注意的是,输入的下标必须在数组的长度范围之内,否则会出现数组越界。
像这种根据下标随机读取元素的方式叫随机读取。
还有一种是根据值进行查找,返回数组下标,这里给出最基本的线性查找,基本实现如下:
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;
}
2.3 增加一个元素
将给定的元素插入到有序数组的对应位置中,可以先找位置,再将其后元素整体右移,最后插入到空位置上。具体实现如下:
public static int addByElementSequence(int[] arr, int size, int element) {
//如果数组已满,则插入失败
if(size >= arr.length){
return -1;
}
//找到新元素的插入位置
//index初始化为size是为了处理插入到数组末尾的情况
int index = size;
for(int i = 0; i < size; i++){
if(element < arr[i]){
index = i;
break;
}
}
//元素后移
for(int i = size; i > index; i--){
arr[i] = arr[i-1];//从index下标开始的元素后移一个位置
}
arr[index] = element; //插入元素
return index;
}
除了上述的插入操作外,还有按照指定位置插入元素。
实现代码如下:
public static int insert(int[] arr, int size, int element, int index) {
//判断访问下标是否超出范围
if(index < 0 || index > size){
System.out.println("超出数组实际元素范围");
}
//从右向左循环,将元素逐个右移
for(int i = size; i > index; i--){
arr[i] = arr[i-1];
}
//将腾出的位置放入新元素
array[index] = element;
size++;
return size;
}
2.4 删除一个元素
删除元素和插入元素的操作相反,如果删除的元素位于数组中间,其后的元素都需要向前挪动1位。
实现代码如下:
public static int 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--;
}
return size;
}
3、总结
数组和链表都属于线性表,数组是物理上连续,逻辑上也连续,而链表是逻辑上连续,物理上不连续。
与链表的增删查操作相比,数组的增删操作会更复杂一点,因为在增删操作之后,仍然要保证数组元素在物理存储上的连续性。而数组的查询操作,因为在存储时已经带有下标索引,所以以下标来查询元素的时候比链表查询元素更加简单直接,不过根据元素来查找下标仍需要线性查找或一些例如二分查找等其他查找方法。