带头节点单链表

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. Python面试题之阅读下面的代码,写出A0,A1至An的最终值

    A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5))) A1 = range(10) A2 = [i for i in A1 if i in A0] A3 ...

  2. JS 将对象转换成字符 字符串转换成json对象

    //js对象 var user = { "name": "张学友", "address": "中国香港" }; //将对 ...

  3. 最长递增子序列nlogn的做法

    费了好大劲写完的  用线段树维护的 nlogn的做法再看了一下 大神们写的 nlogn  额差的好远我写的又多又慢  大神们写的又少又快时间  空间  代码量 哪个都赶不上大佬们的代码 //这是我写的 ...

  4. 浅析vue响应式原理

    图很清晰 当我们把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 g ...

  5. ARM 汇编访问 CPSR / SPSR 寄存器 【 msr ,mrs 】

    状态寄存器访问过程:读 - 改 - 写 读 CPSR / SPSR 指令[ mrs ]    格式:<opcode><cond> Rn, cpsr/spsr 写 CPSR / ...

  6. 微信小程序改变全局变量

    假设A为登录页面并将登录获得的用户信息保存到app.js中的全局变量userInfo中,然后在B页面进行使用. app.js globalData:{    userInfo:null, } a.js ...

  7. 【学术篇】SPOJ FTOUR2 点分治

    淀粉质入门第一道 (现在个人认为spoj比bzoj要好_(:з」∠)_ 关于点分治的话推荐去看一看漆子超的论文>>>这里这里<<< 之前一直试图入点分治坑, 但是因 ...

  8. (PASS)什么是原子性和原子性操作?

    什么是原子性操作呢? 下面我举一个例子来说明一下: A想要从自己的帐户中转1000块钱到B的帐户里.那么从A开始转帐,到转帐结束的这一个过程,称之为一个事务.在这个事务里,要做如下操作: 1. 从A的 ...

  9. typescript + vue开发遇到的坑

    1,错误 :TS2304: Cannot find name 'require' 在ts使用nodejs,没有安装nodejs的TypeScript定义类型 ,使用require报的错 解决方法:如果 ...

  10. 6358. 【NOIP2019模拟2019.9.15】小ω的仙人掌

    题目 题目大意 给你一串二元组\((a_i,b_i)\)的数列. 求最小的区间\([l,r]\)长度,满足\([l,r]\)中的每个二元组选或不选,使得\(\sum a_i=w\)且\(\sum b_ ...