顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
它的详细定义如下:
顺序表是一种数据结构,用于存储一组具有相同数据类型的元素,并按照元素在内存中的顺序进行存储和访问。顺序表中的元素在内存中是连续存储的,通过元素的下标可以直接访问和修改元素的值。
顺序表是线性表的一种实现方式。线性表是指具有相同数据类型的元素按照一定的顺序排列的数据结构,其中每个元素都有唯一的前驱元素和后继元素(除了第一个元素没有前驱,最后一个元素没有后继)。线性表可以通过顺序表、链表、栈、队列等不同的数据结构来实现。
顺序表的主要特点是:
- 元素的存储是连续的,可以通过下标直接访问和修改元素。
- 具有固定的容量,需要预先分配足够的内存空间来存储元素。
- 元素的插入和删除操作可能需要移动其他元素来保持顺序。
顺序表的隶属关系是属于线性表这个更广义的概念,同时也是线性表的一种具体实现方式。其他线性表的实现方式还包括链表、栈和队列等。不同的实现方式在存储结构和操作特性上有所差异,可以根据具体的需求选择合适的数据结构来实现线性表。
可能有人还是会有一点懵,顺序表到底隶属于什么?我们要怎么使用它?
这么说吧,顺序表可以是一个类,也可以是一个数据结构的实例。
作为一个类,顺序表可以被定义为一个具体的数据类型,其中包含了相关的属性和方法来实现顺序表的操作。这样的顺序表类可以通过实例化来创建具体的顺序表对象,并通过调用对象的方法来进行元素的插入、删除、查找等操作。(比如一会儿我们要来实现的这个顺序表)
另一方面,顺序表也可以被看作是线性表的一种实现方式,即通过顺序存储结构来实现线性表的操作。在这种情况下,顺序表可以被看作是线性表的一种具体实例,它满足线性表的特性,并提供了线性表的操作接口。
所以,顺序表既可以是一个类,也可以是一个数据结构的实例,它可以实现线性表的接口或者具有线性表的特性和操作。具体要看在哪个上下文中使用和定义顺序表。
下面是我写的一个基于数组实现的顺序表(SeqList)的完整代码,包含了每个方法的实现:
public class MySeqList {
private int[] array; //用来存放数据元素
private int size; //代表当前顺序表中的有效数据个数
private static final int DEFAUL_SIZE = 10 ; //一个默认的数据个数
// 默认构造方法
public MySeqList() {
this.array = new int[DEFAUL_SIZE];
//size 不初始化,默认为 0 .
}
// 将顺序表的底层容量设置为initCapacity
public MySeqList(int initCapacity) {
this.array = new int[initCapacity];
this.size = 0;
}
// 新增元素,默认在数组最后新增
public void add(int data) {
if (this.size == this.array.length) {
resizeArray();
}
this.array[this.size] = data;
this.size++;
}
// 在 pos 位置新增元素
//先要挪动数据------从后往前挪动
public void add(int pos, int data) throws IndexOutOfBoundsException {
if (pos < 0 || pos > this.size) {
throw new IndexOutOfBoundsException("Invalid position: " + pos);
}
//如果是满的,要先进行扩容
if (this.size == this.array.length) {
resizeArray();
}
for (int i = this.size; i > pos; i--) {
this.array[i] = this.array[i - 1];
}
this.array[pos] = data;
this.size++;
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i < this.size; i++) {
if (this.array[i] == toFind) {
return true;
}
}
//如果找的是一个引用类型,还可以用等号吗?
//equals()返回值是 true、false
//comparaTo 返回的是整型,比较大小
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < this.size; i++) {
if (this.array[i] == toFind) {
return i;
}
}
return -1;
}
// 获取 pos 位置的元素
public int get(int pos) throws IndexOutOfBoundsException {
if (pos < 0 || pos >= this.size) {
throw new IndexOutOfBoundsException("Invalid position: " + pos);
}
return this.array[pos];
}
// 给 pos 位置的元素设为 value
// 可以理解为更新
public void set(int pos, int value) throws IndexOutOfBoundsException {
if (pos < 0 || pos >=this.size) {
throw new IndexOutOfBoundsException("Invalid position: " + pos);
}
array[pos] = value;
}
// 删除第一次出现的关键字 key
public void remove(int toRemove) {
int index = indexOf(toRemove);
if (index != -1) {
for (int i = index; i < this.size - 1; i++) {
this.array[i] = this.array[i + 1];
}
this.size--;
}
}
// 获取顺序表长度
public int size() {
return size;
}
// 清空顺序表
public void clear() {
this.size = 0;
}
// 打印顺序表
public void display() {
for (int i = 0; i < this.size; i++) {
System.out.print(this.array[i] + " ");
}
//换行
System.out.println();
}
// 内部方法:数组扩容
private void resizeArray() {
int[] newArray = new int[this.array.length * 2];
System.arraycopy(this.array, 0, newArray, 0, this.size);
this.array = newArray;
}
}
class IndexOutOfBoundsException extends Exception{
public IndexOutOfBoundsException() {
super("Invalid position");
}
public IndexOutOfBoundsException(String message) {
super(message);
}
}
当然,这里 add(int data) 和 resizeArray() 还可以运用 Arrays 类的 copyOf () 改写为这样:
public boolean isFull(){
if(this.size==this.array.length){
return true;
}
return false;
}
public void add2(int data) {
if (isFull()) {
this.array = Arrays.copyOf(this.array,2*this.array.length);
}
this.array[this.size] = data;
this.size++;
}
复习以下我们的 Arrays.copyOf ( ) 方法:
Arrays.copyOf ( ) 方法是Java中用于复制数组的方法。它可以复制指定数组的所有元素到一个新数组中,并返回新数组。 Arrays.copyOf ( ) 方法有两个参数:源数组和目标数组的长度。
下面是 Arrays.copyOf ( ) 方法的语法:
public static <T> T[] copyOf(T[] original, int newLength)
其中,original是源数组,newLength是目标数组的长度。返回的数组类型与源数组类型相同。
以下是一个示例,演示如何使用 Arrays.copyOf ( ) 方法复制数组:
import java.util.Arrays; public class ArrayCopyExample { public static void main(String[] args) { int[] sourceArray = {1, 2, 3, 4, 5}; int[] targetArray = Arrays.copyOf(sourceArray, sourceArray.length); System.out.println("Source Array: " + Arrays.toString(sourceArray)); System.out.println("Target Array: " + Arrays.toString(targetArray)); //别怀疑,输出的值是一样的 } }
需要注意的是, Arrays.copyOf ( ) 方法会根据目标数组的长度进行复制。如果目标数组长度小于源数组长度,复制结果将截取源数组的前部分元素;如果目标数组长度大于源数组长度,复制结果将在末尾填充默认值 0 。
哦,对了。你也可以把 “ 检查数组是否已满,需要扩容 ” 的代码单独提出来,形成一个新的方法。
还有,在清空顺序表这个方法里,如果里面的每一个元素都是引用类型,那么你需要一个一个的把往前覆盖的数据的原来位置置为 null 。
for(int i =0 ; i < this.size ; i++ )
{
this.array [i]== this.array[i+1];
}
this.array [this.size-1]==null;
this.size--;
如果不这样做,那么你对最后一个对象的引用将遗留而造成内存泄漏。
你是否发现,我们写出来的方法大多是一环套一环的,比如,先写了 indexOf( ) ,在 remove( ) 中就可以直接运用了。所以在开始写代码前,拿出一点时间来思考应该先完成哪一个方法是很有必要的。