Java数据结构顺序表从零基础到精通进阶

 更新时间:2022年03月23日 10:38:35   作者:来自爪哇的bean  
程序中经常需要将一组数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化,顺序表则是将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示

一、什么是线性表

线性表是最基本、最简单、也是最常用的一种数据结构。线性表*(linear list)*是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。常见的线性表有顺序表,链表,栈,队列,字符串等

注意:这里说的线性表都指的是逻辑结构,也就是他们的逻辑结构是线性的,但物理结构却不一定是线性的。

在数据结构逻辑层次上细分,线性表可分为一般线性表和受限线性表。一般线性表也就是我们通常所说的“线性表”,可以自由的删除或添加结点。受限线性表主要包括栈和队列,受限表示对结点的操作受限制

二、顺序表

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中

顺序表可以分为以下两类:

  • 静态顺序表:通过定长数组实现
  • 动态顺序表:数组长度可动态增长

静态顺序表比较死板,如果数组长度太小,担心后面数据存不下,太大,又会有空间浪费.

所以我们一般都用动态增长的顺序表,按需所取.

三、手撕顺序表

在学习数据结构的过程中,我们不仅要学会如何用数据结构,懂得它的理论部分,更要亲自动手去实践,将数据结构实现一遍,这样的话理解会更深刻

顺序表中我们如果只是单纯拿一个数组去用的话就会出现:顺序表中到底有多少有效数据,满了还是没满等问题,所以我们在实现顺序表的时候都还会再加一个size(有效数据个数)属性(顺序表的容量可以通过data.length获得)

属性定义

public class MyArrayList {
    public int[] data;    // 存储数据
    public int size;    // 有效数据个数
}

构造方法

public MyArrayList() {
    this.data = new int[10];   // 后面不够再增容
    this.size = 0;    // 初始无有效数据,size 为0
}

接口实现

对于顺序表我们一般都会有以下接口需要去实现:

// 打印顺序表
public void display();
// 在 pos 位置增加元素
public void add(int pos, int num);
// 判断顺序表中是否包含某个元素
public boolean contains(int num);
// 查找某个元素所在位置
public int search(int key);
// 获取 pos 位置的元素
public int getPos(int pos);
// 将 pos 位置的元素值设为 value
public void setPos(int pos, int value);
//删除第一次出现的关键字key
public void remove(int key);
// 获取顺序表长度
public int size();
// 清空顺序表
public void clear();

在实现这些接口的过程中,凡是涉及到数组下标的,我们都要进行下标合法性的检验,并且要注意用size,还是data.length

确保顺序表空间

在对顺序表增加元素的时候,我们一定要确保顺序表有足够的空间去增加元素,否则就会导致数组下标越界等情况发生

private void ensureCapacity() {
    if (this.size == this.data.length) {
        // 说明满了,该扩容了
        this.data = Arrays.copyOf(this.data, 2 * this.data.length);
    }
}

增加元素

往顺序表中增加元素的时候要注意可以在size位置去加元素(相当于尾插),同时也要确保顺序表有空间足够插入,在移动元素的时候要注意边界情况(下标越界,移动元素过多等),也别忘了size++

public void add(int pos, int num) {
    if (pos < 0 || pos > this.size) {    // 检验下表合法性
        throw new RuntimeException("ArrayIndexOutOfBoundsException : " + pos);
    }
    ensureCapacity();
    for (int i = this.size; i > pos; i--) {
        this.data[i] = this.data[i - 1];
    }
    this.data[pos] = num;
    this.size++;
}

打印顺序表

注意这里只打印有效数据

public String printMyArrayList() {
    String str = "[";
    for (int i = 0; i < this.size - 1; i++) {   // for循环中的右边界应该用size而不用data.length
        str += this.data[i] + ", ";
    }
    str += this.data[this.size - 1];
    str += "]";
    return str;
}

测试

对前面写的三个接口做测试

public class UseArrayList {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(0, 0);
        myArrayList.add(1, 1);
        myArrayList.add(2, 2);
        myArrayList.add(3, 3);
        myArrayList.add(4, 2);
        myArrayList.add(0, 100);   // 头插
        myArrayList.add(2, 666);   // 中间插
        System.out.println(myArrayList.printMyArrayList());
    }
}

运行结果:

判断顺序表中是否包含某个元素

public boolean contains(int num) {
    for (int i = 0; i < this.size; i++) {
        if (this.data[i] == num) {
            return true;
        }
    }
    return false;
}

测试:

public class UseArrayList {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(0, 0);
        myArrayList.add(1, 1);
        myArrayList.add(2, 2);
        myArrayList.add(3, 3);
        System.out.println(myArrayList.contains(3));
        System.out.println(myArrayList.contains(2));
        System.out.println(myArrayList.contains(0));
        System.out.println(myArrayList.contains(666));
    }
}

运行结果:

查找元素

查找顺序表中某个元素的位置(返回下标)

public int search(int key) {
    for (int i = 0; i < this.size; i++) {
        if (this.data[i] == key) {
            return i;
        }
    }
    return -1;    // 不存在这个元素,返回-1
}

测试:

public class UseArrayList {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(0, 0);
        myArrayList.add(1, 1);
        myArrayList.add(2, 2);
        myArrayList.add(3, 3);
        System.out.println(myArrayList.search(0));
        System.out.println(myArrayList.search(3));
        System.out.println(myArrayList.search(666));
    }
}

获取 pos 位置的元素

注意:size位置为无效元素

public int getPos(int pos) {
    if (pos < 0 || pos >= this.size) {
        throw new RuntimeException("ArrayIndexOfBoundsException : " + pos);
    }
    return this.data[pos];
}

