带头节点单链表

1.优势:

1)当链表为空时,指针指向头结点,不会发生null指针异常

2)方便特殊操作(删除第一个有效节点或者插入一个节点在表头)

3)单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会。

4)代码更加简单易懂

2.相关操作

1)建立单链表 即建立一个头结点

   static class Entry<T> {
T data; // 链表节点的数据域
Entry<T> next; // 下一个节点的引用 public Entry(T data, Entry<T> next) {
this.data = data;
this.next = next;
} } /**
* 头节点的类型
*
* @param <T>
*/
private static class HeadEntry<T> extends Entry<T> {
int cnt; // 用来记录节点的个数 public HeadEntry(int cnt, T data, Entry<T> next) {
super(data, next);
this.cnt = cnt;
}
}

2)头插:新建节点next域指向头结点的next域,再将头结点next指向新节点。

    public void insertHead(T val) {
Entry<T> newNode = new Entry<>(val, null);
newNode.next = this.head.next;
this.head.next = newNode;
}

3)尾插:从头结点开始遍历,当next出现null时,已经到达尾部,将next指向新节点即可

    public void insertTail(T val) {
Entry<T> temp = this.head;
Entry<T> newNode = new Entry<>(val, null);
while (temp.next != null) {
temp = temp.next;
}
temp.next = newNode;
}

4)删除值为val的节点 :设置一前一后指针进行遍历,当后指针发现值为val的节点时,前指针的next域指向后指针的next域即可

    public void remove(T val) {
Entry<T> frontTemp = this.head;
Entry<T> temp = frontTemp.next;
while (temp.data != val) {
frontTemp = frontTemp.next;
temp = temp.next;
}
frontTemp.next = temp.next;
}

5)逆置单链表 :将第二个有效节点不断进行头插操作,就会产生逆置效果

    将链表从第二个有效节点开始一分为二,不断头插进前一链表 

public void reverse() {
if (this.head.next == null) {
return;
}
Entry<T> cur = this.head.next.next;
this.head.next.next = null;
Entry<T> post = null;
while (cur != null) {
post = cur.next;
cur.next = head.next;
head.next = cur;
cur = post;
}
}

6)获取倒数第k个节点的值:设置两个相距K的指针,当前指针到达链表尾部时,前指针指向倒数第k个节点

 public T getLastK(int k) {
Entry<T> cur1 = this.head.next;
Entry<T> cur2 = cur1.next;
for (int i = 1; i < k; i++) {
cur2 = cur2.next;
if (cur2 == null) {
return null;
}
}
while (cur2 != null) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1.data;
}

7)判断链表是否有环,如果有,返回入环节点的值;否则返回null:

设置快慢指针,快指针一次移动两次,慢指针一次移动一次,如果出现快指针指向的节点等于慢指针指向的节点 ,即证明节点有环。

入环节点的求法:

设相遇点为A点;当快指针经过 x+2y+z个节点时时 慢指针走了x+y个节点   可得2(x+y)=x+2y+z   化简可得x=z;所以

fast从第一个节点开始走,slow从快慢指针相交的地方开始走,它们相遇的时候,就是环的入口节点

 public T isLinkCircle() {
//利用快慢指针检测是否有环
Entry<T> slow = this.head.next;
Entry<T> quick = this.head.next;
while (quick != null && quick.next != null) {
slow = slow.next;
quick = quick.next.next;
if (slow == quick) {
break;
}
}
if (quick == null) {
return null;
} else {
// fast从第一个节点开始走,slow从快慢指针相交的地方开始走,它们相遇的时候,就是环的入口节点
quick = this.head.next;
while (quick != slow) {
quick = quick.next;
slow = slow.next;
}
return slow.data;
}
}

8)合并两个有序单链表

   public void merge(Link<T> link) {
Entry<T> p = this.head;
Entry<T> p1 = this.head.next;
Entry<T> p2 = link.head.next; // 比较p1和p2节点的值,把值小的节点挂在p的后面
while (p1 != null && p2 != null) {
if (p1.data.compareTo(p2.data) >= 0) {
p.next = p2;
p2 = p2.next;
} else {
p.next = p1;
p1 = p1.next;
}
p = p.next;
}
if (p1 != null) { // 链表1还有剩余节点
p.next = p1;
} if (p2 != null) { // 链表2还有剩余节点
p.next = p2;
}
}

总源代码

class Link<T extends Comparable<T>> {

