Java数据结构-双向不带头非循环链表(模拟实现LinkedList)

目录

  • [1. 双向不带头非循环链表的介绍](#1. 双向不带头非循环链表的介绍)
  • [2. 相关功能的实现](#2. 相关功能的实现)
    • [2.1 基本框架](#2.1 基本框架)
    • [2.2 size](#2.2 size)
    • [2.3 addFirst](#2.3 addFirst)
    • [2.4 addLast](#2.4 addLast)
    • [2.5 addIndex](#2.5 addIndex)
    • [2.6 contains](#2.6 contains)
    • [2.7 remove](#2.7 remove)
    • [2.8 removeAllKey](#2.8 removeAllKey)
    • [2.9 clear](#2.9 clear)
  • [3. 全部代码](#3. 全部代码)

前面我们学习了最简单的链表:单链表,今天我们学习双向不带头非循环链表,这也是Java的集合框架中LinkedList的底层实现

1. 双向不带头非循环链表的介绍

双向指的是:每个链表结点有三个域,一个是数据域,另外两个保存了前驱结点的地址和后继结点的地址,如图,第一个结点的前驱为null,最后一个结点的后继也为null

2. 相关功能的实现

2.1 基本框架

同样定义泛型类myLinkedList,每个结点定义为静态内部类ListNode,提供构造方法给val初始化,定义结点first表示链表的第一个结点,定义last表示链表的最后一个结点

java 复制代码
public class myLinkedList<T> {

    //结点类
    static class ListNode {
        public ListNode prev;//前驱
        public Object val;//数据
        public ListNode next;//后继

        //构造方法
        public ListNode(Object val) {
            this.val = val;
        }
    }

    public ListNode first;//第一个结点
    public ListNode last;//最后一个结点
    //....其他方法
}

2.2 size

size的功能是获取链表长度,定义计数器size,遍历链表每个结点,每遍历一个结点size+1,直至遍历完链表,返回size

java 复制代码
    //得到单链表的长度
    public int size() {
        int size = 0;
        ListNode cur = first;
        while (cur != null) {
            cur = cur.next;
            size++;
        }
        return size;
    }

2.3 addFirst

addFirst意为头插,在链表的头部插入数据,如果链表没有数据,first等于null,则让first和last等于新的结点,然后return;如果链表已有数据,先让新的结点绑定链表,再让原来的头部的prev指向新的结点,接着更新first

java 复制代码
    //头插法
    public void addFirst(T val) {
        ListNode newNode = new ListNode(val);
        //如果没有元素
        if (first == null) {
            first = last = newNode;
            return;
        }
        newNode.next = first;
        first.prev = newNode;
        first = newNode;
    }

2.4 addLast

addLast表示尾插,在链表尾部插入数据,如果链表没有数据,让first和last等于新的结点,然后return;如果链表已有数据,让新的结点的prev指向链表的last,接着让last的next指向新的结点,然后更新last的值

java 复制代码
    //尾插法
    public void addLast(T val) {
        ListNode newNode = new ListNode(val);
        //如果没有元素
        if (last == null) {
            first = last = newNode;
            return;
        }
        newNode.prev = last;
        last.next = newNode;
        last = newNode;
    }

2.5 addIndex

addIndex表示在Index位置插入数据,插入前需要判断Index的合法性(Index不能小于0,不能大于链表的长度),定义IndexNotLegalException异常类,如果Index不合法就抛出这个异常

java 复制代码
public class IndexNotLegalException extends RuntimeException {
    public IndexNotLegalException(String s) {
        super(s);
    }
    public IndexNotLegalException() {
        super();
    }
}

如果Index等于0,表示头插,如果Index等于链表长度,表示尾插,此时直接调用方法;在插入之前先找到Index位置的结点,接着先绑定后面的数据

java 复制代码
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, T val) {
        ListNode newNode = new ListNode(val);
        //index合法性检查
        try {
            if (index < 0 || index > size()) {
                throw new IndexNotLegalException("Index不合法");
            }
        } catch (IndexNotLegalException e) {
            e.printStackTrace();
        }

        //index=0 头插
        if (index == 0) {
            addFirst(val);
            return;
        }
        //index=size 尾插
        if (index == size()) {
            addLast(val);
            return;
        }
        //
        ListNode cur = first;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        ListNode prev = cur.prev;
        //开始绑定
        newNode.next = cur;
        newNode.prev = prev;       
        prev.next = newNode;
        cur.prev = newNode;
    }

2.6 contains

查找链表中是否包含某个数据(key),如果包含返回true否则,返回false

java 复制代码
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(T key) {
        if (first == null) {
            return false;
        }
        ListNode cur = first;
        while (cur != null) {
            if (cur.val.equals(key)) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

2.7 remove

删除链表中第一个值为key的结点,如果链表为空,直接返回。定义del来遍历链表,如果del的值与key相等,分情况讨论:

1.del为第一个结点,此时只需将first指向下一个结点即可

2.del为最后一个结点,此时将last指向前一个结点,然后将last的next置为空

3.一般情况:要删除的结点在中间,定义两个变量:prevNode,表示要删除的结点的前一个结点;nextNode,表示要删除的结点的后一个结点,让prevNode的next指向nextNode,让nextNode的prev指向prevNode,此时就跳过了要删除的结点,没人引用它也就意味着删除了它

java 复制代码
    //删除第一次出现关键字为key的结点
    public void remove(T key) {
        if (first == null) {
            return;
        }
        ListNode del = first;
        while (del != null) {
            if (del.val.equals(key)) {
                //删除
                ListNode preNode = del.prev;
                ListNode nextNode = del.next;
                //pre==null?
                if (pre == null) {
                    //第一个是key
                    first = first.next;
                    return;
                }
                if (next == null) {
                    //最后一个是key
                    last = last.prev;
                    last.next = null;
                    return;
                }
                preNode.next = nextNode;
                nextNode.prev = preNode;
                return;
            }
            del = del.next;
        }
    }

2.8 removeAllKey

删除链表中所有值为key的结点,分情况讨论:

1.前面部分全是要删除的结点:first往后走,如果first为空了,说明整个链表的值都是key,这个时候直接return

2.后面部分全是要删除的结点:让last往前走

3.一般情况:情况1处理了链表前部分都是key的情况,情况2处理了链表后部分都是key的情况,此时的key只能出现在中间部分,删除的逻辑remove一样,只不过remove删除完一个之后return了,removeAllKey则是删除之后继续删除

java 复制代码
    //删除所有值为key的结点
    public void removeAllKey(T key) {
        if (first == null) {
            return;
        }
        //处理前面的key
        while (first.val.equals(key)) {
            first = first.next;
            if (first == null) {
                return;
            }
        }
        //处理后面的key
        while (last.val.equals(key)) {
            last = last.prev;
            last.next = null;
        }
        //
        ListNode del = first;
        while (del != null) {
            if (del.val.equals(key)) {
                //删除
                ListNode preNode = del.prev;
                ListNode nextNode = del.next;
                //
                preNode.next = next;
                nextNode.prev = preNode;
            }
            del = del.next;
        }
    }

2.9 clear

clear表示将链表置空,只需循环遍历链表,将每一个结点都置空

java 复制代码
    public void clear() {
        while (first != null) {
            ListNode next = first.next;
            first.prev = first.next = null;
            first = next;
        }
        last = null;
    }

3. 全部代码

java 复制代码
//不带头 双向 非循环 链表
public class myLinkedList<T> {

    //结点类
    static class ListNode {
        public ListNode prev;//前驱
        public Object val;//数据
        public ListNode next;//后继

        //构造方法
        public ListNode(Object val) {
            this.val = val;
        }
    }

    public ListNode first;//第一个结点
    public ListNode last;//最后一个结点

    //头插法
    public void addFirst(T val) {
        ListNode newNode = new ListNode(val);
        //如果没有元素
        if (first == null) {
            first = last = newNode;
            return;
        }
        newNode.next = first;
        first.prev = newNode;
        first = newNode;
    }

    //尾插法
    public void addLast(T val) {
        ListNode newNode = new ListNode(val);
        //如果没有元素
        if (last == null) {
            first = last = newNode;
            return;
        }
        newNode.prev = last;
        last.next = newNode;
        last = newNode;
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, T val) {
        ListNode newNode = new ListNode(val);
        //index合法性检查
        try {
            if (index < 0 || index > size()) {
                throw new IndexNotLegalException("Index不合法");
            }
        } catch (IndexNotLegalException e) {
            e.printStackTrace();
        }

        //index=0 头插
        if (index == 0) {
            addFirst(val);
            return;
        }
        //index=size 尾插
        if (index == size()) {
            addLast(val);
            return;
        }
        //
        ListNode cur = first;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        ListNode prev = cur.prev;
        //开始绑定
        newNode.next = cur;
        newNode.prev = prev;
        prev.next = newNode;
        cur.prev = newNode;
    }

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(T key) {
        if (first == null) {
            return false;
        }
        ListNode cur = first;
        while (cur != null) {
            if (cur.val.equals(key)) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    //删除第一次出现关键字为key的节点
    public void remove(T key) {
        if (first == null) {
            return;
        }
        //
        ListNode del = first;
        while (del != null) {
            if (del.val.equals(key)) {
                //删除
                ListNode pre = del.prev;
                ListNode next = del.next;
                //pre==null?
                if (pre == null) {
                    //第一个是key
                    first = first.next;
                    return;
                }
                if (next == null) {
                    //最后一个是key
                    last = last.prev;
                    last.next = null;
                    return;
                }
                pre.next = next;
                next.prev = pre;
                return;
            }
            del = del.next;
        }
    }

    //删除所有值为key的节点
    public void removeAllKey(T key) {
        if (first == null) {
            return;
        }
        //处理前面的key
        while (first.val.equals(key)) {
            first = first.next;
            if (first == null) {
                return;
            }
        }
        while (last.val.equals(key)) {
            last = last.prev;
            last.next = null;
        }
        //
        ListNode del = first;
        while (del != null) {
            if (del.val.equals(key)) {
                //删除
                ListNode pre = del.prev;
                ListNode next = del.next;
                //
                pre.next = next;
                next.prev = pre;
            }
            del = del.next;
        }
    }

    //得到单链表的长度
    public int size() {
        int size = 0;
        ListNode cur = first;
        while (cur != null) {
            cur = cur.next;
            size++;
        }
        return size;
    }

    public void clear() {
        while (first != null) {
            ListNode next = first.next;
            first.prev = first.next = null;
            first = next;
        }
        last = null;
    }
}

今天的内容就到这里,感谢老铁们的点赞、收藏、评论~❤

相关推荐
【D'accumulation】5 分钟前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
小叶学C++11 分钟前
【C++】类与对象(下)
java·开发语言·c++
2401_8543910814 分钟前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss23 分钟前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
wxin_VXbishe24 分钟前
springboot合肥师范学院实习实训管理系统-计算机毕业设计源码31290
java·spring boot·python·spring·servlet·django·php
Cikiss24 分钟前
微服务实战——平台属性
java·数据库·后端·微服务
无敌の星仔33 分钟前
一个月学会Java 第2天 认识类与对象
java·开发语言
OEC小胖胖38 分钟前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
2401_857617621 小时前
SpringBoot校园资料平台:开发与部署指南
java·spring boot·后端
quokka561 小时前
Springboot 整合 logback 日志框架
java·spring boot·logback