线性表
线性表是n个具有相同特性的数据元素的有序集合.
常见的线性表:顺序表,链表,栈,队列,字符串…
线性表在逻辑上是连续的,但是在物理结构上不一定是连续的,线性表在物理存储时,通常用数组或者链表的形式来存储.
顺序表
概念及结构
- 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下用数组储存.在数组上完成数据的增删改查.
- . 数组具有一个重要特性:随机访问能力,可以高效的按照下标来操作
2.顺序表可以分为:
- 静态顺序表:用定长度数组来存储
- 动态顺序表:用动态开辟的数组存储(ArrayList)
- 静态顺序表需要知道有多少数据,并且长度N固定后,开小了不够用,开大了浪费空间,所以动态顺序表更灵活,可以根据动态的分配空间大小.以后常用动态顺序表.
顺序表的接口实现
实现一个动态顺序表,需要支持的接口:
public class SeqList {
private int [] datas=new int[100];
private int size=0;//有效元素个数,是长度
//顺序表的最主要特点是支持随机访问能力,可以高效的按照下标来操作。
public void display(){
//打印顺序表
String result="[";
for (int i=0;i<size;i++){
result+=datas[i];
if (i<size-1){//最后一个元素的下标
result+=",";
}
}
result+="]";
System.out.println(result);
}
//在pos位置新增元素data
public void add(int pos,int data){
//判断pos的值是否为有效元素
//边界判断放在方法里面,不破坏其封装性
//可选择用double check;
if (pos<0||pos>size){//是否会越界
return;
}
//对数组进行扩容,当数组容量不够时会自动进行扩容
if (size>=datas.length){
//需要扩容
int [] newdatas=new int[2*datas.length];
for (int i=0;i<=size;i++){
newdatas[i]=datas[i];
}
datas=newdatas;//此时datas指向newdatas地址,
// 原来的地址没有引用去指向它,则它会被GC垃圾回收机制回收
}
//1.尾插情况(较简单)将data的值插入size位置
if (pos==size){
datas[size]=data;
size++;
return;
}
//2.一般位置插入。从后往前访问依次向后移
for (int i=size-1;i>=pos;i--) {
datas[i+1]=datas[i];
datas[pos]=data;
size++;
return;
}
}
public boolean contains(int toFind){
//判断是否包含某个元素。遍历数组,若数组中有元素与toFind相同,则返回true
// 反之则返回false;
for (int i=0;i<size;i++){
if (datas[i]==toFind){
return true;
}
}
return false;
}
public int search(int toFind){
//查找某个元素对应的下标
for (int i=0;i<size;i++){
if (datas[i]==toFind){
return i;
}
}
return -1;
}
public Integer getPos(int pos){
//对pos进行判断,当pos的值为非法时,返回空null
// 但null为引用类型,int为基础类型,所以应改int为Integer;
//对于参数非法的情况处理有两种;1.返回一个非法值(C,C++.Go)
//2.抛出一个异常(java,python)
if (pos<0||pos>size){
return null;
}
return datas[pos];
}
public void setPos(int pos,int value){
datas[pos]=value;
}
public void remove(int toRemove){//toRemo是要删除的元素值;
//1.先找到toRemo的下标位置
int pos =search(toRemove);//ctrl+鼠标左键可跳转到定义
if (pos<-1){
//没找到,则直接返回
return;
}
//2.如果下标是最后一个元素直接尾删即可
if (pos==size-1){
size--;
return;
}
//3.如果下标是中间元素,先从前往后遍历,搬运到末尾,再删除
for (int i = pos; i <size-1 ; i++) {
datas[i]=datas[i+1];//搬运
size--;//删除
return;
}
}
public void clear(){
size = 0;
}
}
简单的测试代码:单元测试,写一个,测一个,降低测试成本:
public class Test {
private static void testContains(){
SeqList seqList=new SeqList();
seqList.add(0,1);
seqList.add(0,2);
seqList.add(0,3);
seqList.add(0,4);
System.out.println(seqList.contains(2));
}
private static void textSecrch(){
SeqList seqList=new SeqList();
seqList.add(0,1);
seqList.add(0,2);
seqList.add(0,3);
seqList.add(0,4);
System.out.println(seqList.search(2));
}
private static void textRemove(){
SeqList seqList=new SeqList();
seqList.add(0,1);
seqList.add(0,2);
seqList.add(0,3);
seqList.add(0,4);
seqList.remove(4);
seqList.display();
}
public static void main(String[] args){
// testContains();
// int [] a=new int[]{3,5,9,0,6,};
// ArrayTest.bubbleSort(a);
// ArrayTest.reversr(a);
// textSecrch();
textRemove();
}
}
关于静态顺序表的扩容:
顺序表操作的性能分析:
- 新增元素:头插和中间元素插入是(O(N)),尾插是O(1)(不考虑扩容的情况).
- 查找元素:O(N);
- 根据下标获取/修改元素O(1).
- 删除元素O(N).
- 删除所有元素O(1).
基于时间复杂度的大小,顺序表最常见应用改,查.
尾插的时候效率也比较高,但是如果涉及到扩容操作,效率就会变低.