线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表
1.1 概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储。
- 动态顺序表:使用动态开辟的数组存储。
静态顺序表适用于确定知道需要存多少数据的场景,静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。相比之下动态顺序表更灵活, 根据需要动态的分配空间大小。
1.2 方法实现
首先创建一个类 MyArrayList
public class MyArrayList {
public int[]list; //可变长数组
public int size; //元素个数
MyArrayList(){
//初始化
this.list = new int[5];
this.size = 0;
}
}
类中方法如下:
// 打印顺序表
public void display() {
for (int i = 0; i < this.size; i++) {
System.out.print(this.list[i]+" ");
}
}
// 尾插
public void pushBack(int data) {
//首先判断表当前元素个数是否和表的当前容量相等
//如果相等,表示需要扩容操作
//一般为二倍扩容
if(this.size == this.list.length) {
this.list = Arrays.copyOf(this.list, 2*this.list.length);
}
this.list[size++] = data;
}
// 尾删
public void popBack() {
if(this.size==0) {
return;
}
this.size--;
}
// 在pos位置处插入元素
public void add(int pos,int data) {
//首先判断插入位置是否合法
if(pos < 0 || pos > this.size) {
System.out.println("插入位置非法!");
return ;
}
//判断是否需要扩容
if(this.size == this.list.length) {
this.list = Arrays.copyOf(this.list, 2*this.list.length);
}
//将pos位置及其后的所有元素后移一个位置
for (int i = this.size-1; i >= pos; i--) {
this.list[i+1] = this.list[i];
}
//将待插入的元素放到pos位置上
this.list[pos] = data;
this.size++;
}
//查找某一个元素对应的位置
public int search(int x) {
for (int i = 0; i < this.size; i++) {
//通过数组遍历的方式
if(this.list[i] == x) {
//找到返回其下标
return i;
}
}
//没有找到则返回-1
return -1;
}
//获取 pos 位置的元素
public int getPos(int pos) {
//判断pos的合法性
if(pos<0||pos>=this.size) {
System.out.println("pos位置非法");
return -1;
}
return this.list[pos];
}
//判定是否包含某个元素
public boolean contains(int x) {
for (int i = 0; i < this.size; i++) {
//采用数组遍历的方法,如果包含返回true
if(this.list[i] == x) {
return true;
}
}
//否则返回false
return false;
}
//给 pos 位置的元素设为value
public void setPos(int pos,int value) {
//判断pos位置的合法性
if(pos<0||pos>=this.size) {
System.out.println("pos位置非法");
return;
}
this.list[pos] = value;
}
//删除第一次出现的关键字key
//方法一:
public void remove(int toRemove) {
if(this.size==0) {
return;
}
//找到需要删除的值,将其后面所有的元素前移一个位置
//最后将数组有效元素个数减1
for (int i = 0; i < this.size; i++) {
if(this.list[i]==toRemove) {
for(int j = i;j < this.size;j++) {
this.list[j] = this.list[j+1];
}
this.size--;
break;
}
}
}
//方法二:
public void remove(int toRemove) {
//首先查找需要删除的值的下标
//如果不存在则直接返回
//如果存在将其后面所有的元素前移一个位置,最后将数组有效元素个数减1
int index = search(toRemove);
if(index == -1) {
System.out.println("没有该关键字");
return;
}
for(int i = index;i < this.size;i++) {
this.list[i] = this.list[i+1];
}
this.size--;
}
// 获取顺序表长度
public int size() {
//直接返回有效元素个数
return this.size;
}
//清空顺序表
public void clear() {
//将有效元素个数设为0
this.size = 0;
}
1.3 顺序表的问题
- 顺序表中间/头部的插入删除,时间复杂度为O(N)
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
- 扩容一般为2倍增长,势必会有一定的空间浪费。