算法之旅(三) 顺序表

顺序表的基本概念及实现

顺序表(Sequential List)是一种线性表的顺序存储实现方式,常见于数组。它利用一段连续的内存空间来存储数据元素,支持快速的随机访问。由于内存地址是连续的,因此可以通过元素的索引(下标)直接访问元素,访问时间复杂度为 O(1)

线性结构

线性结构是数据结构中最基本、最常用的一类结构,其特点是数据元素之间存在一对一的线性关系,数据元素按顺序排列。线性结构包括:

顺序表(数组)
链表

队列


一、顺序表的基本概念

1. 顺序表的特点

  • 连续存储:数据元素在内存中按照顺序连续存放。
  • 随机访问:可以通过下标直接访问任意位置的元素,访问速度快。
  • 固定容量:容量固定,初始化后大小不能改变(对于数组而言)。
  • 存储类型一致:存储的元素类型必须相同。

2. 顺序表的优缺点

优点

  • 访问速度快:支持随机访问,查找元素效率高。
  • 空间利用率高:不需要额外的存储空间来存放节点指针。

缺点

  • 插入和删除效率低:在中间位置插入或删除元素时,需要移动大量元素。
  • 容量固定:数组的容量固定,不能动态扩容(除非手动实现扩容机制)。

二、顺序表的初始化和判空判满功能实现

1. 初始化顺序表

在 Java 中,可以使用数组或集合类来实现顺序表。下面以数组为基础,实现一个简单的顺序表。

public class SequenceList {
    private int[] data;    // 存储元素的数组
    private int size;      // 当前元素数量
    private int capacity;  // 顺序表容量

    // 初始化顺序表
    public SequenceList(int capacity) {
        this.capacity = capacity;
        data = new int[capacity];
        size = 0;
    }
}

2. 判空功能实现

判断顺序表是否为空,即判断 size 是否为 0。

// 判断顺序表是否为空
public boolean isEmpty() {
    return size == 0;
}

3. 判满功能实现

判断顺序表是否已满,即判断 size 是否等于 capacity

// 判断顺序表是否已满
public boolean isFull() {
    return size == capacity;
}

三、顺序表实现之指定位置数据的增加与遍历操作

1. 在指定位置增加元素

在顺序表的指定位置插入元素,需要考虑以下情况:

  • 位置合法性检查:插入位置应在 [0, size] 之间。
  • 顺序表是否已满:如果已满,需要先扩容(后续会介绍)。
  • 元素后移:从插入位置开始,后面的元素需要依次后移一位。
// 在指定位置插入元素
public void insert(int index, int element) {
    if (isFull()) {
        throw new RuntimeException("顺序表已满,无法插入新元素");
    }
    if (index < 0 || index > size) {
        throw new IndexOutOfBoundsException("插入位置不合法");
    }
    // 元素后移
    for (int i = size - 1; i >= index; i--) {
        data[i + 1] = data[i];
    }
    data[index] = element;
    size++;
}

2. 遍历顺序表

遍历顺序表中的元素,依次输出或处理每个元素。

// 遍历顺序表
public void traverse() {
    for (int i = 0; i < size; i++) {
        System.out.print(data[i] + " ");
    }
    System.out.println();
}

四、顺序表实现删除指定位置的元素与修改操作

1. 删除指定位置的元素

删除指定位置的元素,需要将后面的元素依次前移。

// 删除指定位置的元素
public void delete(int index) {
    if (isEmpty()) {
        throw new RuntimeException("顺序表为空,无法删除元素");
    }
    if (index < 0 || index >= size) {
        throw new IndexOutOfBoundsException("删除位置不合法");
    }
    // 元素前移
    for (int i = index; i < size - 1; i++) {
        data[i] = data[i + 1];
    }
    size--;
}

2. 修改指定位置的元素

修改指定位置的元素,即将新值赋给指定位置。

// 修改指定位置的元素
public void update(int index, int element) {
    if (index < 0 || index >= size) {
        throw new IndexOutOfBoundsException("修改位置不合法");
    }
    data[index] = element;
}

五、顺序表实现扩容操作

由于数组的容量固定,当顺序表满时,需要进行扩容。扩容的思路是创建一个更大的数组,将原数组的元素复制过去。

// 扩容操作
private void resize(int newCapacity) {
    int[] newData = new int[newCapacity];
    for (int i = 0; i < size; i++) {
        newData[i] = data[i];
    }
    data = newData;
    capacity = newCapacity;
}

