Java带头节点单链表的增删融合以及是否有环

带头节点单链表

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:

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

入环节点的求法: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;
        }
    }
}

原文地址:https://www.cnblogs.com/jiezai/p/11063304.html

时间: 2024-10-31 08:30:22

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

数据结构笔记5带头结点单链表

/* 本次操作是对带头节点单链表的操作  包括删除  插入 判空 建立  包括删除插入函数  显示函数 目的是为了对单链表做一个基本操作的总结----1*/ #include<stdio.h>#include<malloc.h>#include<conio.h>#define OK 1#define ERROR -1#define OVERFLOW -2#define ENDFLAG 0typedef struct LNode{ int data; struct LNo

java数据结构:单链表常见操作代码实现

一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashMap等集合的底层结构都是链表结构.链表以结点作为存储单元,这些存储单元可以是不连续的.每个结点由两部分组成:存储的数值+前序结点和后序结点的指针.即有前序结点的指针又有后序结点的指针的链表称为双向链表,只包含后续指针的链表为单链表,本文总结的均为单链表的操作. 单链表结构: Java中单链表采用No

单链表的增删查 逆置 倒数第k个节点等问题

    对于单链表而言,它没有双链表那么复杂,它只有头节点,尾节点,节点数据,后继指针.在下面本人实现了 单链表的 增   删   插  查  改.      #include<stdio.h> #include<assert.h> #include<malloc.h> #include<stdlib.h> typedef int Datatype; typedef struct SListNode { Datatype data; struct SList

单链表的增删查改等基本操作C++实现

单链表的初始化.增删查改.遍历一次找中间结点.删除一个无头单链表的非尾结点(不给头结点) #include<stdio.h> #include<stdlib.h> #include<malloc.h> typedef int DataType; typedef struct ListNode { struct ListNode* _next; DataType _data; }ListNode; void InitList(ListNode* &pHead) {

Java数据结构之单链表

链表的组成:链表头+结点 ? ?链表头一般只存储下一个节点的引用 ? ?节点:存数据+下一个节点的引用 链表头代码: package?com.xingej.algorithm.datastructure.linkedList.singleLinkedList; /** ?*?声明一个链表的头部 ?*? ?*?从链表头的使用,联想到hadoop,spark,netty中都有上下文context ?*? ?*[email protected]?erjun?2017年12月8日?上午8:45:08 ?

2.8~2.12带头节点的链表的基本操作

2.8和2.9在带头结点的链表中实现插入和获取元素的操作 #include<stdio.h> #include<stdlib.h> #include<math.h> typedef int Status ; typedef int ElemType; #define OK 1 #define ERROR 0 typedef struct LNode { ElemType data; struct LNode *next; }LNode,*LinkList; Status

【java链表 】java 头插法建单链表

好久前练习用的,现在看难度不大. package project; class Node { private int id; //私有就是只能本类对象及方法访问. private String name; public Node next; //指向下一个类节点 public Node(int id,String name)//有参构造方法 { this.id=id; this.name=name; } public void displayLink() //显示节点内容 { System.ou

单链表——带头节点

一.链表简介 1 数据结构中,链表是最基础的.然而链表根据不同的需求分成的种类很多,单向或双向链表,循环或非循环链表,带头节点或者不带头节点的链表. 2 本文实现——带头节点的单链表. 3 由于仅仅是学习链表的基本操作,所以在数据字段仅仅设置一个字段:   由于仅仅是学习基本操作,不涉及复杂的算法思想,所以不会很难,主要以代码为主,附上必要的解释即可. 二.具体实现 整体分析:带有头节点的单链表的操作很方便,主要体现在插入和删除时不需要判断是否是第一个元素. 1) 头文件定义如下: 1 #ifn

单链表一[带头节点链表]

单链表实现分带头节点链表和不带头节点链表: 使用头文件如下: struct LinkNode {     void *x;      struct LinkNode *next; }; 一,带头节点的链表: 1,链表创建 程序说明: 1)函数调用形式:szyu_link_create0("AA", "BB", NULL);其中NULL结尾是在for循环的判断结束条件为x == NULL.使用NULL可以是for循环正常退出 2)程序先创建头节点head,并初始化he