数据结构与算法(三)链表

简介: 数据结构与算法(三)链表

定义

链表通过指针将一组零散的内存块串联在一起。其中,我们把内存块称为链表的“结点”。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址。

特点

  • 不需要连续的内存空间。
  • 有指针引用
  • 三种最常见的链表结构:单链表、双向链表和循环链表

图形

链表.jpeg

从单链表图中,可以发现,有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点。我们一般把第一个结点叫作头结点,把最后一个结点叫作尾结点。

其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址NULL,表示这是链表上最后一个结点。

  • 双向链表:双向链表有两个指针域,一个指向性上一个节点,一个指向下一个节点,头节点的上一个节点为NULL
  • 循环链表:循环链表是一种特殊的单链表。实际上,循环链表也很简单。它跟单链表唯一的区别就在尾结点。环链表的尾结点指针是指向链表的头结点。

数组和链表的区别

  1. 数组结构使用的是连续的内存空间,可以借助cpu缓存的机制,预读数组中的数据
  2. 链表在内存中并不是连续存储,所以对CPU缓存不友好,没办法有效预读。
  3. 数组的缺点是大小固定,一经声明就要占用整块连续内存空间。如果声明的数组过大,系统可能没有足够的连续内存空间分配给它,导致“内存不足(out of memory)”。如果声明的数组过小,则可能出现不够用的情况。
  4. 动态扩容:数组需再申请一个更大的内存空间,把原数组拷贝进去,非常费时。链表本身没有大小的限制,天然地支持动态扩容。
  5. 时间复杂度:数组的访问为O(1),修改为O(n);链表的访问为O(n),修改为O(1)

这里也不是绝对的,如果数组和链表都是在尾部或者头部进行操作,那数组和链表的访问和修改都是O(1),如栈和队列

手写一个LinkedList

public class MyLinkedList<E> implements MyList<E> {
    private int size;
    private Node<E> first;
    private Node<E> last;
    @Override
    public int size() {
        return size;
    }
    @Override
    public void add(E e) {
        Node<E> newNode = new Node<>(null, e, null);
        if (first == null) {
            first = newNode;
            last = newNode;
            size++;
            return;
        }
        last.next = newNode;
        newNode.pre = last;
        last = newNode;
        size++;
    }
    @Override
    public void add(E e, int index) {
        if (index < 0 || index > size) throw new IndexOutOfBoundsException();
        Node<E> newNode = new Node<>(null, e, null);
        if (index == size) {//加在末尾
            add(e);
            return;
        }
        Node<E> cur = node(index);
        newNode.next = cur;
        if (cur.pre == null) {//头
            cur.pre = newNode;
            first = newNode;
        } else {
            newNode.pre = cur.pre;
            cur.pre.next = newNode;
            cur.pre = newNode;
        }
        size++;
    }
    @Override
    public void remove(int index) {
        if (index < 0 || index > size) throw new IndexOutOfBoundsException();
        if (index == size) {
            last.pre.next = null;
            last = last.pre;
            size--;
            return;
        }
        Node<E> cur = node(index);
        if (cur.pre == null) {
            cur.next.pre = null;
            first = cur.next;
        } else {
            cur.pre.next = cur.next;
            cur.next.pre = cur.pre;
        }
        size--;
    }
    private Node<E> node(int index){
        Node<E> cur = null;
        if (index < (size >> 1)) {//插入位置小于链表的一半
            //从前遍历
            cur = first;
            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }//index=3 插入在第四个位置 cur为第4个链表元素
        } else {
            //从后遍历
            cur = last;
            for (int i = size - 1; i > index; i--) {
                cur = cur.pre;
            }//index=3 插入在第四个位置 cur为第4个链表元素 size =5 last.index=4
        }
        return cur;
    }
    @Override
    public void remove(E e) {
        remove(indexOf(e));
    }
    @SuppressWarnings("unchecked")
    @Override
    public E get(int index) {
        return node(index).element;
    }
    @Override
    public Iterator<E> iterator() {
        return null;
    }
    @Override
    public int indexOf(E e) {
        Node<E> cur = first;
        for (int i = 0 ;i<size;i++){
            if(e.equals(cur.element)){
                return i;
            }
            cur = cur.next;
        }
        return -1;
    }
    private class Node<E> {
        private E element;
        private Node<E> pre;
        private Node<E> next;
        public Node(Node<E> pre, E element, Node<E> next) {
            this.pre = pre;
            this.element = element;
            this.next = next;
        }
    }
目录
相关文章
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
296 4
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
508 30
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
741 25
|
12月前
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
232 0
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
619 5
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
495 5
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
1503 4
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
下一篇
开通oss服务