    // 指向单链表的第一个节点
Entry<T> head; public Link() {
this.head = new Entry<>(null, null);
} /**
* 单链表的头插法
*
* @param val
*/
public void insertHead(T val) {
Entry<T> newNode = new Entry<>(val, null);
newNode.next = this.head.next;
this.head.next = newNode;
} /**
* 单链表的尾插法
*
* @param val
*/
public void insertTail(T val) {
Entry<T> temp = this.head;
Entry<T> newNode = new Entry<>(val, null);
while (temp.next != null) {
temp = temp.next;
}
temp.next = newNode;
} /**
* 单链表中删除值为val的节点
*
* @param val
*/
public void remove(T val) {
Entry<T> frontTemp = this.head;
Entry<T> temp = frontTemp.next;
while (temp.data != val) {
frontTemp = frontTemp.next;
temp = temp.next;
}
frontTemp.next = temp.next;
} /**
* 逆置单链表
*/
public void reverse() {
if (this.head.next == null) {
return;
}
Entry<T> cur = this.head.next.next;
this.head.next.next = null;
Entry<T> post = null;
while (cur != null) {
post = cur.next;
cur.next = head.next;
head.next = cur;
cur = post;
}
} /**
* 获取倒数第K个单链表节点的值
* 1.遍历一次链表,统计链表节点的个数length
* 2.length-k
* 只遍历一次链表,找出数
*
* @param k
*/
public T getLastK(int k) {
Entry<T> cur1 = this.head.next;
Entry<T> cur2 = cur1.next;
for (int i = 1; i < k; i++) {
cur2 = cur2.next;
if (cur2 == null) {
return null;
}
}
while (cur2 != null) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1.data;
} /**
* 判断链表是否有环,如果有,返回入环节点的值;否则返回null
*
* @return
*/
public T isLinkCircle() {
//利用快慢指针检测是否有环
Entry<T> slow = this.head.next;
Entry<T> quick = this.head.next;
while (quick != null && quick.next != null) {
slow = slow.next;
quick = quick.next.next;
if (slow == quick) {
break;
}
}
if (quick == null) {
return null;
} else {
// fast从第一个节点开始走,slow从快慢指针相交的地方开始走,它们相遇的时候,就是环的入口节点
quick = this.head.next;
while (quick != slow) {
quick = quick.next;
slow = slow.next;
}
return slow.data;
}
} /**
* 打印单链表的所有元素的值
*/
public void show() {
Entry<T> temp = this.head.next;
while (temp != null) {
System.out.print(temp.data + " ");
temp = temp.next;
}
System.out.println();
} /**
* 单链表的节点类型
*
* @param <T>
*/
static class Entry<T> {
T data; // 链表节点的数据域
Entry<T> next; // 下一个节点的引用 public Entry(T data, Entry<T> next) {
this.data = data;
this.next = next;
} } /**
* 头节点的类型
*
* @param <T>
*/
private static class HeadEntry<T> extends Entry<T> {
int cnt; // 用来记录节点的个数 public HeadEntry(int cnt, T data, Entry<T> next) {
super(data, next);
this.cnt = cnt;
}
} /**
* 合并两个有序的单链表
*
* @param link
*/ public void merge(Link<T> link) {
Entry<T> p = this.head;
Entry<T> p1 = this.head.next;
Entry<T> p2 = link.head.next; // 比较p1和p2节点的值,把值小的节点挂在p的后面
while (p1 != null && p2 != null) {
if (p1.data.compareTo(p2.data) >= 0) {
p.next = p2;
p2 = p2.next;
} else {
p.next = p1;
p1 = p1.next;
}
p = p.next;
}
if (p1 != null) { // 链表1还有剩余节点
p.next = p1;
} if (p2 != null) { // 链表2还有剩余节点
p.next = p2;
}
}
}

