java带头节点的单链表_自己实现集合框架(五):带头结点单链表的实现
这是系列文章,每篇文章末尾均附有源代码地址。目的是通过模拟集合框架的简单实现,从而对常用的数据结构和java集合有个大概的了解。当然实现没有java集合的实现那么复杂,功能也没有那么强大,但是可以通过这些简单的实现窥探到底层的一些共性原理。
一. 什么叫带头结点的单链表?
带头结点的单链表和普通的单链表差不多,只是在普通单链表的第一个结点之前增加一个特殊的结点,这个结点就叫做头结点。在带头结点的单链表中,head指向头结点。虽然头结点不是用来存储数据的,但是会占用一个存储单元,相当于牺牲一个结点的存储空间来简化单链表的操作。由于增加了头结点,使得所有单链表(包括空表)的头结点均非空,所以对单链表的插入,删除操作不需要区分该单链表是否为空表或是在第一个位置进行,能与在其他位置的插入,删除操作保持一致,这就是带头结点单链表带来的方便之处如下所示:
(a) 空链表
(b) 头插入,不改变head,不用区分插入位置,头结点后面插入元素C如下图所示:
(c) 头删除,不改变head,不用区分删除位置,删除元素c如下图所示
二. 带头结点单链表的实现
1.定义带头结点的单链表类
带头结点的单链表类HeadSinglyLinkedList声明如下,实现方式和普通单链表SinglyLinkedList差别不大,也使用单链表节点类Node。
package org.light4j.dataStructure.linearList.linkList.head;
import org.light4j.dataStructure.linearList.LList;
import org.light4j.dataStructure.linearList.linkList.Node;
/**
* 带头结点的单链表类
*
* @author longjiazuo
*/
public class HeadSinglyLinkedList implements LList {
protected Node head;// 单链表的头结点,指向单链表的头结点
protected Node rear;// 单链表的尾结点,指向单链表的最后一个结点
protected int n;// 单链表的长度
public HeadSinglyLinkedList() {// 构造空单链表
this.head = new Node(null);// 构造头结点,元素值为空
this.rear = this.head;// 构造尾结点,初始化的时候头结点和尾结点都指向头结点
this.n = 0;// 初始化链表长度为0
}
}
代码解释:
① 单链表HeadSinglyLinkedList类实现接口LList,所以必须实现该接口的相关方法,和普通单链表的方法类似的方法下面不再列举,请查看前面的单链表的实现的文章,下面会对二者不一样的方法做些说明。
② 在构造函数里面指定了头结点和尾结点,头结点的数据为空,初始化的时候,头结点和尾结点都指向头结点。
3. 判断带头结点单链表是否为空
/**
* 判断带头结点的单链表是否为空
*/
@Override
public boolean isEmpty() {
return this.head.next == null;
}
代码解释:
① 由于增加了头结点,所以判断带头结点单链表是否为空的条件是根据头结点的下一个结点是否为空作为依据。
4. 求带头结点单链表的长度
/**
* 返回带头结点的单链表长度,时间复杂度为O(1)
*/
@Override
public int length() {
return this.n;
}
代码解释:
① 由于增加了表示单链表长度的成员变量n,所以获取单链表长度直接返回n即可,操作的时间复杂度为O(1)。
5. 带头结点单链表的插入
/**
* 在指定位置插入非空的指针对象
*/
@Override
public boolean add(int index, E element) {
if (element == null) {// 不允许插入非空元素
return false;
}
if (index >= this.n) {// 尾插入,插入在最后
this.add(element);
} else {
Node p = this.head;
int i = 0;
while (p.next != null && i < index) {
i++;
p = p.next;
}
// 下面操作可以包含头插入和中间插入
Node q = new Node(element);
q.next = p.next;
p.next = q;// 将q结点插入到p结点之后
this.n++;
return true;
}
return false;
}
/**
* 在单链表的最后插入元素对象,时间复杂度是O(1)
*/
@Override
public boolean add(E element) {
if (element == null) {// 不允许插入非空元素
return false;
}
this.rear.next = new Node(element);// 尾插入
this.rear = this.rear.next;// 移动尾指针
this.n++;// 链表长度增加
return true;
}
代码解释:
① 带头结点的单链表在进行插入操作的时候不需要区分插入位置。
② 插入操作会维护单链表的长度n,插入一个元素之后n加1。
6. 带头结点单链表的删除
/**
* 移除索引index处的结点,操作成功返回被移除的对象,失败则返回null
*/
@Override
public E remove(int index) {
E old = null;
if (index >= 0) {// 头删除,中间删除,尾删除
Node p = this.head;
int i = 0;
while (p.next != null && i < index) {// 从头结点开始遍历,定位到待删除结点的前驱结点
i++;
p = p.next;
}
if (p.next != null) {
old = p.next.data;
if (p.next == this.rear) {// 如果p结点的后一个结点是尾结点,则移除之后尾结点指针前移
this.rear = p;
}
p.next = p.next.next;// 删除p结点的后继结点
this.n--;// 链表长度减少
return old;
}
}
return old;
}
代码解释
① 带头结点的单链表在进行删除操作的时候不需要区分删除位置。
② 删除操作会维护单链表的长度n,删除一个元素之后n减1。
7. 清空带头结点的单链表
/**
* 清空单链表
*/
@Override
public void clear() {
this.head.next = null;
this.rear = this.head;
this.n = 0;
}
代码解释
① 清空带头结点的单链表需要把头结点的下一个结点置为空。
② 把尾结点指向头结点。
③ 把单链表的长度置为0。
8. 重写toString()方法
@Override
public String toString() {// 返回所有元素值对应的字符串
String str = "(";
Node p = this.head.next;//不是从头结点开始,而是从头结点的下一个结点开始
while (p != null) {
str += p.data.toString();
p = p.next;
if (p != null) {
str += ", ";
}
}
return str + ")";
}
三.测试
测试代码如下所示:
package org.light4j.dataStructure.linearList.linkList.head;
import org.light4j.dataStructure.linearList.LList;
public class Test {
public static void main(String[] args) {
LList list = new HeadSinglyLinkedList();
for (int i = 0; i < 10; i++) {
list.add(0, new String((char) ('A' + i) + ""));
}
System.out.println(list.toString());
list.remove(0);//移除第一个元素
System.out.println(list.toString());
}
}
运行结果如下图所示:
四.带头结点的单链表操作效率分析
由于增加成员变量n来维护单链表的长度,所以length()操作的时间复杂度为O(1)。由于增加了尾结点rear指向单链表的最后一个结点,所以在尾结点进行插入操作的时间复杂度是O(1)。别的操作的时间复杂度和普通单链表一样,请参见之前文章单链表的实现的分析。
五.源代码示例
github地址:点击查看
码云地址:点击查看
打赏
微信扫一扫,打赏作者吧~欢迎关注人生设计师的微信公众账号
公众号ID:longjiazuoA
java带头节点的单链表_自己实现集合框架(五):带头结点单链表的实现相关推荐
- java带头结点的单链表_自己实现集合框架 (五): 带头结点单链表的实现
这是系列文章,每篇文章末尾均附有源代码地址.目的是通过模拟集合框架的简单实现,从而对常用的数据结构和java集合有个大概的了解.当然实现没有java集合的实现那么复杂,功能也没有那么强大,但是可以通过 ...
- 写出一段代码将链表中的两个节点位置互换位置_面试 leetcode 算法专题系列(二)—— 链表...
前言:只照着常考题去刷题确实是一种方法.但调研之后发现自己还是考虑不周,刷题刷的不应该是题,而是解题的思路和熟练程度.于是我决定重新组织一下刷题笔记的讲解顺序,不再以面试常考题来刷.而是以面试出题频率 ...
- java listnode 合并链表_剑指offer:合并两个排序的链表(Java)
1.问题描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 2.思路 方法1:非递归方法 根据题目这个很类似排序中的外排过程,两个数组分别排好序,然后再 ...
- java集合框架栈_自己实现集合框架(九):栈接口
这是系列文章,每篇文章末尾均附有源代码地址.目的是通过模拟集合框架的简单实现,从而对常用的数据结构和java集合有个大概的了解.当然实现没有java集合的实现那么复杂,功能也没有那么强大,但是可以通过 ...
- 黑马毕向东Java课程笔记(day14-1——14-11):集合类(集合框架)——集合类分类与特点+List集合接口及其子类
1.集合类特点 为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式. 数组和集合类同是容器,有何不同 ...
- java跨用问题怎么解决_跨浏览器问题的五种解决方案
c编程技巧-117个问题解决方案示例 79.2元 包邮 (需用券) 去购买 > 简评:浏览器兼容性问题常常让人头疼,以下是避免出现这些问题的五个技巧. 1. 前缀 CSS3 样式 如果您正在使用 ...
- java懒加载的原理_每天使用 Spring 框架,那你知道 lazy-init 懒加载原理吗?
普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean("")时进行触发. Spring ...
- java list转成map对象_将List集合中的map对象转为List对象形式--封装类
importjava.util.ArrayList;importjava.util.HashMap;importjava.util.Iterator;importjava.util.List;impo ...
- java 取map中的数据_获取Map集合中数据的方法
importjava.util.HashMap;importjava.util.Iterator;importjava.util.LinkedHashMap;importjava.util.Map;i ...
最新文章
- 第10章 嵌入式linux的调试技术
- Boost.PropertyTree 属性树的简介
- 《淘宝网开店 SEO 推广 营销 爆款 实战200招》——1.5 开一家适合你自身发展的店铺...
- jmeter之ip欺骗
- day36 fullstack gevent模块 IO阻塞和非阻塞 IO多路复用 异步IO介绍 其他的补充
- ElasticSearch的API python调用
- python2.7.10安装教程_Linux系统(CentOS)下python2.7.10安装
- MySQL数据库基础(多表关联查询、内外全连接、复合条件查询、子查询)
- apache nginx 区别
- 1004. 成绩排名 (20)
- DEM数据如何生成高程点
- navicat 导入excel 闪退
- 树莓派GPIO引脚详解
- SOUI自定义控件(4)
- 2009-03-13读书记录:《Enjoying Web Development with Wickte》三章心得
- SSD固态硬盘特性术语bit位设置
- Android 解决TextView排版参差不齐的问题
- WDA-FPM-4-用OVP做查询跳转到明细
- JavaEE学习笔记整理
- 【Pytorch学习笔记2】Pytorch的主要组成模块