数据结构之线性结构
在数据结构概述 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中关于线性结构有简单却形象的介绍,数据元素之间一对一的关系:
在线性结构中,有且仅有一个元素被称为“第一个”,除第一个元素之外其他元素均有唯一一个“前驱”;有且仅有一个元素被称为“最后一个”,除最后一个元素之外其他元素均有唯一一个“后继”。线性表示最简单、基础也是最常用的线性结构,本文重点描述线性表的顺序存储和链式存储。
什么是线性表呢?由同一类型的数据元素构成的线性结构——具有相同数据类型的n个数据元素的有限序列通常记为。其中,n为表长,当n=0时称为空表。
线性表的基本运算有哪些呢?
1)线性表的初始化:Init_List(L),操作结果是构造一个空的线性表。
2)求线性表的长度:Length_List(L),操作结果是返回线性表中所含元素的个数。
3)取表元素:Get_List(L,i),表L存在且在Length_List(L)内,操作结果是返回线性表L中的第i个元素的值或地址。
4)按值查找:Locate_List(L,x),x是给定的一个数据元素。线性表L存在,操作结果是在表L中查找值为x的数据元素,其结果返回在L中首次出现的值x的哪个元素的序号或地址,称为查找成功;否则,在L中未找到值为x的数据元素返回一个特殊值表示查找失败。
5)插入操作:Insert_List(L,i,x)。线性表L存在,插入位置不超过表长;操作结果是在线性表L的第i个位置上插入一个值为x的新元素,这样使i原来位置及其后兄嘚数据元素的序号均+1;插入后表长为原表长+1。
6)删除操作:Delete_List(L,i)。线性表L存在,删除表长内位置i的数据元素。操作结果是在线性表L中删除序号为i的数据元素,删除后使原i序号及其后位置元素序号均-1,新表长为原表长-1。
以上为线性表逻辑上的基本运算,用Java语言可以用接口(interface)的形式定义线性表的ADT中的共有方法,如下:
一、线性表的顺序存储与实现
线性表的顺序存储是指内存中用地址连续的一块存储空间顺序存放线性表的各元素,用这种存储形式存储的线性表为顺序表。因为内存中的地址空间是线性的,因此用物理上相邻实现数据元素之间的逻辑相邻关系是既简单又自然的。存储关系如下图:
数据结构的运算是定义在逻辑结构定义上,而运算的具体实现是建立在存储结构上,因此定义的线性表的基本运算作为逻辑结构的一部分,每一个操作的具体实现只有在确定了线性表的存储结构之后才能完成。
可以定义一个顺序表类SeqList,将数据存储区data、位置last与顺序表中的基本操作进行封装,作为对抽象数据类型接口IList的实现,如下:
二、线性表的链式存储与实现
链表是通过一组任意的存储单元来存储线性表中的数据元素,D解决了那么R呢?怎么表示出数据元素之间的线性关系呢?为建立起数据元素之间的线性关系,对每个数据元素除了存放数据元素的自身信息之外,还需要存放相邻数据元素存储单元的地址——这两部分组成一个“结点”。根据存储单元地址分为单链表、双向链表、循环链表。
1、单链表
单链表中“结点”,如:。data域称为数据域,用来存放数据元素的信息;next域称为引用域,用来存放其后继元素对象的地址信息。n个元素的线性表可以通过每个结点的引用域拉成一个“链”,因为每个结点中只有一个指向后继的引用所以称为单链表。
结点ADT可以定义如下:
通常,用“表头引用”来标识一个单链表,如单链表L、单链表H等是指某链表的第一个结点的地址被记录在引用变量L、H中,当其为“NULL”时则表示一个空表。定义一个链表类LinkList,将链表中结点的存储与链表中的基础操作封装在一起作为对ADT接口IList的实现,如下:
则申请一个LinkNode类的实例的语句为:,执行的结果是将一个LinkNode类的实例地址赋值给变量p。图解如下:
2、循环链表
对于单链表而言,最后一个结点的引用域是空不指向任何结点对象。如果将该链表的表头结点对象植入该引用域则使得链表头尾结点相连就构成了单循环链表,如下图示:
单循环链表上的操作基本上与非循环链表相同,唯一的区别只是在判断线性表是否到达末尾时将原来判断引用域是否为NULL变为是否是表头对象而已。
3、双向链表
单链表的节点中只有一个指向其后继结点的引用域next,因此若已知某节点的引用为p,其后继结点的引用则为p.next,而找其前驱则只能从该链表的表头开始,顺着各结点的next域进行。为了节省找前驱的时间,则只能付出空间的代价:每个结点再加一个指向前驱的引用域,结点的结构如下图:
,其ADT定义如下:
用这种结点组成的链表称为双向链表。与单链表类似,双向链表通常也是用指向表头的引用变量标识,也可以带头结点和做出循环结构。如下图示带头结点的双向循环链表:
以上数据结构涉及基础操作后续单独行文总结。