数据结构的顺序表
01线性结构
(1)除第一个和最后一个数据元素外,每个数据元素只有一个前驱数据元素和一个后继数据元素;
(2)第一个数据元素没有前驱数据元素;
(3)最后一个数据元素没有后继数据元素。
02线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结
构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
03个人理解
世上本没有线性表用的人多了便有了。线性表概念的存在是人为定义的讲一系列这种行为定义为线性表,顺序表的出现时在计算机物理地址上连续存储单元依次存储数据元素的结构,最为符合连续地址单元的结构为数组,所以我们使用数组作为顺序表的主体结构
计算机有两种基本的存储结构(物理存储结构):顺序结构、离散结构。使用顺序结构实现的线性表称为顺序表。
Java内存中,栈内存和堆内存占了很大一部分空间:栈内存的存储是顺序结构,堆内存的存储是离散结构。
特点 物理内存上连续,大小固定
一般顺序表分为
1.静态的顺序表:使用定长数组存储。
2.动态的顺序表:使用动态开辟的数组存储。
04抽象画顺序表
一般的一个静态的顺序表由一个定长数组,和一个存入元素个数的标记组成
这里定义一个固定长度为10数组 Object[] elementDate 用来存放数据
并为这个数组关联一个 usersize 表示存入元素的个数
05实现顺序表【接口】
01接口
public interface ISequence {
//在pos位置插入val
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();
}
02add【添加元素】
public boolean add(int pos, Object data) {
//判断pos的合法性 放入数据是否会放生溢出
if(pos<0||pos>this.userSize){
return false;
}
if(isFull()){
//数组扩容
this.elemDate= Arrays.copyOf(this.elemDate,
this.elemDate.length*2);
}
//挪数据
int key=this.userSize-1;
for(int i=key;i>=pos;i--){
this.elemDate[i+1]=this.elemDate[i];
}
//放入数据 userSized
this.elemDate[pos]=data;
this.userSize++;
return true;
}
一个顺序表在考虑元素添加时的因素分为三点
1.添加元素的合法性
添加的这个元素不能非法 体现在添加的位置不能小于零pos<0且不能大于存放当前元素的个数也就是说不能超过最后一位的下一位
2.添加元素时候超过元素最大容量
使用数组扩容Arrays.copyOf(this.elemDate, this.elemDate.length*2);
3.存放数据时
注意与数组存放不同在顺序表中,顺序表中的元素会因为添加元素的位置进行位置变动,加入添加在最后一位则之前的元素不影响,如果添加在第一位则之后的每一个元素要向后移动一位,以此类推。usersize的大小也要随之改变
4.为什么这里要使用key=userSize-1
在遍历时候时候使用的是元素的角标
//判断当前顺序表为空
private boolean isEmpty(){
return this.userSize==0;
}
//判断元素是否存满
private boolean isFull(){
return this.userSize==this.elemDate.length;
}
判断当前顺序表是否为空,为空则 userSize 等于0
02search[查找元素]
@Override
public int search(Object key) {
if(key==null){
throw new UnsupportedOperationException("不可传入参数");
}
if(isEmpty()){
return -1;
}
for(int i=0;i<this.userSize;i++){
if(this.elemDate[i].equals(key)){ //引用数据类型是否相等使用equals
return i;
}
}
return -1;
}
查找元素的思路是
1.先判断传入的参数是否合法,不合法则结束。
2.判断顺序表是否为空,假如为空也就不再次进行查询
3.查找时从头开始遍历每一个存入元素,遍历的次数 userSize-1; 保证每一个元素的下角标都被遍历,使用equals是确保在对比是做的事数组元素内容的比对。
做完所有 return 结果
03contains【查找是否含有】
@Override
public boolean contains(Object key) {
if(key==null){
throw new UnsupportedOperationException("不可传入参数");
}
if(isEmpty()){
throw new UnsupportedOperationException("数组为空");
}
for(int i=0;i<this.userSize-1;i++){
if(this.elemDate[i].equals(key)){
return true;
}
}
return false;
}
与search功能重复完全可以替换
public boolean contains(Object key){
if(search(key)==-1){
return false;
}else{
return true;
}
}
04getPos【获得数据内容】
@Override
public Object getPos(int pos) {
if(pos<0||pos>=this.userSize){
return null;
}
return this.elemDate[pos];
}
通过传入相应的下标返回元素内容
05remove【删除对应的元素内容】
@Override
public Object remove(Object key) {
int index = search(key);
if(index==-1){
return false;
}
Object originalData = this.elemDate[index];
int i=0;
for(i=index;i<this.userSize-1;i++){
this.elemDate[i]=this.elemDate[i+1];
}
this.elemDate[i+1]=null;
this.userSize--;
return originalData;
}
通过调用 search 获得目标元素的下标
1.判断是否合法
返回false 证明该顺序表中不含该元素
2.与添加元素相对应,通过index 作为循环的开始一直循环到最有一个下标位置userSize-1 将下一个元素的数据拿出来覆盖之前一个 达到顺序表向前移动,将最后一个元素=null 通过清理机制释放
06clear【回收顺序表】
也叫清空顺序表
@Override
public void clear() {
for(int i=0;i<this.userSize-1;i++){
this.elemDate[i]=null;
}
this.userSize=0;
}
通过遍历使每一个下标引用为null 被回收
07size【大小】和display【打印】
@Override
public int size() {
return this.userSize;
}
@Override
public void display() {
for(int i=0;i<this.userSize;i++){
System.out.println(this.elemDate[i]);
}
}
总结:
1.顺序表在物理上属于连续的,在使用时考虑传入参数的合法性。在变化的同时也有userSize的变化
2.顺序表的最主要构造为数组
3.顺序表的插入和删除时间复杂度为 O(n) 因为在操作时都要遍历一遍数组
4.顺序表在访问元素时支出下表直接访问所以时间复杂度为0(1)
5.顺序表的优点是:支持随机访问;空间利用率高(连续分配,不存在空间浪费)。
6.顺序表的缺点是:大小固定(一开始就要固定顺序表的最大长度);插入和删除元素需要移动大量的数据。
生命不息!!!奋斗不止!!!