Java带头节点单链表的增删合并以及是否有环的更多相关文章

  1. java实现单链表的增删功能

    JAVA 实现单链表的增删功能 package linked; class LinkedTable{ } public class LinkedTableTest { public static vo ...

  2. C语言:根据形参c中指定的英文字母,按顺序打印出若干后继相邻字母,-主函数中放入一个带头节点的链表结构中,h指向链表的头节点。fun函数找出学生的最高分-使用插入排序法对字符串中的字符进行升序排序。-从文件中找到指定学号的学生数据,读入次学生数据,

    //根据形参c中指定的英文字母,按顺序打印出若干后继相邻字母,输出字母的大小与形参c一致,数量由形参d指定.例如:输入c为Y,d为4,则输出ZABC. #include <stdio.h> ...

  3. 转 java - 如何判断单链表有环

    转自 https://blog.csdn.net/u010983881/article/details/78896293 1.穷举遍历 首先从头节点开始,依次遍历单链表的每一个节点.每遍历到一个新节点 ...

  4. Java数据结构之单链表

    这篇文章主要讲解了通过java实现单链表的操作,一般我们开始学习链表的时候,都是使用C语言,C语言中我们可以通过结构体来定义节点,但是在Java中,我们没有结构体,我们使用的是通过类来定义我们所需要的 ...

  5. Java数据结构-03单链表(二)

    在之前我们封装了一些操作在接口类中,并在抽象类实现了相同的方法.下面我们开始写代码: 无头结点单链表:(注意下面的AbstractList是之前抽取的类,不是java.util包下的类) public ...

  6. 图解Java数据结构之单链表

    本篇文章介绍数据结构中的单链表. 链表(Linked List)介绍 链表可分为三类: 单链表 双向链表 循环列表 下面具体分析三个链表的应用. 单链表 链表是有序的列表,它在内存中存储方式如下: 虽 ...

  7. c语言实现--带头结点单链表操作

    可能是顺序表研究的细致了一点,单链表操作一下子就实现了.这里先实现带头结点的单链表操作. 大概有以下知识点. 1;结点:结点就是单链表中研究的数据元素,结点中存储数据的部分称为数据域,存储直接后继地址 ...

  8. Java数据结构-02单链表(一)

    一.链式存储: ①简述:线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的.存储单元由两部分组成,数据源和指针,数据源放数据,指针指向下个 ...

  9. Java实现单链表的增删查改及逆置打印

    //所提供的接口 LinkList.java package Struct; public interface LinkList {//判断链表为空public boolean linkListIsE ...

随机推荐

  1. BOM的构成

    1.DOM 和 BOM 的区别 DOM:文档对象模型,把[文档]当做一个[对象]来看待,DOM的顶级对象是document 主要学习的是操作页面元素,DOM 是 W3C 的标准规范 BOM:浏览器对象 ...

  2. ant design 两个tabs如何同时切换

    假设界面上有两个地方用到了同一个tabs,但是切换其中一个tabs,另一个tabs并不会同时切换,因为只是在其中一个tabs上调用了onChange,所以需要用到activeKey动态地设置tabs的 ...

  3. python 生成推导式

    推导式comprehensions(又称解析式),是Python的一种独有特性.推导式是可以从一个数据序列构建另一个新的数据序列的结构体. 共有三种推导,在Python2和3中都有支持: 列表(lis ...

  4. 笔记45 Hibernate快速入门(二)

    Hibernate O/R 映射 一.多对一 一个Product对应一个Category,一个Category对应多个Product,所以Product和Category是多对一的关系.使用hiber ...

  5. Dao层结合Service层处理异常

    1. 接口存在异常不利于解耦. 2. 将编译时异常转化为运行时异常或其子类,通知上层,上层可以根据自身能力选择处理或向上抛出. 举例: 将UserDao中的SQLException转化为DaoExce ...

  6. Python中的网络爬虫怎么用?

    爬虫概述 (约2016年)网络爬虫个人使用和科研范畴基本不存在问题,但商业盈利范畴就要看对方了. 通过网站的Robots协议(爬虫协议)可以知道可以和不可以抓取的内容,其中User-Agent: 为允 ...

  7. 使用vue-cli脚手架和vue-router搭建项目(一)

    之前做的项目一直比较简单,并没有引入整个路由库.今天准备练习下

  8. leetcood学习笔记-501- 二叉搜索树中的众数

    题目描述: 方法一: class Solution: def findMode(self, root: TreeNode) -> List[int]: if not root: return [ ...

  9. 带各位深入理解java1.8之supplier

    supplier也是是用来创建对象的,但是不同于传统的创建对象语法:new,看下面代码:public class TestSupplier { private int age; (www.0831jl ...

  10. springMVC快速入门 共分为五步

    springMVC快速入门 共分为5步分别为: 1 导入依赖 ​​ 2 spring-mvc.xml 配置 ​ 3 web.xml配置 ​ 4 自定义一个核心控制类 ​ 5 页面配置 详细步骤以及代码 ...