线性表的链式存储结构

来源:互联网 发布:ubuntu 没有deb命令 编辑:程序博客网 时间:2024/05/16 08:44

链式存储结构是基于指针(Java中用对象的引用来表示指针)实现的,存储方式是给每个数据元素分配一个指针封装到一起,然后用“链子”连接,所以,该结构的特点是数据元素间的逻辑关系表现在结点的链接关系上,它的一个直观的优点就是:数据元素在内存上不一定非要在一片连续的地址上存储。

链表主要有单链表、单循环链表和循环双向链表三种。

和顺序表一样,只要是线性表肯定方法是共通的只是实现方式不一样,所以我们还是要先定义线性表的基本操作(如增删改查):

//建立接口并逐一实现//接口中只有常量(通过final修饰)和抽象方法,即接口是一个抽象类,那么其所有的方法必须被全覆盖。//类要实现接口来复写,接口中所有成员都是public的。interface List{public abstract boolean isEmpty();//接口中public abstract 可忽略不写。int getSize();//boolean contains(Object e);Object getData(int i)throws Exception;//int indexOf(Object e);void insert(int i,Object e)throws Exception;Object delete(int i)throws Exception;//boolean remove(Object e);}

先实现单链表:

单链表有带头结点结构和不带头结点结构,看图:


图上很清楚就看出区别了,我再举个栗子帮助记忆。我们假设有一个旅游团,游客就是我们的数据元素,每个游客都是一个结点,为了确保安全,我们会给每个游客排序编号,那么编号就是指针。每个旅游团都会有相应的小旗帜告诉大家我们是属于哪个团的以防走错,那么小旗帜就是头指针(head),当然还有导游,即为头结点。在观赏某一景点是可以是导游举着旗子带领大家组成一个带头结点链表,若导游有重要事情比如去办理票务,那么可以由排头的游客(第一个数据元素结点)举着旗子暂时管理,这就是不带头结点的链表。糊涂了,糊涂了~大笑

回正题:带头结点的好处就是,当你需要在第一个数据元素结点前插入一个新结点时,只需改变头结点的指针指向就行,不用修改头指针head的值。所以,它的插入和删除操作相对简单。

实现链表就必须先定义结点,代码如下:

//先定义节点类class Node{Object data;//数据元素Node next;//表示下一个节点的对象引用,即指针Node(Node nextval){  //头结点构造函数next = nextval;}Node(Object obj,Node nextval){  //其他结点构造函数data = obj;next = nextval;}public Node getNext(){return next;}public void setNext(Node nextval){next = nextval;}public Object getElement(){return data;}public void setElement(Object obj){data = obj;}public String toString(){   //转换data为string类型return data.toString();}}

接下来我们就可以定义单链表类并进行简单操作了,单链表类的成员变量至少要有头指针和数据元素个数,如果有当前结点位置就更完美了,代码如下:

//建立单链表类 实现接口并复写功能class LinList implements List{Node head;//头指针Node current;//当前结点位置int size;LinList(){  //初始化结点,创建头结点。head = current = new Node(null);size = 0;}public void index(int i)throws Exception{//定位if(i<-1 || i>size-1){throw new Exception("参数错误");}if(i == -1) return;current = head.next;int j = 0;while((current!=null) && j<i){current = current.next;j++;}}public void insert(int i,Object obj)throws Exception{//插入if(i<0 || i>size){throw new Exception("参数错误");}index(i-1);current.setNext(new Node(obj,current.next));size++;}public Object delete(int i)throws Exception{//删除if(size == 0){throw new Exception("链表已空无元素可删除");}if(i<-1 || i>size-1){throw new Exception("参数错误");}index(i-1);Object obj = current.next.getElement();current.setNext(current.next.next);size--;return obj;}public Object getData(int i)throws Exception{//取数据元素if(i<-1 || i>size-1){throw new Exception("参数错误");}index(i);return current.getElement();}public int getSize(){//取数据个数return size;}public boolean isEmpty(){//是否空return size == 0;}}public class LinListDemo {public static void main(String[] args) {LinList linList = new LinList();int n = 10;try{for(int i=0;i<n;i++){linList.insert(i, new Integer(i+1));}for(int i=0;i<linList.size;i++){System.out.print(linList.getData(i)+" ");}System.out.println();linList.delete(4);linList.insert(5, new Integer(22));for(int i=0;i<linList.size;i++){System.out.print(linList.getData(i)+" ");}}catch(Exception e){System.out.println(e.getMessage());}}}/*运行结果:1 2 3 4 5 6 7 8 9 10 1 2 3 4 6 22 7 8 9 10 */

链表优点:插入和删除操作时不需要移动数据元素,节省时间。

缺点:除了数据元素还要储存指针,所以空间利用率低于顺序表,且不支持随机读取,就是说不能根据下标(没有)取元素,只能从头结点遍历。


循环单链表:就是把最后一个结点的指针不置空而是指向整个链表的第一个结点(头结点或第一元素结点),形成环状结构。

带头结点的循环单链表的操作实现方法和单链表的两点差异:

(1)构造函数中要加上head.next=head形成环状。

(2)在index(i)中循环结束判断为current != head。


双向链表:在每个结点中增加了一个前驱指针prior用以指向前驱结点,可以有效查找前结点而不必从头结点开始遍历。

双向链表也分为带和不带头结点,循环和不循环结构。

双向链表的操作麻烦~~


仿真链表:引入了仿真指针这个东西,简单的就是用一个n行2列数组存放,第一列存数据元素,第二列存下标用来表示链表中数据元素的前后关系。

这种仿真方法也可存放树和图等结构,以后再讨论。

0 0
原创粉丝点击