【数据结构 Java 版】玩转顺序表

一、线性表

线性表是 n 个具有相同特性的数据元素的有限序列。

常见的线性表有:

顺序表、链表、栈、队列、字符串

线性表的逻辑是线性结构,也就是连续的一条线。但是物理结构并不一定是连续的,线性表在物理上存储时,通常以数组链式结构的形式存储,例如在这里插入图片描述

二、顺序表

1. 概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况采用数组存储

那既然顺序表用数组存储为什么不直接使用数组呢?顺序表和数组是同一个东西吗?我们可以思考一下我们如何求一个数组和一个顺序表的实际使用大小呢?

  • 对于数组: 好像有点麻烦,因为我们很难以什么条件去判断最后一个实际使用的数组的位置
  • 对于顺序表: 我们可以存一个计数一个,哈哈哈,这样一看其实数组和顺序表是不一样的

那么一个简单的顺序表是怎样构成的呢?

我们应该有数组去存储,有这个数组的总容量,有数组中实际使用的值

换做代码就是

扫描二维码关注公众号,回复: 13171037 查看本文章
publuc class MyArrayList{
    
    
    // 存储数据的数组
    public int[] elem;
    // 数组的总容量
    public static int capacity = 10;
    // 数组实际使用大小
    public int usedSize;
    public MyArrayList(){
    
    
        this.elem = new int[capacity];
    }
}

2. 接口实现

而了解了顺序表的结构后,我们要自己来实现以下接口去更加方便的实现一些内容

  1. 判断顺序表是否已满

    public boolean isFull(){
          
          
        return this.usedSize == capacity;
    }
    
  2. 在 pos 位置新增元素

    public void add(int pos, int data){
          
          
        if(pos<0 || pos>this.usedSize){
          
          
            System.out.println("pos 位置不合法");
            return;
        }
        if(isFull()){
          
          
            // 扩容
            this.elem=Arrays.copyOf(this.elem, 2*capacity);
            capacity*=2;
        }
        for(int i = this.usedSize;i>pos;i--){
          
          
            this.elem[i]=this.elem[i-1];
        }
        this.elem[pos]=data;
        this.usedSize++;
    }
    
  3. 打印顺序表

    public void display(){
          
          
        for(int i=0;i<this.usedSize;i++){
          
          
            System.out.print(this.elem[i]+" ");
        }
    }
    
  4. 判断顺序表是否为空

    public boolean isEmpty(){
          
          
        return this.usedSize==0;
    }
    
  5. 判断是否包含某个元素

    public boolean contains(int toFind){
          
          
        if(isEmpty()){
          
          
            return false;
        }
        for(int i=0;i<this.usedSize;i++){
          
          
            if(this.elem[i]==toFind){
          
          
                return true;
            }
        }
        return false;
    }
    
  6. 查找某个元素的第一次出现的位置

    public int search(int toFind){
          
          
        if(isEmpty()){
          
          
           return -1;
        }
        for(int i=0;i<this.usedSize;i++){
          
          
            if(this.elem[i]==toFind){
          
          
                return i;
            }
        }
        return -1;
    }
    
  7. 获取 pos 位置的元素

    public int getPos(int pos){
          
          
        if(isEmpty()){
          
          
            return -1;
            // throw new RuntimeException("顺序表为空");
        }
        if(pos<0 || pos>=this.usedSize){
          
          
            throw new RuntimeException("顺序不合法");     
        }
        return this.elem[pos];
    }
    
    • 其实上述代码返回 -1 其实是不一定的,因为在已知该数组不可能等于-1的情况下,我们可以这样处理,但是如果能等于-1.就是错的,就需要换其他值(这相当于具体情况对业务进行处理)
    • 也可以使用注释里的 throw new RuntimeException("顺序表为空"); ,这其实是手打抛出错误(异常
  8. 获取顺序表长度

    public int size(){
          
          
        return this.usedSize;
    }
    
  9. 给 pos 的位置的元素设为 value

    public void setPos(int pos, int value){
          
          
        if(pos<0 || pos>=this.usedSize){
          
          
            throw new RuntimeException("pos 不合法")
        }
        this.elem[pos]=value;
    }
    
  10. 删除第一次出现的关键字 key

    public void remove(int toRemove){
          
          
        if(isEmpty()){
          
          
            System.out.println("数组为空");
            return;
        }
        int index=search(toRemove);
        if(indfex==-1){
          
          
            System.out.println("没用你要删除的数字");
            return;
        }
        for(int i =index;i<this.usedSize-1;i++){
          
          
            this.elem[i]=this.elem[i+1];
        }
        this.usedSize--;    
    }
    

    其实在循环中是不能删除最后一个元素的,因为 i < this.usedSize ,但是上述代码是可以直接删除最后一个元素,因为 usedSize 自减后,就相当于删除最后一个元素了

  11. 清空顺序表

    public void clear(){
          
          
        for(int i=0;i<this.usedSize;i++){
          
          
            this.elem[i]=0;
        }
        this.usedSize=0;
    }
    

3. 增、删、改、查的时间复杂度

既然我们实现了以上接口,我们可以根据上述代码思考下,顺序表中增删改查的时间复杂度为多少

  • 增:时间复杂度为:O(N)
  • 删:时间复杂度为:O(N)
  • 查:对于查找关键字的时间复杂度为:O(N),对于查找给定下标的时间复杂度为:O(1)
  • 改:时间复杂度为:O(1)

通过观察增删查改的时间的复杂度我们发现,顺序表更适合用作查改,而不太适合用作增删

三、总结

今天介绍的顺序表应该也比较轻松,其实它的本质就是数组。但是在我们上述实现接口时我们可以发现

  • 顺序表更适合用作查改,而不适合用作增删
  • 每次增容都需要申请新空间,释放旧空间,会导致空间使用有不小的消耗
  • 增容时是按倍数增长的,容易造成空间的浪费

因此,下一章我将介绍链表,它将解决上述顺序表一些不合使用的地方

猜你喜欢

转载自blog.csdn.net/weixin_51367845/article/details/119989525