LinkedList与链表

1. ArrayList的缺陷

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

2. 链表

2.1 链表的概念及结构

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

注意:

  1. 链式结构在逻辑上是连续的,但在物理上不一定是连续的

  2. 现实中的结点一般都是从堆上申请出来的

  3. 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,可能不连续

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

  1. 单向或者双向

  2. 带头或者不带头

  3. 循环或者非循环

常用:

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如 哈希桶、图的邻接表等等。

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

2.2 链表的实现

复制代码
// 1、无头单向非循环链表实现
public class SingleLinkedList {
//头插法
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() {
}
public void display() {}
}

3.链表题

  1. 删除链表中等于给定值 val 的所有节点。 OI链接
复制代码
public void removeAllKey(int key) {
        if(head == null) {
            return;
        }
        ListNode pre = head;
        ListNode cur = head;
        while (cur != null){
            if(cur.val == key){
                pre.next = cur.next;
                cur = cur.next;
                continue;
            }
            pre = cur;
            cur = cur.next;
        }
        if(head.val == key){
            head = head.next;
        }
    }
  1. 反转一个单链表。反转链表

    复制代码
    public ListNode reverseList(ListNode head) {
            if(head == null){
                return head;
            }
            ListNode cur = head.next;
            ListNode curN = cur.next;
            head.next = null;
            while (cur != null){
                cur.next = head;
                head = cur;
                cur = curN.next;
            }
            return head;
        }
  1. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。链表的中间结点

    复制代码
    public ListNode middleNode(ListNode head) {
            if(head == null){
                return head;
            }
            ListNode fast = head;
            ListNode slow = head;
            while (fast != null && fast.next != null){
                fast = fast.next.next;
                slow = slow.next;
            }
            return slow;
        }
  1. 输入一个链表,输出该链表中倒数第k个结点。返回倒数第 k 个节点
复制代码
 public int kthToLast(ListNode head, int k) {
        if(head == null){
            return -1;
        }
        if(k <= 0){
            return -1;
        }
        ListNode fast = head;
        ListNode slow = head;
        int cout = 0;
        while(cout != k-1){
            fast = fast.next;
            if(fast == null){
            return -1;
            }
            cout++;
        }
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow.val;
    }
  1. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。合并两个有序链表

    复制代码
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
            ListNode newList = new ListNode(-1);
            ListNode tmp = newList;
    ​
            while(list1 != null && list2 != null){
                if(list1.val < list2.val){
                tmp.next = list1;
                list1 = list1.next;
                tmp = tmp.next;
                } else {
                tmp.next = list2;
                list2 = list2.next;
                tmp = tmp.next;
                }
            }
            if(list1 != null){
                tmp.next = list1;
            } else {
                tmp.next = list2;
            }
    ​
            return newList.next; 
    ​
        }
  1. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。链表分割

    复制代码
    public ListNode partition(ListNode pHead, int x) {
            ListNode as = null;
            ListNode ae = null;
            ListNode bs = null;
            ListNode be = null;
            ListNode cur = pHead;
    ​
            while(cur != null){
                if(cur.val < x){
                    if(bs == null){
                        bs = be = cur;
                    } else {
                        be.next = cur;
                        be = be.next;
                    }
                } else {
                    if(as == null){
                        as = ae = cur;
                    } else {
                        ae.next = cur;
                        ae = ae.next;
                    }
                }
                cur = cur.next;
            }
            if(as != null){
                ae.next = null;
            }
            if(bs == null){
                return as;
            } 
            be.next = as;
            return bs;
            
        }
  1. . 链表的回文结构。链表的回文结构

    复制代码
    public boolean chkPalindrome(ListNode A) {
            // write code here
            if(A == null) return true;
            ListNode fast = A;
            ListNode slow = A;
            while(fast != null && fast.next != null){
                fast = fast.next.next;
                slow = slow.next;
            }
            ListNode cur = slow.next;
            while(cur != null){
                ListNode curN = cur.next;
                cur.next = slow;
                slow = cur;
                cur = curN; 
            }
            ListNode head = A;
            while(head != slow){
                if(head.val != slow.val){
                    return false;
                }
                if(head.next == slow){
                    return true;
                }
                head = head.next;
                slow = slow.next;
            }
            return true;
        }
  1. 输入两个链表,找出它们的第一个公共结点。相交链表

    复制代码
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode pl = headA;
            ListNode ps = headB;
            //1.先求2个链表的长度
            int lenA = 0;
            int lenB = 0;
            while(pl != null) {
                lenA++;
                pl = pl.next;
            }
            while(ps != null) {
                lenB++;
                ps = ps.next;
            }
            pl = headA;
            ps = headB;
            //2. 求一下差值
            int len = lenA -lenB;
            if(len < 0) {
                pl = headB;
                ps = headA;
                len = lenB -lenA;
            }
            //走完上述2步,pl一定指向最长的链表 ps 一定指向最短的链表
            // 接下来的操作 只需要操作 pl  和 ps 就行了
            //3. 让最长的链表 走len步
            while(len != 0) {
                pl = pl.next;
                len--;
            }
            //4.两个引用同时走 直到他们相遇
            while(pl != ps) {
                pl = pl.next;
                ps = ps.next;
            }
            if(pl == null) {
                return null;//没相交
            }
            return pl;
        }
  1. 给定一个链表,判断链表中是否有环。环形链表

    复制代码
     public boolean hasCycle(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            while(fast != null && fast.next != null){
                fast = fast.next.next;
                slow = slow.next;
                if(fast == slow){
                    return true;
                }
            }
            return false;
        }
  1. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL 。环形链表 II
