一文学懂数据结构系列之:顺序表
Posted 一头小山猪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文学懂数据结构系列之:顺序表相关的知识,希望对你有一定的参考价值。
写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成,愿将昔日所获与大家交流一二,希望对学习路上的你有所助益。同时,博主也想通过此次尝试打造一个完善的技术图书馆,任何与文章技术点有关的异常、错误、注意事项均会在末尾列出,欢迎大家通过各种方式提供素材。
- 对于文章中出现的任何错误请大家批评指出,一定及时修改。
- 有任何想要讨论和学习的问题可联系我:zhuyc@vip.163.com。
- 发布文章的风格因专栏而异,均自成体系,不足之处请大家指正。
一文学懂数据结构系列之:顺序表
本文关键字:数据结构、基本结构、线性表、顺序表、结构实现
文章目录
一、什么是数据结构
本专栏为《手撕算法》栏目的子专栏:《数据结构》,会讲述一些基础及经典的数据结构,并对常规操作进行实现。在此之前需要先了解数据与结构之间的关系,各自有什么样的特点。
1. 数据与数据结构
- 数据
数据是信息的载体,在计算机领域中,可以认为所有能够被输入到计算机,并且能够被计算机处理的符号都可以被称为数据。可以是字符串、音频、视频等各数据形式,本专栏主要讨论数值数据(结合各数据结构)。
- 数据元素
数据元素就是数据中的每个个体,是组成数据(集合)的基本单位。有些时候数据元素当中包含的数据并不是单一的,所以我们可以把数据元素看成一条数据或是某个数据结构中的一个节点。
- 数据项
数据项就是数据元素的组成部分,是其中具体的细项。数据项可以分为简单数据项和组合数据项两种,如姓名、年龄等不可再分割的描述属于简单数据项;对于日期等数据项还可以被分为更小的数据项,属于组合数据项。
- 数据对象
数据对象是性质相同的数据元素的集合,通常作为要处理数据的统称,如:整数、正整数等,也可以是符合自定义规则的数据,如具有相同数据维度的数据表等。
- 数据结构
数据结构可以用于刻画和描述数据元素之间的关系,如:线性、树形等。同时,也可以看做是符合一种或多种特定关系的数据元素的集合。
2. 逻辑结构
逻辑结构是用于描述各个数据元素之间的逻辑关系,主要描述数据元素的组织形式,包含元素间相邻的个数,开始结点、终端结点、前驱、后继等。其中:集合、树、图也可统称为非线性结构。
- 松散结构(集合)
如果数据元素之间除了属于同一个集合(即具备某个相同性质)外,再没有其他的关系,可以称这种关系为松散性的(不作为研究的重点)。
- 线性结构
线性结构中的数据元素之间存在一对一关系。在非空情况下,有且仅有一个开始结点和终端结点。并且开始结点没有前驱,有一个后继;终端结点没有后继,但有一个前驱;其余结点有且仅有一个前驱和后继。
- 树形结构
树形结构中的数据元素之间存在一对多关系。在非空情况下,有一个称为根的结点,此结点无前驱结点;其余结点有且仅有一个前驱,但可以有多个后继(没有后继的结点称之为叶子结点)。
- 图形结构
图形结构中的数据元素之间存在多对多关系。在非空情况下,任何结点都可能有多个前驱和多个后继。
3. 存储结构
数据的存储结构是数据的逻辑结构在计算机中的实现,说白了就是具体是如何存储的。由于在计算机中最小的数据表示单位是二进制位(一个bit),所以数据元素的值都是以二进制方式存储的(一个值可能会对应多个二进制位),而数据元素之间逻辑关系的表示主要有以下4种方式。
- 顺序存储
顺序存储是将所有数据元素存放在一片连续的存储空间中,使逻辑上相邻的数据元素在物理位置上也相邻。
- 链式存储
链式存储不要求逻辑上相邻的数据元素在存储时物理位置也相邻,可以存储在任意位置,但是需要由两部分组成:数据元素本身和指针(用于记录相邻元素的位置)。
- 索引存储
所以存储在存储元素的同时还增加了一个索引表。通常会在索引表中记录数据元素中的关键数据项的值,称为关键字(能够唯一标识数据元素),除此之外还会存储对应的存储地址(可以在按规则排序时使用)。
- 散列存储
散列存储也称哈希存储。该方式将开辟一片连续的存储空间,在该区域中存储数据元素的关键字,存储的位置根据预先设定的哈希函数来计算。
4. 数据操作
数据操作就是对数据进行某种处理,以达到预期的效果,也被称为数据的运算。在确定了数据结构后,通常会对其中的数据元素进行如下操作:
- 创建
对于数据结构的初始化操作,会根据预先定义好的结构创建出一个可操作的实例。
- 销毁
将已创建出的存储结构进行回收,进行空间的释放(通常交由计算机控制,不作为研究重点)。
- 插入
向数据结构中的某个位置上插入一个指定的新数据元素。
- 删除
从数据结构中删除某个符合条件的数据元素,并保持数据结构的性质。
- 查找
在数据结构中查找满足条件的数据元素。
- 修改
修改数据结构中指定数据元素的值。
- 遍历
对数据结构中的每一个数据元素按某种方式或路径访问一次,并且仅访问一次。
二、线性表
1. 线性表介绍
线性表是由n个数据元素构成的有限序列,其中n为线性表的长度,当n=0时,线性表为空表。线性表中的数据均是同一类型,并且数据元素之间具有线性或一对一的逻辑关系。
- 第一个元素没有前驱,称为开始结点。
- 最后一个元素没有后继,称为终端结点。
- 其他元素有且仅有一个前驱和后继。
- 顺序表
线性表的顺序存储:顺序存储的线性表,用一片地址连续的存储单元依次存放线性表中的各个数据元素。
- 链表
线性表的链式存储:链式存储的线性表,链表中每一个结点包含数据域和指针域两部分。其中数据域用于存放数据元素的值,指针域用于存放相邻元素的位置信息。
2. 顺序表
- 逻辑上相邻的元素,在物理存储上也相邻。
- 存储密度高,需要预先分配对应长度的空间。
- 便于随机存取,可以根据地址关系直接获取到对应位置的元素。
- 不利于插入和删除,为了保证线性关系,需要对其他元素进行串位。
三、结构实现
1. 接口定义
使用Java语言实现,在接口中先定义需要具备的功能(基于线性表):
/**
* 线性表功能接口
*/
public interface IList {
/**
* 将线性表置为空表
*/
public void clear();
/**
* 查看线性表是否为空
* @return true:空 false:非空
*/
public boolean isEmpty();
/**
* 查看线性表长度
* @return 当前数据元素个数
*/
public int length();
/**
* 返回指定位置上的数据元素,如果索引超出范围,则引发越界异常
* @param i 索引位置
* @return 对应位置的数据元素
*/
public Object get(int i);
/**
* 在指定位置之前插入一个数据元素,如果索引超出范围,则引发越界异常
* @param i 索引位置
* @param e 数据元素
*/
public void insert(int i, Object e) throws Exception;
/**
* 删除并返回对应位置上的数据元素,如果索引超出范围,则引发越界异常
* @param i 索引位置
* @return 数据元素
*/
public Object remove(int i) throws Exception;
/**
* 返回待查找数据元素在线性表中第一次出现的位置,若不存在返回-1
* @param e 数据元素
* @return 索引位置
*/
public int indexOf(Object e);
/**
* 遍历输出顺序表中的所有数据元素
*/
public void show();
}
2. 功能实现
使用Java代码对线性表进行顺序存储实现,如下:
/**
* 线性表的顺序存储实现:顺序表
*/
public class SequenceList implements IList {
private Object[] elements;// 线性表存储空间
private int currentLength;// 线性表当前长度
/**
* 创建一个具有指定存储空间大小的顺序表
* @param size 存储空间大小
*/
public SequenceList(int size){
elements = new Object[size];
currentLength = 0;// 线性表中最初数据元素为0
}
@Override
public void clear() {
currentLength = 0;
}
@Override
public boolean isEmpty() {
return currentLength == 0;
}
@Override
public int length() {
return currentLength;
}
@Override
public Object get(int i) {
if (i < 0 || i > currentLength - 1){
throw new IndexOutOfBoundsException("索引下标越界");
}
return elements[i];
}
@Override
public void insert(int i, Object e) throws Exception {
if (currentLength == elements.length){
throw new Exception("当前顺序表已满");
}
if (i < 0 || i > currentLength){
throw new IndexOutOfBoundsException("索引下标越界");
}
for (int j = currentLength; j > i; j--) {
elements[j] = elements[j - 1];
}
elements[i] = e;
currentLength++;
}
@Override
public Object remove(int i) {
if (i < 0 || i > currentLength - 1){
throw new IndexOutOfBoundsException("索引下标越界");
}
Object e = elements[i];
for (int j = i; j < currentLength - 1; j++) {
elements[j] = elements[j + 1];
}
currentLength--;
return e;
}
@Override
public int indexOf(Object e) {
int j = 0;
while (j < currentLength && !elements[j].equals(e)){
j++;
}
if (j < currentLength){
return j;
}else {
return -1;
}
}
@Override
public void show() {
for (int i = 0; i < currentLength; i++) {
System.out.print(elements[i] + "\\t");
}
System.out.println();
}
}
3. 调用测试
使用测试程序完成对该数据结构的测试,如下:
public class SequenceListTest {
public static void main(String[] args) throws Exception {
// 使用线性表对顺序表进行实现,初始化一个长度为5的空间
IList list = new SequenceList(5);
// 添加3个元素
list.insert(0,10);
list.insert(1,20);
list.insert(2,30);
// 查看第1个位置的元素
System.out.println("第1个位置的元素为:" + list.get(0));
// 查找值为20的数据元素
System.out.println("元素20第一次出现的位置是" + list.indexOf(20));
// 删除第三个位置的数据元素
System.out.println("元素" + list.remove(2) + "已被成功删除");
// 遍历顺序表
list.show();
// 查看当前数据元素个数
System.out.println("当前数据元素个数为:" + list.length());
// 清空顺序表
list.clear();
// 查看顺序表是否为空
System.out.println(list.isEmpty() ? "顺序表为空" : "顺序表非空");
}
}
扫描下方二维码,加入官方粉丝微信群,可以与我直接交流,还有更多福利哦~
以上是关于一文学懂数据结构系列之:顺序表的主要内容,如果未能解决你的问题,请参考以下文章