数据结构与算法入门-----数组
学习目标
为什么数组从0开始编写
数组如何实现随机访问
概念
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
核心概念理解
线性表:
数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。(链表、队列、栈等也是线性表结构)
非线性表,数据之间并不是简单的前后关系,如二叉树、堆、图等。
连续的内存空间和相同类型的数据:
连续则说明这段空间是不可间断的,相同类型则说明所存储的数据要是类型一致的,不能有其他类型放在同一数组里面
特征
随机访问。正是因为以上的两个特性,数组有了随机访问这个特征。,但对数组进行删除插入,为了保证数组的连续性,就要做大量的数据搬移工作
问题的解决
通过下标随机访问过程
我们拿一个长度为10的int类型的数组int [] a =new int[10]来举例。
如图所示,计算机给数据a[10],分配了一块连续内存空间1000~1039,其中,内存块的首地址为base_address=1000。
计算机会给每一个内存单元分配一个地址,计算机通过地址来访问内存中的数据。
当计算机需要随机访问数组中的某个元素时,它会首先通过下面的寻址公式,计算出该元素存储的内存地址:
a[i]_address = base_address + i * data_type_size
其中,data_type_size表示数组中每个元素的大小。如果数组中存储的是int类型数据,所以data_type_size的大小就为4个字节。
从0开始的原因:
1、减少CPU运算负担,少了一次减法运算指令
如:
从0开始内存地址计算方式:
a[k]_address = base_address + k * type_size
从1开始内存地址计算方式:
a[k]_address = base_address + (k-1)*type_size
2、历史原因
常见的方法
增删改查
插入操作(根据索引)
大致流程:找到位置→移动元素→插入
代码如下:
public boolean insert(int index, int value){
// 数组空间已满
if (count == n) {
System.out.println("没有可插入的位置");
return false;
}
// 如果count还没满,那么就可以插入数据到数组中
// 位置不合法
if (index < 0||index > count ) {
System.out.println("位置不合法");
return false;
}
// 位置合法
//移动元素
for( int i = count; i > index; --i){
data[i] = data[i - 1];
}
//插入值
data[index] = value;
//空间++
++count;
return true;
}
时间复杂度分析:
1、在数组末尾处添加,不需要移动,此时时间复杂度为O(1)
2、在数组开头处添加,需要移动,此时时间复杂度为O(n)
3、平均时间复杂度为O(n)
删除操作 (根据索引)
大致流程:找到要删除的元素→移动元素
代码如下
public boolean insert(int index, int value){
// 数组空间已满
if (count == n) {
System.out.println("没有可插入的位置");
return false;
}
// 如果count还没满,那么就可以插入数据到数组中
// 位置不合法
if (index < 0||index > count ) {
System.out.println("位置不合法");
return false;
}
// 位置合法
//移动元素
for( int i = count; i > index; --i){
data[i] = data[i - 1];
}
//插入值
data[index] = value;
//空间++
++count;
return true;
}
时间复杂度分析:
1、在数组末尾处删除,不需要移动,此时时间复杂度为O(1)
2、在数组开头处删除,需要移动,此时时间复杂度为O(n)
3、平均时间复杂度为O(n)
查询操作
大致流程:找到需要查询的下标→显示结果
//根据索引,找到数据中的元素并返回
public int find(int index){
if (index<0 || index>=count) return -1;
return data[index];
}
改进方法:
插入操作(不需要移动元素版本)
核心:替换
具体过程:创建一个新的空间→将原值放置到新空间→新元素插入
如图所示:
删除操作(累计触发)
如上图所示,当我们要依次删除 a,b,c 三个元素。
为了避免d,e,f,g,h这几个数据被移动三次,可以先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,我们再触发执行一次真正的删除操作。
形似JVM,
联系
数组是一个基础的数据结构,在查询方面的优势较为明显,也比较通俗易懂,但是在插入与删除方面,效率较为低下,所以,需要另一种数据结构来解决这个问题。
注意事项
数组越界问题
数组与链表的区别
典型错误:
链表适合插入、删除,时间复杂度O(1);数组适合查找,查找时间复杂度为O(1)。
解析:数组是适合查找操作,但是查找的时间复杂度并不为O(1)。排好序的数组,用二分查找,时间复杂度O(logn)。
正确的表述:
数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。