数据结构----线性表、顺序表、模拟实现顺序表

1. 线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,指具有相同数据类型的元素按照一定的顺序排列的数据结构,其中每个元素都有唯一的前驱元素和后继元素(除了第一个元素没有前驱,最后一个元素没有后继),常见的线性表:顺序表、链表、栈、队列…
在这里插入图片描述

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

2. 顺序表

顺序表是一种线性表的存储结构,它是由一组地址连续的存储单元依次存储线性表中的元素,元素之间的逻辑关系和物理关系是一致的。顺序表可以通过数组来实现,在数组上完成数据的增删查改。

顺序表的主要特点是:

  • 元素之间的逻辑关系和物理关系是一致的,即顺序表中的元素在内存中是连续存储的。
  • 可以通过下标直接访问和修改元素,因此支持随机访问。
  • 具有固定的容量,需要预先分配足够的内存空间来存储元素,如果需要动态调整大小,可能需要重新分配内存空间并进行元素的复制。
  • 元素的插入和删除操作可能需要移动其他元素来保持顺序,因此效率较低。

顺序表在实际应用中有着广泛的应用,例如数组就是一种顺序表的实现方式。在计算机科学中,顺序表也是一种重要的数据结构,用于存储和操作线性表中的元素。

3. 模拟实现顺序表

下面是我写的一个基于数组实现的顺序表(MyArrayList)的完整代码,包含了每个方法的实现:

【IList.clss】:
模拟List接口,里面记录了MyArrayList需要实现的方法

public interface IList {
    // 新增元素,默认在数组最后新增
    public void add(int data);

    // 在 pos 位置新增元素
    public void add(int pos, int data);

    // 判定是否包含某个元素
    public boolean contains(int toFind);

    // 查找某个元素对应的位置
    public int indexOf(int toFind);

    // 获取 pos 位置的元素
    public int get(int pos);

    // 给 pos 位置的元素设为 value
    public void set(int pos, int value);

    //删除第一次出现的关键字key
    public void remove(int toRemove);

    // 获取顺序表长度
    public int size();

    // 清空顺序表
    public void clear();

    // 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
    public void display();
}

【MyArrayListEmpty.clss】:
自定义异常数组为空异常(运行时异常)

public class MyArrayListEmpty extends RuntimeException{
    public MyArrayListEmpty(String msg) {
        super(msg);
    }
}

【PositionIllegal.clss】:
自定义异常数组下标非法(运行时异常)

public class PositionIllegal extends RuntimeException{
    public PositionIllegal(String msg) {
        super(msg);
    }
}

【PositionOutOfBounds .clss】:
自定义异常数组访问元素时下标越界(运行时异常)

public class PositionOutOfBounds extends RuntimeException {
    public PositionOutOfBounds(String msg) {
        super(msg);
    }
}

【MyArrayList.class】:

public class MyArrayList implements IList {
    int[] elements;//用来存放数据元素
    int usedSize;//代表当前顺序表中的有效数据个数

    public static final int DEFAULT_SIZE = 2;//默认的数组中的数据的个数

    // 默认构造方法
    public MyArrayList() {
        this.elements = new int[DEFAULT_SIZE];//默认初始大小
    }

    // 将顺序表的底层容量设置为initCapacity
    public MyArrayList(int initCapacity) {
        this.elements = new int[initCapacity];//自定义大小
    }

