目录
🎄获取pos位置的元素与给 pos 位置的元素设为 value
🚀 内容说明:
🚆 顺序表和链表是数据结构的基础内容,本篇文章主要介绍顺序表。
🍉 有些人比较好奇,C语言的数据结构和Java的数据结构有什么区别?
- 答案就是:没区别,数据结构是一门单独的学科,和语言没有关系。只是用不同的语言实现一样的逻辑。
🛫文章使用Java语言介绍顺序表与链表。
一、线性表
- ✨线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
- 🌌线性表在逻辑上是线性结构,也就说是连续的一条直线。
- ⭐线性表在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
二、顺序表
🍓概念及结构
- 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
✨顺序表一般可以分为:
- 🍎静态顺序表:使用定长数组存储。
- 🍎动态顺序表:使用动态开辟的数组存储。
- 静态顺序表适用于确定知道需要存多少数据的场景。
- 静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。
- 相比之下动态顺序表更灵活, 根据需要动态的分配空间大小。
🍓接口实现
- 创建一个 MyArrayList 类实现顺序表增删改查的基本操作
public class MyArrayList {
//定义一个elem 数组 -> 顺序表
public int[] elem;
//数组的有效数据个数
public int usedSize;
public MyArrayList(){
//对数组实例化对象
this.elem = new int[10]; //数组如果不初始化,默认值为null,就不指向任何对象,数组要指定大小。
//usedSize 默认值为0,可以不用初始化
}
}
- 创建一个 Test 类调用 MyArrayList 类中所实现的方法。
public class Test {
public static void main(String[] args) {
MyArrayList myArrayList = new MyArrayList();
}
}
🌲实现一些方法,操作顺序表
// 打印顺序表
public void display() {}
// 获取顺序表长度
public int size() {return 0;}
// 在 pos 位置新增元素
public void add(int pos, int data) {}
// 判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 查找某个元素对应的位置
public int search(int toFind) { return -1; }
// 获取 pos 位置的元素
public int getPos(int pos) { return -1; }
// 给 pos 位置的元素设为 value
public void setPos(int pos, int value) { }
//删除第一次出现的关键字key
public void remove(int toRemove) { }
// 清空顺序表
public void clear() { }
🎄打印顺序表 -> 遍历数组
- 因为usedSize表示数组中有效的数据个数,所以顺序表的长度就是 usedSize 的值。
// 打印顺序表
public void display() {
//因为usedSize表示数组中有效的数据个数
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i]+" ");
}
System.out.println();
}
// 获取顺序表长度
public int size() {
//有效的数据个数就是顺序表的长度
return this.usedSize;
}
🎄在pos位置添加一个元素(插入)。
- 顺序表在插入元素时,所插入的位置前面一定要有元素(空顺序表除外)。
- 对pos位置的合法性进行判断:数组没有负数下标,所以pos不可以小于0,usedSize表示数组有效数据个数,如果pos大于usedSize,就超出顺序表的范围。(pos<0 || pos>usedSize)
- 判断顺序表是否满了,(如果usedSize 等于 数组长度就表示是顺序表已满),如果顺序表满了,就对数组进行扩容:使用Arrays.copyOf。写一个方法判断是顺序表否满了。
- 添加元素:首先从插入元素的位置开始将元素进行后移,移动时从后面开始移动(如果从前面移动,会将后面的值覆盖)。
- 元素移动后,将pos位置放入要插入的元素。顺序表中的添加了元素,有效数据的个数就会增加,所以对usedSize自增。
// 在 pos 位置新增元素
public void add(int pos, int data) {
if(pos<0 || pos > this.usedSize){
System.out.println("位置不合法");
return;
}
//如果顺序表满了,就进行扩容
if(isFull()){
elem = Arrays.copyOf(elem,2*elem.length);
}
//满足条件,在pos位置新增元素
for (int i = this.usedSize-1; i >=pos ; i--) {
this.elem[i+1] = this.elem[i];
}
this.elem[pos] = data;
this.usedSize++;
}
//判断顺序表是否满了
public boolean isFull(){
return this.usedSize==this.elem.length;
}
🌊代码测试:
🎄判断是否包含某个元素与查找某个元素对应的位置
- 对顺序表进行检查操作,遍历整个数组,与所要检查的元素进行比较。
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i <this.usedSize; i++) {
if(this.elem[i]==toFind){
return true;
}
}
return false;
}
// 查找某个元素对应的位置
public int search(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i]==toFind){
return i;
}
}
return -1;
}
🌊代码测试:
🎄获取pos位置的元素与给 pos 位置的元素设为 value
- 这两个操作都是在顺序表中查找指定位置的元素。
- 对pos位置的合法性进行判断:数组没有负数下标,所以pos不可以小于0,usedSize表示数组有效数据个数,数组下标是从0开始的,所以如果pos大于usedSize-1,就超出有效元素的范围。(pos<0 || pos>usedSize)
- 判断顺序表是否为空,无有效元素即为空:usedSize = 0。(写一个方法进行判断)
- 不满足添加返回-1,满足条件实现对应方法。
//判断顺序表是否为空
public boolean isEmpty(){
if(this.usedSize==0){
return true;
}
return false;
}
// 获取 pos 位置的元素
public int getPos(int pos) {
if(pos<0 || pos>this.usedSize-1){
System.out.println("位置不合法");
return -1;
}
if(isEmpty()){
System.out.println("顺序表为空");
return -1;
}
return this.elem[pos];
}
// 给 pos 位置的元素设为/更新 value
public void setPos(int pos, int value) {
if(pos<0 || pos>this.usedSize-1){
System.out.println("位置不合法");
return;
}
if(isEmpty()){
System.out.println("顺序表为空");
return;
}
this.elem[pos] = value;
}
🌊代码测试:
🎄删除第一次出现的元素
- 对顺序表进行检查,记录要删除元素的位置(可以直接调用前面所写的查找元素对应位置的方法,该方法如果找不到元素返回-1,所以使用该方法是要进行判断,如果返回-1就表示顺序表中没有要删除的元素),从当前位置遍历顺序表,将后面的元素向前移动。
- 判断顺序表是否为空,无有效元素即为空:usedSize = 0。(写一个方法进行判断)
- 不满足添加返回-1,满足条件实现对应方法。删除一个元素后,顺序表中有效数据的个数就会减少,所以对usedSize自减。
// 查找某个元素对应的位置
public int search(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i]==toFind){
return i;
}
}
return -1;
}
//判断顺序表是否为空
public boolean isEmpty(){
if(this.usedSize==0){
return true;
}
return false;
}
//删除第一次出现的关键字key
public void remove(int toRemove) {
if(isEmpty()){
System.out.println("顺序表为空");
return;
}
int index = search(toRemove);
//search 方法找不到元素会返回-1
if(index==-1){
System.out.println("要删除的元素不存在");
return;
}
for (int i = index; i <this.usedSize-1 ; i++) {
this.elem[i] = this.elem[i+1];
}
this.usedSize--;
}
🌊代码测试:
🎄清空顺序表
- 如果顺序表中存放的是普通类型的元素,可以直接将有效数据的个数置为0:usedSize = 0;
- 如果顺序表中存放的是引用类型的元素(存放地址),就要遍历顺序表,将每一个元素都置为null。
// 清空顺序表
public void clear() {
//当前顺序表中的元素是普通数据类型
this.usedSize = 0;
//如果顺序表中的元素是引用类型
/* for (int i = 0; i < this.usedSize; i++) {
this.elem[i] = null;
}*/
}
🍓顺序表中存在的问题
- 顺序表中间/头部的插入删除,时间复杂度为O(N)
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
- 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
🍎顺序表中存在的这些缺点使用链表就可以解决,后面会继续介绍链表的使用。