算法之旅(三) 顺序表

顺序表的基本概念及实现

顺序表(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 比较实现。
  • 增加与遍历操作:在指定位置插入元素时,需要将后续元素后移;遍历时依次访问每个元素。
  • 删除与修改操作:删除指定位置元素需要将后续元素前移;修改操作直接更新指定位置的值。
  • 扩容操作:当顺序表满时,通过创建更大的数组并复制元素来实现扩容。
  • 使用泛型适应多种类型数据:通过泛型,使顺序表可以存储任意类型的数据,提高了通用性。

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容