    @Override
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elements[i] + " ");
        }
        System.out.println();
    }

    @Override
    public void add(int data) {
        //扩容
        checkCapacity();
        //添加数组
        this.elements[this.usedSize] = data;
        //有效数据个数加一
        this.usedSize++;
    }

    private boolean isFull() {
        //1.检查数组是否满了
        if (this.usedSize == elements.length) {
            return true;
        }
        return false;
    }

    private void checkCapacity() {
        //2.检查数组容量并进行扩容
        if (isFull()) {
            // 数组容量扩大二倍
            this.elements = Arrays.copyOf(elements, 2 * (this.elements.length));
        }
    }

    @Override
    public void add(int pos, int data) {
        //1.检查数组元素下标是否合法
        checkPositionIllegalOnAdd(pos);
        //2.检查数组的容量
        checkCapacity();
        //3.将数据存放在对应要存放的位置
        for (int i = this.usedSize - 1; i >= pos; i++) {
            this.elements[i + 1] = this.elements[i];
        }
        this.elements[pos] = data;
        this.usedSize++;
    }


    @Override
    public boolean contains(int toFind) {
        //1.判断数组是否为空
        isEmpty();
        //2.判断数组中是否含有该元素
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elements[i] == toFind) {
                return true;
            }
        }
        return false;
    }


    @Override
    public int indexOf(int toFind) {
        //1.判断数组是否为空
        isEmpty();
        //2.判断数组中是否含有该元素
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elements[i] == toFind) {
                return i;
            }
        }
        //3.数组中不含有该元素,返回值为-1
        return -1;
    }


    @Override
    public int get(int pos) {
        //检查数组下标是否越界
        checkPositionGetAndSet(pos);
        //判断数组是否为空
        isEmpty();
        //得到下标对应的值
        int ret = this.elements[pos];
        return ret;
    }


    @Override
    public void set(int pos, int value) {
        //检查数组下标是否越界
        checkPositionGetAndSet(pos);
        //判断数组是否为空
        isEmpty();
        //设置下标对应的值
        this.elements[pos] = value;
    }

    @Override
    public void remove(int toRemove) {
        //获取要删除的元素对应的下标
        int index = indexOf(toRemove);
        //判断下标
        if (index != -1) {
            //将index之后的所有元素向前移动一位
            for (int i = index; i < this.usedSize - 1; i++) {
                this.elements[i] = this.elements[i + 1];
            }
            //删除元素之后,数组计数器减一
            this.usedSize--;
        }
    }

    @Override
    public int size() {
        return this.usedSize;
    }

    @Override
    public void clear() {
        this.usedSize = 0;
    }

    private void checkPositionIllegalOnAdd(int pos) throws PositionIllegal {
        //存放数据的合法位置为 :0 ~ usedSize
        if (pos < 0 || pos > this.usedSize) {
            throw new PositionIllegal("数组的下标" + pos + "不合法");
        }
    }

    private void checkPositionGetAndSet(int pos) {
        if (pos < 0 || pos >= this.usedSize) {
            throw new PositionOutOfBounds("数组下标" + pos + "越界");
        }
    }

    public void isEmpty() {
        if (this.usedSize == 0) {
            throw new MyArrayListEmpty("该数组为空");
        }
    }
}

【讲解部分一】:

    private void checkCapacity() {
        //2.检查数组容量并进行扩容
        if (isFull()) {
            // 数组容量扩大二倍
            this.elements = Arrays.copyOf(elements, 2 * (this.elements.length));
        }
    }

Arrays.copyOf ( ) 方法是Java中用于复制数组的方法。它可以复制指定数组的所有元素到一个新数组中,并返回新数组。 Arrays.copyOf ( ) 方法有两个参数:源数组和目标数组的长度。

下面是 Arrays.copyOf ( ) 方法的语法:

public static <T> T[] copyOf(T[] original, int newLength)

其中,original是源数组,newLength是目标数组的长度。返回的数组类型与源数组类型相同。需要注意的是, Arrays.copyOf ( ) 方法会根据目标数组的长度进行复制。如果目标数组长度小于源数组长度,复制结果将截取源数组的前部分元素;如果目标数组长度大于源数组长度,复制结果将在末尾填充默认值 0 。

【讲解部分二】:

public boolean contains(int toFind) {
        //1.判断数组是否为空
        isEmpty();
        //2.判断数组中是否含有该元素
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elements[i] == toFind) {
                return true;
            }
        }
        return false;
    }

如果里面的每一个元素都是引用类型,需要使用equals()来进行比较,如果是自定义类型,还需要自己覆盖equals()方法。

public boolean contains(int toFind) {
        //1.判断数组是否为空
        isEmpty();
        //2.判断数组中是否含有该元素
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elements[i].equals(toFind)) {
                return true;
            }
        }
        return false;
    }

【讲解部分三】:

  public void remove(int toRemove) {
        //获取要删除的元素对应的下标
        int index = indexOf(toRemove);
        //判断下标
        if (index != -1) {
            //将index之后的所有元素向前移动一位
            for (int i = index; i < this.usedSize - 1; i++) {
                this.elements[i] = this.elements[i + 1];
            }
            //删除元素之后,数组计数器减一
            this.usedSize--;
        }
    }

如果里面的每一个元素都是引用类型,需要对最后一个元素设置为null,如果不这样做,那么你对最后一个对象的引用将遗留而造成内存泄漏。

【讲解部分四】:

  public void clear() {
        this.usedSize = 0;
    }

如果数组中每一个元素都是引用类型,将this.usedSize = 0;时,会发生内存泄露,原因是JVM只有当对象没有被引用时,才会进行对象回收,this.usedSize = 0;时并没将元素置为null,顺序表仍然引用对象,JVM无法进行回收。

方式一:

  public void clear() {
        this.element= null;
        this.usedSize = 0;
    }

较为暴力,不推荐

方式二:

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

比较建议,使用垃圾回收器(GC)进行回收

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值