Java链表

文章目录

  • 目录

    前言

    一、链表

    1.1链表的概念及结构

    二、无头单向非循环链表实现过程

    2.1无头单向非循环链表部分函数

    2.2链表的插入

    2.2.1头插法

    2.2.2尾插法 

    2.2.3任意位置的插入

    2.3 是否包含这个元素

    2.4删除元素 

    2.4.1删除特定的一个元素

    2.4.2删除所有的特定元素

    2.5获取链表的长度 

    三、 无头双向链表实现过程

    3.1无头双向链表部分函数

    3.2双向链表的插入

    3.2.1头插法

    3.2.2尾插法

    3.2.3任意位置插入

    3.3查找元素

    3.4删除元素

    3.4.1删除一个对应的元素

    3.4.2 删除对应的所有元素

    3.5计算链表长度

    3.6删除链表

    四、LinkedList的使用

    4.1基本使用

    4.2 常用方法

    五、ArrayList与LinkedList区别

    总结


前言

承接上文顺序表,由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。本文将记录Java链表的学习过程,链表的概念以及链表的实现。


一、链表

1.1链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。

Java链表_第1张图片

 Java链表_第2张图片

 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向或者双向

Java链表_第3张图片


2. 带头或者不带头
 

Java链表_第4张图片

3. 循环或者非循环
 

Java链表_第5张图片

虽然有这么多的链表的结构,但是我们重点掌握两种:
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。

Java链表_第6张图片


无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

Java链表_第7张图片

 

二、无头单向非循环链表实现过程

2.1无头单向非循环链表部分函数

// 1、无头单向非循环链表实现
public class SingleLinkedList {
    static class Node{
        public int val;
        public Node next;
        public Node(int val){
            this.val =val;
        }
    }
    //定义一个头指针用于指向第一个元素的地址
    Node head;
    //头插法
    public void addFirst(int data) {}

    //尾插法
    public void addLast(int data) {}

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {}

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        return false;
    }
    
    //删除第一次出现关键字为key的节点
    public void remove(int key) {
    }
    //删除所有值为key的节点
    public void removeAllKey(int key){
    }
    
    //得到单链表的长度
    public int size(){
        return -1;
    }
    
    public void clear() {}
}

2.2链表的插入

2.2.1头插法

public void addFirst(int data) {
        Node node = new Node(data);
        node.next = head;
        head = node;
    }

2.2.2尾插法 

public void addLast(int data) {
        Node node = new Node(data);
        //尾插法需要判断头节点是否为空,不然会空指针异常
        if (head == null) {
            head = node;
            return;
        }
        Node cur = head;
        //让cur指针指向最后一个元素方便插入
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = node;
    }

 2.2.3任意位置的插入

    public void addIndex(int index, int data) {
        Node cur = head;
        //首位置就是头插法
        if (index <= 1) {
            addFirst(data);
            return;
        }
        //末位置就是尾插法
        if (index >= size()) {
            addLast(data);
            return;
        }
        //这里选择的是在哪一个位置
        while (index > 2) {
            cur = cur.next;
            index--;
        }
        Node node = new Node(data);
        node.next = cur.next;
        cur.next = node;
    }

2.3 是否包含这个元素

    public boolean contains(int key) {
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

2.4删除元素 

2.4.1删除特定的一个元素

    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        if (head == null) {
            return;
        }
        Node cur = head;
        //如果是第一个元素,直接头指针后移
        if (cur.val == key) {
            head = head.next;
            return;
        }
        //从第二个元素开始判断
        while (cur.next != null) {
            if (cur.next.val == key) {
                //直接跳过被删除的元素即可
                cur.next = cur.next.next;
                return;
            }
            cur = cur.next;
        }
        System.out.println("数据不存在!");
    }

2.4.2删除所有的特定元素

public void removeAllKey(int key){
        int len = size();
        if (head == null) {
            return;
        }
        Node cur = head.next;
        //用一个指针记录待删除的前一个元素地址,以免后面脱链
        Node pre = head;
        while(cur != null) {
            if (cur.val == key) {
                pre.next = cur.next;
            } else {
                pre = cur;
            }
            cur = cur.next;
        }
        if (head.val == key) {
            head = head.next;
        }
        //当数据没有减少说明没有删除,也就是没有这个数据
        if(len == size()) {
            System.out.println("没有这个数据!");
        }
    }

2.5获取链表的长度 

    public int size(){
        Node cur = head;
        int num = 0;
        while (cur != null) {
            num++;
            cur = cur.next;
        }
        return num;
    }

 

三、 无头双向链表实现过程

3.1无头双向链表部分函数

// 2、无头双向链表实现
public class MyLinkedList {
    //头插法
    public void addFirst(int data){ }
    //尾插法
    public void addLast(int data){}
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){}
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){}
    //删除第一次出现关键字为key的节点
    public void remove(int key){}
    //删除所有值为key的节点
    public void removeAllKey(int key){}
    //得到单链表的长度
    public int size(){}
    public void clear(){}
}