测试:

public class UseArrayList {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(0, 0);
        myArrayList.add(1, 1);
        myArrayList.add(2, 2);
        myArrayList.add(3, 3);
        System.out.println(myArrayList.getPos(0));
        System.out.println(myArrayList.getPos(3));
        System.out.println(myArrayList.getPos(1));
        System.out.println(myArrayList.getPos(6));
    }
}

将 pos 位置的元素值设为 value

这里不涉及元素的移动

public void setPos(int pos, int value) {
    if (pos < 0 || pos >= this.size) {     // size位置为无效元素
        throw new RuntimeException("ArrayIndexOfBoundsException : " + pos);
    }
    this.data[pos] = value;
}

测试:

public class UseArrayList {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(0, 0);
        myArrayList.add(1, 1);
        myArrayList.add(2, 2);
        myArrayList.add(3, 3);
        myArrayList.setPos(0, 666);
        myArrayList.setPos(3, 777);
        System.out.println(myArrayList.printMyArrayList());
    }
}

删除第一次出现的关键字key

注意在删除的时候的数组边界以及改变size

public void remove(int key) {
    int pos = search(key);
    if (pos == -1) {
        return;        // 若是这个数字不存在,则返回
    }
    for (int i = pos; i < this.size - 1; i++) {
        this.data[i] = this.data[i + 1];      // 从后往前挪,直接将要删除的数字覆盖掉
    }
    this.size--;
}

获取顺序表长度

public int size() {
    return this.size;
}

清空顺序表

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

删除所有的key

如果我们想要删除顺序表中所有的key,如何做?

法一:我们可以将第一次出现的key删除完之后再继续搜索若有,则删除,没有则删除完毕

public void removeAll(int key) {
    for (int i = 0; i < this.size; i++) {
        if (this.search(key) != -1) {
            remove(key);
        } else {
            return;
        }
    }
}

法二:我们可以转变以下思路,不直接删除key,而是重新创建一个数组,将源数组中不是key的值复制到新数组,再让原数组的引用指向新数组,间接完成删除操作

public void removeAllPlus(int key) {
    int[] newData = new int[this.data.length];    // 新数组长度应该和源数组长度相同
    int j = 0;
    for (int i = 0; i < this.size; i++) {
        if (this.data[i] != key) {
            newData[j] = this.data[i];
            j++;
        }
    }
    this.data = newData;
    this.size = j;
}

注意:在元素复制完之后要改变源数组的引用,并改变顺序表的size

法三:我们既然可以通过复制的方式实现间接删除操作,那么我们可以想着将原数组就当成目标数组,即两个指针,一个数组

public void removeAllPlusPlus(int key) {
    int dest = 0;
    for (int src = 0; src < this.size; src++) {
        if (this.data[src] != key) {
            this.data[dest] = this.data[src];
            dest++;
        }
    }
    this.size = dest;
}

到此这篇关于Java数据结构顺序表从零基础到精通进阶的文章就介绍到这了,更多相关Java 顺序表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springmvc 参数绑定总结

    springmvc 参数绑定总结

    本篇文章主要介绍了详解springmvc 参数绑定,详细的介绍了springmvc各种参数绑定的情况,具有一定的参考价值,有兴趣的可以了解一下。
    2017-03-03
  • Java中静态代码块、构造代码块、构造函数和普通代码块的区别

    Java中静态代码块、构造代码块、构造函数和普通代码块的区别

    在Java中,静态代码块、构造代码块、构造函数、普通代码块的执行顺序是一个笔试的考点,通过这篇文章希望大家能彻底了解它们之间的执行顺序,需要的朋友可以参考下
    2023-05-05
  • 详解Spring Boot使用Maven自定义打包方式

    详解Spring Boot使用Maven自定义打包方式

    这篇文章主要介绍了Spring Boot使用Maven自定义打包方式,本文通过多种方式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • 浅谈HashMap中7种遍历方式的性能分析

    浅谈HashMap中7种遍历方式的性能分析

    本文先从HashMap的遍历方法讲起,然后再从性能、原理以及安全性等方面,来分析HashMap各种遍历方式的优势与不足
    2021-06-06
  • Java+Selenium调用JavaScript的方法详解

    Java+Selenium调用JavaScript的方法详解

    这篇文章主要为大家讲解了java在利用Selenium操作浏览器网站时候,有时会需要用的JavaScript的地方,代码该如何实现呢?快跟随小编一起学习一下吧
    2023-01-01
  • Java 数据结构与算法系列精讲之汉诺塔

    Java 数据结构与算法系列精讲之汉诺塔

    汉诺塔是源于印度一个古老传说的益智玩具。大梵天创造世界时做了三根石柱,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,三根柱子之间一次只能移动一个圆盘
    2022-02-02
  • Springboot集成定时器和多线程异步处理操作

    Springboot集成定时器和多线程异步处理操作

    这篇文章主要介绍了Springboot集成定时器和多线程异步处理操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • 使用Java实现PDF文字识别的方法详解

    使用Java实现PDF文字识别的方法详解

    在现代信息化的社会中,PDF文件已经成为一种非常常见的文档格式,本文将详细介绍如何使用Java实现PDF文字识别,包括所需的工具、库、代码实现以及实际应用中的注意事项,需要的朋友可以参考下
    2025-02-02
  • JPA 使用criteria简单查询工具类方式

    JPA 使用criteria简单查询工具类方式

    这篇文章主要介绍了JPA 使用criteria简单查询工具类方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java构造器与传值学习总结

    Java构造器与传值学习总结

    这篇文章主要为大家详细介绍了Java构造器与传值学习总结,文中示例介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01

最新评论