线性表 - 数组
1.1 数组的介绍
-
数组(Array)是有限个相同类型的变量所组成的有序集合,数组中的每一个变量被称为元素。数组是最为简单、最为常用的数据结构
-
数组下标从零开始(Why)
-
存储原理
- 数组用一组连续的内存空间来存储一组具有相同类型的数据
- 灰色格子被使用的内存
- 橙色格子空闲的内存
- 红色格子数组占用的内存
- 数组用一组连续的内存空间来存储一组具有相同类型的数据
-
数组可以根据下标随机访问数据
-
假设首地址是:1000,int是4字节(32位),实际内存存储是位,随机元素寻址
-
a[i]_address=a[0]_address+i*4
-
该公式解释了三个方面
- 连续性分配
- 相同的类型
- 下标从0开始(从 0 开始计算元素下标根据公式不需要偏移量,这也是数组下标从 0 开始的原因)
-
-
1.2 自己实现一个数组,并具有基本的操作方法
- 创建默认大小的数组,或者是创建指定大小的数组
- 对数组的元素进行增删改查的操作,其中增加支持顺序插入,指定索引的位置插入两种(需要向后移动元素)
/**
* @author 云梦归遥
* @date 2022/5/12 10:09
* @description
*/
public class MyArray {
private int[] array; //构建的数组对象
private static final int ARRAY_LENGTH = 8; // 定义无参构造默认的数组长度
private int length = 0; // 记录数组有效长度
// 数组构造方法
public MyArray(){// 默认构造方法数组长度为 8
this.array = new int[ARRAY_LENGTH];
}
public MyArray(int len){// 有参构造则依靠传入的整型数字来构造数组
this.array = new int[len];
}
// 增加数据
// 默认插入是顺序插入
public boolean insert(int num){
if (length < array.length){
array[length++] = num; // 插入到下一个位置
return true;
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
// 还可以指定索引位置插入
public boolean insert(int num, int index){
if (length < array.length){
int chance = 0; // 记录是否成功实现数组中元素的移动,从而实现挪出空位
for (int i = index + 1; i < array.length - 1; i++){
if (array[i] == 0){
for (int j = i; j > index; j--){
array[j] = array[j - 1];
}
chance++;
break;
}
}
if (chance > 0){
// 说明挪动位置成功
array[index] = num; // 插入到指定位置
++length;
return true;
} else {
// 说明挪动位置失败,即后面没有空格出现,插入失败
return false;
}
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
// 删除
public boolean delete(int index){
if (index < array.length && index >= 0){
array[index] = 0; // 数组初始化后默认数值就是 0
--length; // 有效长度 - 1
return true;
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
// 修改
public boolean update(int num, int index){
if (index < array.length && index >= 0){
array[index] = num; // 更新指定索引的数据
return true;
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
// 查询
public int select(int index){
if (index < array.length && index >= 0){
return array[index];
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
// 数组的扩容(数组长度不够,需要进行扩容)
public boolean resize(){
try {
// 将目前的数组进行 2 被放大
int[] newArray = new int[array.length * 2];
// 将原数组中的数据复制到新的数组中
System.arraycopy(array, 0, newArray, 0, array.length);
array = newArray;
return true;
} catch (Exception e){
e.printStackTrace();
}
return false;
}
@Override
public String toString() {
return "MyArray{" +
"array=" + Arrays.toString(array) +
'}';
}
}
1.3 进行简单的测试
package com.lagou.test;
import com.lagou.entity.MyArray;
/**
* @author 云梦归遥
* @date 2022/5/12 10:45
* @description
*/
public class MyArrayTest {
public static void main(String[] args) {
MyArray myArray = new MyArray(); // 默认数组,长度为 8
myArray.insert(1);
myArray.insert(2);
myArray.insert(3);
myArray.insert(4, 1);
System.out.println(myArray.toString());
myArray.update(5, 3);
myArray.delete(0);
System.out.println(myArray.toString());
}
}
测试结果:
1.4 总结
-
时间复杂度
- 读取和更新都是随机访问,所以是O(1)
- 插入数组扩容的时间复杂度是O(n),插入并移动元素的时间复杂度也是O(n),综合起来插入操作的时间复杂度是O(n)
- 删除操作,只涉及元素的移动,时间复杂度也是O(n)
-
优缺点
- 优点:
- 数组拥有非常高效的随机访问能力,只要给出下标,就可以用常量时间找到对应元素
- 缺点:
- 插入和删除元素方面。由于数组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫移动,影响效率。 (ArrayList(数组实现,适合查询,更新) LinkedList(链表实现,适合插入,删除) )
- 申请的空间必须是连续的,也就是说即使有空间也可能因为没有足够的连续空间而创建失败
- 如果超出范围,需要重新申请内存进行存储,原空间就浪费了
- 优点:
-
应用
- 数组是基础的数据结构,应用太广泛了,ArrayList、Redis、消息队列等等