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
相关推荐
hrrrrb1 小时前
【Spring Security】Spring Security 概念
java·数据库·spring
小信丶1 小时前
Spring 中解决 “Could not autowire. There is more than one bean of type“ 错误
java·spring
周杰伦_Jay2 小时前
【Java虚拟机(JVM)全面解析】从原理到面试实战、JVM故障处理、类加载、内存区域、垃圾回收
java·jvm
程序员小凯5 小时前
Spring Boot测试框架详解
java·spring boot·后端
豐儀麟阁贵5 小时前
基本数据类型
java·算法
_extraordinary_6 小时前
Java SpringMVC(二) --- 响应,综合性练习
java·开发语言
程序员 Harry6 小时前
深度解析:使用ZIP流式读取大型PPTX文件的最佳实践
java
wxweven7 小时前
校招面试官揭秘:我们到底在寻找什么样的技术人才?
java·面试·校招
陈陈爱java7 小时前
新知识点背诵
java
失散137 小时前
分布式专题——39 RocketMQ客户端编程模型
java·分布式·架构·rocketmq