3.2双向链表的插入

3.2.1头插法

    //头插法
    public void addFirst(int data){
        Node node = new Node(data);
        if (head == null) {
            head = node;
            last = node;
            return;
        }
        node.next = head;
        head.prev = node;
        head = node;
    }

3.2.2尾插法

    public void addLast(int data){
        Node node = new Node(data);
        if (head == null) {
            head = node;
            last = node;
            return;
        }
        last.next = node;
        node.prev = last;
        last = node;
    }

3.2.3任意位置插入

    public void addIndex(int index,int data){
        if (index < 0 || index > size()) {
            System.out.println("插入位置错误!");
        }
        if (index == size()) {
            addLast(data);
        }
        if (index <= 1) {
            addFirst(data);
        }
        Node cur = head;
        while (index > 2) {
            index--;
            cur = cur.next;
        }
        Node node = new Node(data);
        node.next = cur.next;
        cur.next.prev = node;
        cur.next = node;
        node.prev = cur;
    }

3.3查找元素

    public boolean contains(int key){
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

3.4删除元素

3.4.1删除一个对应的元素

    public void remove(int key){
        int len = size();
        if (head == null) {
            return;
        }
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                //当要删除的元素是第一个元素时
                if (cur == head) {
                    //如果只有一个元素情况要考虑
                    if (head.next == null) {
                        head = null;
                        last =null;
                    } else {
                        head = head.next;
                        head.prev = null;
                    }
                    return;
                } else {
                    //删除的是最后一个元素
                    if (cur.next == head) {
                        last = last.prev;
                        cur.prev.next = null;
                    } else {
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                    return;
                }
            }
            cur = cur.next;
        }
        if(len == size()) {
            System.out.println("没有该元素存在");
        }
    }

3.4.2 删除对应的所有元素

在3.4.1的基础上删掉return即可

    public void removeAllKey(int key){
        int len = size();
        if (head == null) {
            return;
        }
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                //当要删除的元素是第一个元素时
                if (cur == head) {
                    //如果只有一个元素情况要考虑
                    if (head.next == null) {
                        head = null;
                        last =null;
                    } else {
                        head = head.next;
                        head.prev = null;
                    }
                } else {
                    //删除的是最后一个元素
                    if (cur.next == head) {
                        last = last.prev;
                        cur.prev.next = null;
                    } else {
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
            }
            cur = cur.next;
        }
        if(len == size()) {
            System.out.println("没有该元素存在");
        }
    }

3.5计算链表长度

     public int size(){
        Node cur = head;
        int num = 0;
        while (cur != null) {
            num++;
            cur = cur.next;
        }
        return num;
    }

3.6删除链表

    public void clear(){
        Node cur = head;
        //将每一个都释放掉(当然也可以直接暴力清除)
        while (cur != null) {
            //这里借用一个指针先记录cur的后一个元素,防止拖链
            Node curNext = cur.next;
            cur.next = null;
            cur.prev = null;
            cur = curNext;
        }
        head = null;
        last = null;
    }

 

四、LinkedList的使用

4.1基本使用

public static void main(String[] args) {
    // 构造一个空的LinkedList
    List list1 = new LinkedList<>();
    List list2 = new java.util.ArrayList<>();
    list2.add("JavaSE");
    list2.add("JavaWeb");
    list2.add("JavaEE");
    // 使用ArrayList构造LinkedList
    List list3 = new LinkedList<>(list2);
}

4.2 常用方法

方法 解释
boolean add(E e) 尾插 e
void add(int index, E element) 将 e 插入到 index 位置
boolean addAll(Collection c) 尾插 c 中的元素
E remove(int index) 删除 index 位置元素
boolean remove(Object o) 删除遇到的第一个 o
E get(int index) 获取下标 index 位置元素
E set(int index, E element) 将下标 index 位置元素设置为 element
void clear() 清空
boolean contains(Object o) 判断 o 是否在线性表中
int indexOf(Object o) 返回第一个 o 所在下标
int lastIndexOf(Object o) 返回最后一个 o 的下标
List subList(int fromIndex, int toIndex) 截取部分 list

五、ArrayList与LinkedList区别

不同点 ArrayList LinkedList
存储空间上 物理上一定连续 逻辑上连续,但物理上不一定连续
随机访问 支持O(1) 不支持:O(N)
头插 需要搬移元素,效率低O(N) 只需修改引用的指向,时间复杂度为O(1)
插入 空间不够时需要扩容 没有容量的概念
应用场景 元素高效存储+频繁访问 任意位置插入和删除频繁


总结

本文主要介绍了Java中链表的基本使用方法,并且介绍了Java中LinkedList常用的一些方法,同时实现了链表的部分重要的函数

你可能感兴趣的:(数据结构,java,链表,数据结构)