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;
    }
}

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

相关推荐
夏天的味道٥4 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
冰糖码奇朵5 小时前
大数据表高效导入导出解决方案,mysql数据库LOAD DATA命令和INTO OUTFILE命令详解
java·数据库·sql·mysql
好教员好5 小时前
【Spring】整合【SpringMVC】
java·spring
浪九天6 小时前
Java直通车系列13【Spring MVC】(Spring MVC常用注解)
java·后端·spring
Archer1947 小时前
C语言——链表
c语言·开发语言·链表
堕落年代7 小时前
Maven匹配机制和仓库库设置
java·maven
功德+n7 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven
开心比对错重要7 小时前
leetcode69.x 的平方根
数据结构·算法·leetcode
香精煎鱼香翅捞饭7 小时前
java通用自研接口限流组件
java·开发语言
ChinaRainbowSea8 小时前
Linux: Centos7 Cannot find a valid baseurl for repo: base/7/x86_64 解决方案
java·linux·运维·服务器·docker·架构