链表
更多内容,前往 IT-BLOG
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。链表是有序的列表,但是它在内存中是存储如下:
一、单链表介绍
【1】一种最简单的结点结构如图所示,它是构成单链表的基本结点结构。在结点中数据域用来存储数据元素,指针域用于指向下一个具有相同结构的结点。因为只有一个指针结点,称为单链表:
【3】在单链表结构中还需要注意的一点是,由于每个结点的数据域都是一个 Object 类的对象,因此,每个数据元素并非真正如图中那样,而是在结点中的数据域通过一个 Object类的对象引用来指向数据元素的。与数组类似,单链表中的结点也具有一个线性次序,即如果结点 P 的 next 引用指向结点 S,则 P 就是 S 的直接前驱,S 是 P 的下一个节点。单链表的一个重要特性就是只能通过前驱结点找到下一个结点,而无法从后续结点找到前驱结点。
二、单链表的查询、添加、修改、删除操作
【1】查询操作:在单链表中进行查找操作,只能从链表的首结点开始,通过每个结点的 next 引用来一次访问链表中的每个结点以完成相应的查找操作。例如需要在单链表中查找是否包含某个数据元素 e,则方法是使用一个循环变量 p,起始时从单链表的头结点开始,每次循环判断 p所指结点的数据域是否和 e 相同,如果相同则可以返回 true,否则让p指向下一个节点,继续循环直到链表中所有结点均被访问,此时 p 为 null;后续代码会实现。
关键操作:① 起始条件:p = head;② 结束条件:找到(e.equals(p.getData())==true)未找到( p == null)③ p指向下一个结点:p = p.getNext();
缺点:逐个比较,频繁移动指针,导致效率低下;
【2】修改操作:在单链表中修改数据,属于增删改查中最简单的操作,通过传入需要修改的节点对象,从链表的 head节点向下遍历,如果修改的节点编号NO等于链表中的某个编号NO则将链表中的节点修改为传入的节点即可。后续代码会实现。
【3】添加操作:在单链表中数据元素的插入,是通过在链表中插入数据元素所属的结点来完成的。对于链表的不同位置,插入的过程会有细微的差别。中间、末尾的添加过程其实是一样的,关键是在首部添加,会有不同,会改变整个单链表的起始结点。后续代码会实现。
关键操作:以添加中间结点为例:① 指明新结点的后继 s.setNext(p.getNext());或者 s.next = p.next;② 指明新结点的前驱(其实是指明前驱结点的后继是新结点) p.setNext(s);或者 p.next = s;
优缺点:添加节点不需要移动元素,只需要修改元素的指针即可,效率高。但是如果需要先查询到添加位置再添加新元素,因为有逐个查询的过程,效率不高。
【4】删除操作:类似添加操作,创建一个临时节点(temp)向下遍历,判断其 next节点是否等于需要删除的节点。如果等于则将临时的next节点指向 next节点的next节点。此时需要删除的节点就没有对象引用,JVM进行垃圾回收GC的时候则会回收此对象。后续代码会实现。
在使用单链表实现线性表的时候,为了使程序更加简洁,我们通常在单链表的最前面添加一个哑元结点,也称为头结点。在头结点中不存储任何实质的数据对象,其 next 域指向线性表中 0 号元素所在的结点,可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便,常用头结点。一个带头结点的单链表实现线性表的结构图如图所示:
三、单链表代码实例
四、双向链表
单向链表的缺点:【1】单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
【2】单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp是待删除节点的前一 个节点;
与单向链表的区别:主要却别就是双向列表多了一个向前的指针 pre,能够通过当前节点,查看自己的上一个节点。方法的实现与单链表基本相同,就是由原来的只考虑 next 节点的情况,改变成了需要考虑 next 和 pre 两个节点而已;