前言
学习数据结构路途漫漫,没错我又从头开始数据结构的学习了,先复习总结一下顺序表的实现逻辑吧~
目录
1. 顺序表的定义+图例
2. 实现顺序表基本操作
2.1 打印顺序表中各个元素
2.2 是否含有某个元素
2.3 获取有效元素的个数
2.4 查找某个元素对应的位置
2.5 新增元素(默认尾插)
2.6 任意位置插入
2.7 更新对应位置的值
2.8 删除第一次出现值为key的元素
2.9 清空顺序表
3.顺序表优缺点
1. 顺序表的定义+图例
顺序表是在计算机内存中以数组的形式保存的线性表,其元素关系为一对一,也就是说各个元素除了首元素(无前驱,一个后继)和尾元素(无后继,一个前驱)之外,其它元素都只有唯一一个前驱和唯一一个后继,它采用的顺序存储结构(四大存储结构分别为顺序存储,链式存储,索引存储,散列存储),物理存储单元连续。顺序表根据它的空间大小是否确定可以分为静态顺序表(固定的空间大小)和动态顺序表(空间大小可以扩大)。这里我们以动态顺序表为例,存储的元素数据类型为int整型(引用类型会有一定的不同,如果大家想了解的话可以评论区提问)。
图例:
可以出现当我们应该为顺序表定义如下属性:存储数据的数组,当前顺序表中元素个数和顺序表的默认最大容量(用于扩容)。
2. 实现顺序表基本操作
注:在顺序表构造函数记得初始化数组(数组空间大小为默认最大容量)哦~
2.1 打印顺序表中各个元素
这个逻辑很简单,我们直接将用于存储数据的数组进行遍历输出它的每个元素值就好了~
//打印顺序表元素
public void display(){
for (int i = 0; i < size; i++) {
System.out.print(data[i]+" ");
}
}
2.2 是否含有某个元素
我们在遍历数组的同时对数组元素值进行判断是不是我们要查找的元素值,如果是则返回true,反之返回false。
//是否含有某个元素
public boolean contains(int key){
for (int i = 0; i < size; i++) {
if(data[i] == key) return true;
}
return false;
}
2.3 获取有效元素的个数
这个直接返回顺序表当前的元素个数就好啦~
//获取有效元素个数
public int getSize() {
return this.size;
}
2.4 查找某个元素对应的位置
和2.2操作类似,遍历数组的基础上,进行判断是否含有目标元素值,如果有返回对应下标,否则返回-1(找不到)。
//查找某个元素对应位置
public int getIndex(int key){
for (int i = 0; i < size; i++) {
if(data[i] == key) return i;
}
return -1; //找不到
}
2.5 新增元素(默认尾插)
大家可以知道顺序表的当前元素个数等于其最后一个元素的下标+1,当我们对顺序表进行尾插时,我们直接将新增元素放入当前元素个数位置下,让当前元素个数+1就好了。
这样就好了吗?大家试想,如果当前元素个数已和顺序表的默认最大容量相等,那我们还能以当前元素个数下标在存储数据的数组中去新增元素吗?答案肯定是不能的,这样会造成数组越界,所以我们得考虑数组是否已满,我们需不需要去增容的问题。如果数组已满,我们需要为它增容(Arrays.copyOf())后再去新增元素。代码修改如下:
//新增元素(尾插)
public void addLast(int value){
if(isFull()){
//增容
Expansion();
}
data[size] = value;
size++;
}
//扩容
private void Expansion() {
data = Arrays.copyOf(data,2*size); //扩大两倍
}
//判断存储数据的数组是否已满
public boolean isFull(){
return size == maxCapacity;
}
2.6 任意位置插入
大家知道顺序表的物理空间是连续的,如果我们需要往其中某个位置插入元素的话,是不能直接将对应位置的值直接修改为要新增的元素值的,这样会将该位置原本的元素值替换,如图:
所以为了避免数据的丢失,我们需要将pos到最后一个元素整体往后挪一位,将pos位置腾空,留给需要新增的元素。(一样要进行判断顺序表是否已满哦~)
是不是这样就可以了呢?如果我插入的位置是-1或者超过了当前元素个数呢?这样的位置肯定是不合法的,所以我们还需要对插入位置进行判断是否合法,具体代码如下:
//任意位置插入
public void addToIndex(int pos,int value){
if(!isCorrect(pos)){
System.out.println("插入位置不合法");
return;
}
if(isFull()){
//增容
Expansion();
}
//pos~size整体后移一位
for (int i = size; i >= pos; i--) {
data[i] = data[i-1];
}
//添加
data[pos] = value;
size++;
}
//判断插入元素位置是否合法
public boolean isCorrect(int pos){
return pos >= 0 && pos <= size; //如果插入位置在0~size范围内就合法,反之不合法
}
2.7 更新对应位置的值
直接将对应位置的元素值更新就好了(判断位置合法性)
//更新对应位置的值
public void update(int pos,int value){
//判断位置合法性
if(!isCorrect(pos)){
System.out.println("插入位置不合法");
return;
}
//更新
data[pos] = value;
}
2.8 删除第一次出现值为key的元素
在2.6中我们知道为了防止对应位置的元素值被覆盖,我们需要挪动元素,由此我们可以先找到元素值为key的元素的下标把它进行覆盖,pos~size位置的元素整体前移一位,则可以完成删除操作。
具体代码实现:
//删除第一次出现值为key的元素
public void delete(int key){
//查找值为key的下标
int index = getIndex(key); //查找某个元素对应位置
if(index != -1) {
//pos~size位置元素整体前移一位
for (int i = index; i < size; i++) {
data[i] = data[i+1];
}
size--; //当前元素个数-1
}else{
System.out.printf("不存在值为%d的元素",key);
}
}
2.9 清空顺序表
直接将顺序表中当前元素个数(有效数据个数)置为0,而顺序表中元素可赋值为0也可不用赋值为0(只针对整型)。
//清空顺序表
public void clear(){
size = 0;
}
3.顺序表优缺点
优点:内存空间连续。尾插和尾删的效率比较高(O(1)),能够随机访问(通过下标直接访问(O(1)))
缺点:每次在中间位置插入或删除元素都需要移动元素,效率比较低(O(N))。
总结完毕~欢迎大家互相学习~