复制代码
public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        if(fast == null || fast.next == null){
            return null;
        }
        slow = head;
        while(slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

链表 - 力扣(LeetCode)全球极客挚爱的技术成长平台

牛客网在线编程编程学习|练习题数据结构|系统设计题库 (nowcoder.com)

4.LinkedList的模拟实现

复制代码
// 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 display(){}
public void clear(){}
}

5.LinkedList的使用

5.1 什么是LinkedList

LinkedList的底层是双向链表结构(链表后面介绍),由于链表没有将元素存储在连续的空间中,元素存储在单独的节 点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

【说明】

  1. LinkedList实现了List接口

  2. LinkedList的底层使用了双向链表

  3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问

  4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)

  5. LinkedList比较适合任意位置插入的场景

5.2 LinkedList的使用

  1. LinkedList的构造

    方法 解释
    LinkedList() 无参构造
    public LinkedList(Collection<? extends E> c) 使用其他集合容器中元素构造List
    复制代码
    public static void main(String[] args) {
    // 构造一个空的LinkedList
    List<Integer> list1 = new LinkedList<>();
    List<String> list2 = new java.util.ArrayList<>();
    list2.add("JavaSE");
    list2.add("JavaWeb");
    list2.add("JavaEE");
    // 使用ArrayList构造LinkedList
    List<String> list3 = new LinkedList<>(list2);
    }
  2. LinkedList的其他常用方法介绍

方法 解释
boolean add(E e) 尾插 e
void add(int index, E element) 将 e 插入到 index 位置
boolean addAll(Collection<? extends E> 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
相关推荐
hnjzsyjyj3 小时前
AcWing 827:双链表 ← STL list
数据结构·链表·list·双链表
Yan-英杰3 小时前
Amazon SES + NestJS 实战:零成本打造高送达率邮箱验证方案
java·服务器·前端·网络·数据库·ai
CAU界编程小白3 小时前
数据结构系列之链表
数据结构·c++·链表
青云交3 小时前
Java 大视界 -- Java 大数据机器学习模型在元宇宙虚拟场景智能交互中的关键技术
java·机器学习·边缘计算·元宇宙·多模态融合·智能交互·情感计算
老任与码3 小时前
责任链模式
java·开发语言·责任链模式
哇!好大一个橙子3 小时前
Google浏览器及其对应版本chromedriver驱动下载(含最新版本)
java·python
安然~~~4 小时前
jvm之【垃圾回收器】
java·jvm
不宕机的小马达4 小时前
【Maven】Maven概述、安装以及其他相关知识
java·数据库·maven
鸽鸽程序猿4 小时前
【算法】【优选算法】BFS 解决边权相同最短路问题
java·算法·宽度优先