数据结构之链表(LinkedList详解)

简介: 数据结构之链表(LinkedList详解)

一、什么是LinkedList?


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

在集合框架中, LinkedList 也实现了 List 接口,具体如下:

1667914308321.jpg

1. LinkedList 实现了 List接口

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

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

4. LinkedList 的任意位置插入和删除元素时效率比较高,时间复杂度为 O(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); 
}

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<E> subList (int fromIndex, int toIndex)

截取部分 list


LinkedList的遍历:(foreach遍历、使用迭代器遍历---正向遍历、使用反向迭代器---反向遍历

LinkedList<String> list = new LinkedList<>();
        list.add("javaSE");
        list.add("javaWeb");
        list.add("javaEE");
        // foreach遍历
        for (String x: list) {
            System.out.print(x + " ");
        }
        // 使用迭代器遍历---正向遍历
        ListIterator<String> list1 = list.listIterator();
        while (list1.hasNext()){
            System.out.print(list1.next()+" ");
        }
        // 使用反向迭代器---反向遍历
        ListIterator<String> lis2 = list.listIterator(list.size());
        while (lis2.hasPrevious()){
            System.out.print(lis2.previous()+" ");
        }


三、LinkedList自实现


public class MyLinkedList {
    static class ListNode{
        int val;
        ListNode pre;
        ListNode next;
        public ListNode(int val){
            this.val = val;
        }
    }
    public ListNode head;//头部
    public ListNode tail;//尾部
    public void addFirst(int data){
        ListNode node = new ListNode(data);
        if (head == null){
            head = node;
            tail = node;
            return;
        }
        node.next = head;
        head.pre = node;
        head = node;
    }
    public void addLast(int data){
        ListNode node = new ListNode(data);
        if (head == null){
            head = node;
            tail = node;
            return;
        }
        tail.next = node;
        node.pre = tail;
        tail = node;
    }
    //任意位置插入,第一个数据节点为0号下标
    public boolean addIndex(int index,int data){
        if (index < 0 || index > size()){
            System.out.println("index位置不合法!");
            return false;
        }
        if (index == 0){
            addFirst(data);
            return true;
        }
        if (index == size()){
            addLast(data);
            return true;
        }
        ListNode indexNode = findNode(index);
        ListNode node = new ListNode(data);
        node.pre = indexNode.pre;
        indexNode.pre.next = node;
        node.next = indexNode;
        indexNode.pre = node;
        return true;
    }
    public ListNode findNode(int index){
        ListNode cur = head;
        while (index != 0){
            cur = cur.next;
            index--;
        }
        return cur;
    }
    public boolean contains(int key){
        ListNode cur = head;
        while (cur != null){
            if (cur.val == key)return true;
            cur = cur.next;
        }
        return false;
    }
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == head) {
                    head = head.next;
                    if (head != null) {
                        head.pre = null;
                    } else {
                        tail = null;
                    }
                } else {
                    cur.pre.next = cur.next;
                    if (cur.next != null) {
                        cur.next.pre = cur.pre;
                    } else {
                        tail = tail.pre;
                    }
                }
                return;
            }
            cur = cur.next;
        }
    }
    public void removeAllKey(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == head) {
                    head = head.next;
                    if (head != null) {
                        head.pre = null;
                    } else {
                        tail = null;
                    }
                } else {
                    cur.pre.next = cur.next;
                    if (cur.next != null) {
                        cur.next.pre = cur.pre;
                    } else {
                        tail = tail.pre;
                    }
                }
            }
            cur = cur.next;
        }
    }
    public int size(){
        ListNode cur = head;
        int count = 0;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }
    public void display(){
        ListNode cur = head;
        while (cur != null){
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
    }
    public void clear(){
        ListNode cur = head;
        while (cur != null){
            ListNode temp = cur.next;
            cur.next = null;
            cur.pre = null;
            cur = temp;
        }
        head = null;
        tail = null;
    }
}

四、链表实现逆序打印的两种方式(递归和非递归)


递归逆序打印:

public void display(ListNode head) {
        if(head == null) {
            return;
        }
        if(head.next == null) {
            System.out.print(head.val+" ");
            return;
        }
        display(head.next);
        System.out.print(head.val+" ");
    }

非递归逆序打印(用栈实现):

public void display(ListNode head) {
        if (head == null) {
            return;
        }
        Stack<ListNode> stack = new Stack<>();
        ListNode cur = head;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        while (!stack.empty()) {
            ListNode ret = stack.pop();
            System.out.print(ret.val+" ");
        }
    }

五、ArrayList和LinkedList有什么区别?


ArrayList实质是顺序表,底层是一个数组。LinkedList实质是一个链表。


他们都具有增删查改的功能,但是在实现方式上有区别。比如在插入元素的时候,ArrayList往0位置上插入一个元素,就需把整个数组整体往后移动,时间复杂度为O(N)。而LinkedList只需要修改指向就可以了,时间复杂度为O(1)。但是ArrayList可以支持随机访问,时间复杂度为O(1),所以一般情况下ArrayList顺序表适合频繁根据下标位置访问,LinkedList比较适合插入和删除比较频繁的情况。


从存储上来说,ArrayList顺序表在物理上和逻辑上都是连续的,但是在扩容的时候,可能会造成空间的浪费。而LinkedList在物理上不一定是连续的,在逻辑上是连续的,可以做到随用随取。



不同点 ArrayList LinkedList




存储空间上         

物理上逻辑上一定连续

逻辑上连续,但物理上不一定连续

随机访问

支持 O(1)

不支持: O(N)

头插

需要搬移元素,效率低 O(N)

只需修改引用的指向,时间复杂度为 O(1)

插入

空间不够时需要扩容

没有容量的概念

应用场景

元素高效存储 + 频繁访问

任意位置插入和删除频繁

相关文章
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
300 4
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
511 30
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
752 25
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
621 5
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
499 5
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
1515 4
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
410 0