// 修改插入方法,支持自动扩容
public void insert(int index, int element) {
    if (index < 0 || index > size) {
        throw new IndexOutOfBoundsException("插入位置不合法");
    }
    if (isFull()) {
        resize(capacity * 2); // 扩容为原来的两倍
    }
    // 元素后移
    for (int i = size - 1; i >= index; i--) {
        data[i + 1] = data[i];
    }
    data[index] = element;
    size++;
}

六、顺序表使用泛型适应多种类型数据

为了使顺序表适应多种类型的数据,可以使用 Java 的泛型。

public class SequenceList<T> {
    private T[] data;      // 存储元素的数组
    private int size;      // 当前元素数量
    private int capacity;  // 顺序表容量

    // 初始化顺序表
    @SuppressWarnings("unchecked")
    public SequenceList(int capacity) {
        this.capacity = capacity;
        data = (T[]) new Object[capacity];
        size = 0;
    }

    // 判空
    public boolean isEmpty() {
        return size == 0;
    }

    // 判满
    public boolean isFull() {
        return size == capacity;
    }

    // 插入元素
    public void insert(int index, T element) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("插入位置不合法");
        }
        if (isFull()) {
            resize(capacity * 2);
        }
        for (int i = size -1; i >= index; i--) {
            data[i +1] = data[i];
        }
        data[index] = element;
        size++;
    }

    // 删除元素
    public void delete(int index) {
        if (isEmpty()) {
            throw new RuntimeException("顺序表为空,无法删除元素");
        }
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("删除位置不合法");
        }
        for (int i = index; i < size -1; i++) {
            data[i] = data[i +1];
        }
        size--;
        // 缩容(可选)
        if (size > 0 && size == capacity / 4) {
            resize(capacity / 2);
        }
    }

    // 修改元素
    public void update(int index, T element) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("修改位置不合法");
        }
        data[index] = element;
    }

    // 获取元素
    public T get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("获取位置不合法");
        }
        return data[index];
    }

    // 遍历顺序表
    public void traverse() {
        for (int i = 0; i < size; i++) {
            System.out.print(data[i] + " ");
        }
        System.out.println();
    }

    // 扩容/缩容
    @SuppressWarnings("unchecked")
    private void resize(int newCapacity) {
        T[] newData = (T[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
        capacity = newCapacity;
    }
}

七、测试顺序表

编写一个测试类,验证顺序表的各项功能。

public class SequenceListTest {
    public static void main(String[] args) {
        SequenceList<Integer> list = new SequenceList<>(5);

        // 插入元素
        list.insert(0, 10);
        list.insert(1, 20);
        list.insert(2, 30);
        list.insert(1, 15);
        list.insert(3, 25);
        list.insert(5, 35); // 触发扩容
        System.out.print("插入元素后:");
        list.traverse();

        // 删除元素
        list.delete(2);
        System.out.print("删除索引为 2 的元素后:");
        list.traverse();

        // 修改元素
        list.update(3, 100);
        System.out.print("修改索引为 3 的元素后:");
        list.traverse();

        // 获取元素
        int element = list.get(2);
        System.out.println("索引为 2 的元素为:" + element);

        // 判空
        System.out.println("顺序表是否为空:" + list.isEmpty());

        // 判满
        System.out.println("顺序表是否已满:" + list.isFull());
    }
}

运行结果

插入元素后:10 15 20 25 30 35 
删除索引为 2 的元素后:10 15 25 30 35 
修改索引为 3 的元素后:10 15 25 100 35 
索引为 2 的元素为:25
顺序表是否为空:false
顺序表是否已满:false

  • 顺序表的基本概念:顺序表利用连续的内存空间存储数据,支持快速的随机访问。
  • 初始化和判空判满:在初始化时指定容量,判空判满通过 sizecapacity 比较实现。
  • 增加与遍历操作:在指定位置插入元素时,需要将后续元素后移;遍历时依次访问每个元素。
  • 删除与修改操作:删除指定位置元素需要将后续元素前移;修改操作直接更新指定位置的值。
  • 扩容操作:当顺序表满时,通过创建更大的数组并复制元素来实现扩容。
  • 使用泛型适应多种类型数据:通过泛型,使顺序表可以存储任意类型的数据,提高了通用性。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

相关阅读更多精彩内容

友情链接更多精彩内容