在说顺序表之前,先认识一下线性表(Linear List)。线性表称为有序表(Ordered List),是数学概念应用在计算机科学中一种基本的数据结构。
- 线性表数据元素可以是任何一种类型,不过对于同一线性表的每一个元素都必须属于同一类型。
- 从数据在物理内存存储形式上线性表可以分为: 顺序表和链式表,本文主要做了顺序表相关操作的整理。
顺序表:
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
顺序表又可分为:
3. 静态顺序表:使用定长数组存储。
4. 动态顺序表:使用动态开辟的数组存储。
本文实现的是动态顺序表:
首先,在一个接口中声明顺序表的各项操作:
1.在pos位置插入data;
2. 查找关键字key 找到返回key的下标,没有返回null;
3. 查找是否包含关键字key是否在顺序表当中;
4. 得到pos位置的值;
5. 删除第一次出现的关键字key;
6. 得到顺序表的长度;
7. 打印顺序表;
8. 清空顺序表以防内存泄漏;
package com.Impl;
public interface ISequence {
//在pos位置插入data
boolean add(int pos,Object data);
//查找关键字key 找到返回key的下标,没有返回null;
int search(Object key);
//查找是否包含关键字key是否在顺序表当中(这个和search有点冲突)
boolean contains(Object key);
//得到pos位置的值
Object getPos(int pos);
//删除第一次出现的关键字key
Object remove(Object key);
//得到顺序表的长度
int size();
//打印顺序表
void display();
//清空顺序表以防内存泄漏
void clear();
}
在具体实现类中实现接口中方法的实现:
package com.dao;
import com.Impl.ISequence;
import java.util.Arrays;
public class SequenceImpl implements ISequence {
private Object[] elemDate;
private int usedSize;
public static final int DEFAULT_CAPACITY = 10;
public SequenceImpl(){
this.elemDate = new Object[DEFAULT_CAPACITY];
this.usedSize = 0;
}
//判断顺序表是否为空
private boolean isFull(){
return this.usedSize == this.elemDate.length;
}
private boolean isEmpty(){
return this.usedSize == 0;
}
@Override
在pos位置插入data
public boolean add(int pos, Object data) {
//判断pos位置的合法性
if(pos>this.usedSize || pos<0){
return false;
}
if(isFull()){
//扩容
this.elemDate = Arrays.copyOf(this.elemDate,this.elemDate.length*2);
}
else{
//移动数据
for(int i = this.usedSize-1;i>=pos;i--){
//Object[i] = Object[i-1];
this.elemDate[i+1] = this.elemDate[i];
}
}
//放入数据,并且usedSize++
this.elemDate[pos] = data;
this.usedSize++;
return true;
}
//查找关键字key 找到返回key的下标,没有返回null;
@Override
public int search(Object key) {
if(key == null){
throw new UnsupportedOperationException("不可以传入null作为参数!");
}
if(isEmpty()){
return -1;
}
for (int i = 0; i < this.usedSize; i++) {
if(this.elemDate[i].equals(key)){
return i;
}
}
return -1;
}
//查找是否包含关键字key是否在顺序表当中(这个和search有点冲突)
@Override
public boolean contains(Object key) {
if(key.equals(null)){
throw new UnsupportedOperationException("不可以传入null作为参数!");
}
if(isEmpty()){
return false;
}
for (int i = 0; i < this.usedSize; i++) {
if(this.elemDate[i] == key){
return true;
}
}
return false;
}
//得到pos位置的值
@Override
public Object getPos(int pos) {
if(pos<0 || pos >= this.usedSize){
return null;
}
return this.elemDate[pos];
}
//删除第一次出现的关键字key
@Override
public Object remove(Object key) {
int index = search(key);
if(index == -1){
return -1;
}
Object oldData = this.elemDate[index];
int i = index;
for(;i<this.usedSize-1;i++){
this.elemDate[i] = this.elemDate[i+1];
}
this.elemDate[i+1] = null;
this.usedSize--;
return oldData;
}
//得到顺序表的长度
@Override
public int size() {
return this.usedSize;
}
//打印顺序表
@Override
public void display() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elemDate[i]+" ");
}
System.out.println();
}
//清空顺序表以防内存泄漏
@Override
public void clear() {
for (int i = 0; i < this.usedSize; i++) {
this.elemDate[i] = null;
}
this.usedSize = 0;
}
}
在主方法中进行顺序表相关操作的测试:
package com.main;
import com.dao.SequenceImpl;
public class TestMain {
public static void main(String[] args) {
SequenceImpl sequence = new SequenceImpl();
//插入
for (int i = 10; i >=0; i--) {
sequence.add(10-i,i);
}
//打印
sequence.display();
//输出顺序表大小
System.out.println(sequence.size());
//判断元素"5"是否在顺序表中
System.out.println(sequence.contains(5));
//得到3位置的值
System.out.println(sequence.getPos(3));
//返回数字9(存在)和数字15(不存在)对应的下标
System.out.println(sequence.search(9));
System.out.println(sequence.search(15));
//删除首个出现的"6",并打印一次观察
sequence.remove(6);
sequence.display();
//将顺序表清空,并打印一次
System.out.println("将顺序表清空,并打印一次:");
sequence.clear();
sequence.display();
}
}
顺序表分析:
优点:
便于随机访问查找
缺点:
不便于插⼊,删除(末尾插⼊或删除⽐较⽅便,但中间/头部的插入删除,时间复杂度为O(N))
场景:
需要⼤量访问元素,尾删,尾插较多时使⽤顺序表