数据结构之单链表详解

这篇具有很好参考价值的文章主要介绍了数据结构之单链表详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

😽博主CSDN主页:小源_😽

🖋️个人专栏:《数据结构》🖋️

😀努力追逐大佬们的步伐~


目录

1.前言

2. 链表

2.1 链表的概念及结构

2.2 链表的结构分类

3. 无头单向非循环链表的模拟实现

3.1 定义IList接口

3.2 重写IList中的方法

3.3 display(打印方法)

3.4 size方法

3.5 contains方法

3.6 addFirst头插法

3.7 addLast尾插法

3.8 addIndex任意插法

3.9 remove方法

3.10 removeAllKey方法

3.11 clear方法

4.小结


1.前言

我们都知道,ArrayList的底层是一段连续的空间,在ArrayList任意位置插入或者删除元素时,就需要将后续元素整体往前或往后搬移,时间复杂度为O(n),效率比较低,并且在插入元素遇到扩容时,有可能会浪费空间,所以ArrayList不适合做任意位置插入和删除比较多的场景。因此,java集合中又引入了LinkedList,即链表结构。

本章重点:

本文着重讲解了链表的结构和无头单向非循环链表的实现。


2. 链表

2.1 链表的概念及结构

链表是一种物理结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。我们举个小火车例子就可以轻松理解这句话:
数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

链表类似火车一样,它是由一个一个节点/结点(车厢)组成的。我们可以把头节点比作火车头,把后面的节点比作火车的车厢,把利用每个节点中存放的下一个节点的地址来连接每一个节点比作火车之间的铁钩。

链表的每个节点都分为两个部分,一个是val/dadta域(用来存放值),另一个是next域(用来存放下一个节点的地址)。最后一个节点的next域为null。

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表
注意:
  1.从上图可以看出,链式结构在物理上不一定是连续的,但是 逻辑上是连续的。
  2.现实中的节点一般都是从堆上申请出来的。
  3.从堆上申请的空间,是按照一定的策略来分配到,两次申请的空间可能连续,也可能不连续。

2.2 链表的结构分类

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

1.单向后者双向

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

2.带头后者不带头 

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

3. 循环或者非循环

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

虽然有这么多的链表结构,但是我们重点掌握这两种: 

1.无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

2.无头双向链表: 结构复杂,一般用来单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然复杂,但是在后面模拟实现的时候就能发现它有很多特点,反而变简单了


3. 无头单向非循环链表的模拟实现

3.1 定义IList接口

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

3.2 重写IList中的方法

定义一个MySingleList类来实现IList接口,并且重写IList中的方法

(选中IList,快捷键为Ctrl+O):

首先需要定义一个内部类作为节点的类,用static修饰,代表只能在当前类中使用。

接着定义一个头节点head。

然后创建一些新的节点,将head指向第一个节点的位置

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

public class MySinglelList implements IList{
    //节点的内部类
    static class ListNode {
        //val域
        public int val;
        //next域
        public ListNode next;
        //构造方法,接收放在val域的值
        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;
    
    //创建新的节点
    public void createList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);
        
        //修改当前节点的next值为指定的节点
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        //用头节点指向第一个节点的位置
        this.head = node1;
    }
    
    
    @Override
    public void addFirst(int data) {

    }

    @Override
    public void addLast(int data) {

    }

    @Override
    public void addIndex(int index, int data) {

    }

    @Override
    public boolean contains(int key) {
        return false;
    }

    @Override
    public void remove(int key) {

    }

    @Override
    public void removeAllKey(int key) {

    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public void clear() {

    }

    @Override
    public void display() {

    }
}

3.3 display(打印方法)

首先我们要知道如何遍历链表:

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

@Override
    public void display() {
        ListNode cur = this.head;
        //判断是否遍历完了链表,当cur指向null时就说明遍历完了
        while (cur != null) {
            System.out.print(cur.val + " ");
            //cur就是这样从当前位置走到下一个节点的位置
            cur = cur.next;
        }
        System.out.println();
    }

3.4 size方法

求链表个数/长度

@Override
    public int size() {
        //定义一个计数器
        int count = 0;
        //同样的,利用cur遍历链表 
        ListNode cur = this.head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

3.5 contains方法

指的是查找是否包含关键字key是否在单链表当中。

@Override
    public boolean contains(int key) {
        ListNode cur = this.head;
        //遍历链表
        while (cur != null) {
            //如果cur.val等于key返回true
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

3.6 addFirst头插法

指的是将插入的节点存放在链表的第一个位置。

1.实例化一个节点

2.改变插入节点的next

3.改变head

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

​
@Override
    public void addFirst(int data) {
        //1.先创建一个新的节点
        ListNode node = new ListNode(data);
        //如果头节点为空,直接将头节点指向新创建的节点
        if (this.head == null) {
            this.head = node;
        } else {
            //2.如果头节点不为空,将node的next指向head
            node.next = this.head;
            //3.然后将head指向刚刚插在最前面的节点
            this.head = node;
        }
    }

​

3.7 addLast尾插法

指的是将插入的节点存放在链表的第一个位置。

1.实例化一个节点

2.找到最后一个节点cur

3.cur.next = node;

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

@Override
    public void addLast(int data) {
        //先创建一个新的节点
        ListNode node = new ListNode(data);
        //我们最好不移动头节点head的位置,所以用cur指向head,用来遍历链表
        ListNode cur = this.head;
        //如果头节点为空,直接将头节点指向新创建的节点
        if (this.head == null) {
            this.head = node;
        } else {
            //如果头节点不为空,要先找到链表的尾巴,然后再进行连接
            //遍历cur,cur不为空时,会指向下一个节点,下一个节点为null时,停止
            while (cur.next != null) {
                cur = cur.next;
            }
            //此时cur已经指向最后一个节点,现在将 cur.next 指向 node
            cur.next = node;
        }
    }

分析: 头插法的时间复杂度为O(1),尾插法的时间复杂度为O(N)

结论:1.如果想让cur停在最后一个节点的位置,while循环条件为 cur.next != null

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

           2.如果想把整个链表的节点都遍历完,while循环条件为 cur != null

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

3.8 addIndex任意插法

指的是在任意位置插入一个节点。

1.让cur走(index - 1)步

2.node.next = cur.next;

cur.next = node;

 数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

@Override
    public void addIndex(int index, int data) {
        if(index < 0 || index > size()) {
            //可以抛出自定义异常
            System.out.println("index位置不合法:" + index);
            return;
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        //用cur接收前驱
        ListNode cur = searchPrev(index);
        //实例化一个新的节点
        ListNode node = new ListNode(data);
        node.next = cur.next;
        cur.next = node;
    }

    //找到要插入位置的前驱
    public ListNode searchPrev(int index) {
        int count = 0;
        ListNode cur = this.head;
        //遍历链表,让cur走(index - 1)步
        while (count != (index - 1)) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

注意:在绑定一个节点的时候,一定要先绑定后面的这个节点

假如顺序反过来:

cur.next = node;

node.next = cur.next;

则会导致node.next找不到cur.next

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

3.9 remove方法

指的是删除第一次出现关键字为key的节点。

以下代码中 del 是要删除的节点,cur 是 del 的前驱

1.找到前驱(就是要删除的节点的前一个节点)

2.判断返回值是否为空

3.删除(cur.next = del.next以后,del所指向的节点因为没有人指向,所以会被系统自动回收)

 数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

​
​
​
@Override
    public void remove(int key) {
        //头节点为空,直接返回
        if (this.head == null) {
            return;
        }
        
        //如果头节点的val等于key,将头节点改为头节点的下一个节点
        if (this.head.val == key) {
            this.head = this.head.next;
            return;
        }

        //1.找到前驱
        //用cur接收
        ListNode cur = findPrev(key);
        //2.判断返回值是否为空
        if (cur == null) {
            System.out.println("没有你要删除的对象");
            return;
        }
        //3.删除
        //如果不为空,del就是要删除的对象
        ListNode del = cur.next;
        cur = del.next;
    }

    /**
     * 找到key的前一个节点的地址
     * @param key
     * @return
     */
    private ListNode findPrev(int key) {
        ListNode cur = this.head;
        //遍历链表
        //注意while循环条件不能是cur != null,因为cur会走到最后一个节点的后面(null),
        //后面的代码需要用到cur,null没有next,会报空指针异常
        while (cur.next != null) {
            //如果cur的下一个节点的val等于key,说明cur就是我们要找的前驱
            if (cur.next.val == key) {
                //此时cur就是要删除的节点的前驱
                return cur;
            }
            cur = cur.next;
        }
        return cur;
    }

​

​

​

3.10 removeAllKey方法

指的是删除所有值为key的节点。

1.定义一个前驱指向头节点

2.定义cur指向head.next

3.如果cur.val = key 则删除,然后cur的位置向后移动一个节点

4.如果cur.val != key,则prev和cur的位置同时向后移动一个节点

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

@Override
    public void removeAllKey(int key) {
        if (this.head == null) {
            return;
        }

        //定义一个要删除的节点的前驱指向head
        ListNode prev = head;
        //定义一个cur指向head.next,cur是要删除的节点
        ListNode cur = head.next;
        while (cur != null) {
            //遍历链表,如果cur.val == key,删除
            if (cur.val == key) {
                prev.next = cur.next;
            } else {
                //如果cur.val != key,继续下一个节点
                prev = prev.next;
            }
            cur = cur.next;
        }
        
        //如果头节点的val值为key,直接把head的指向改为head.next即可
        if (head.val == key) {
            head = head.next;
        }
    }

3.11 clear方法

指的是删除链表的所有节点。

1.直接把头节点置空的方法太粗糙,我们下面学习更细节的写法:

2.遍历链表,把每个节点的 val = null, next = null 即可

数据结构之单链表详解,数据结构,数据结构,学习,java,idea,链表

@Override
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            //用curNext记录cur.next,因为接下来cur.next将会改变为null
            //  我们要用curNext来继续遍历链表
            ListNode curNext = cur.next;
            //cur.val = 0;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

4.小结

顺序表的缺点就是链表的优点,链表的缺点就是顺序表的优点,即:

顺序表的优点:适合进行给定下标查找的场景。

顺序表的缺点:不适合做任意位置插入和删除比较多的场景。

链表的优点:适合做任意位置插入和删除比较多的场景。

链表的缺点:不适合进行给定下标查找的场景。


最后,祝大家天天开心,更上一层楼!关注我🌹,我会持续更新学习分享...🖋️文章来源地址https://www.toymoban.com/news/detail-768623.html

到了这里,关于数据结构之单链表详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构-链表-单链表(3)

    目录 1. 顺序表的缺陷 2. 单链表 2.1 单链表的基本结构与接口函数 2.2 重要接口 创建新节点的函数: 2.2.1 尾插 2.2.2 头插 2.2.3 尾删 2.2.4 头删 2.2.5 查找 2.2.6 插入 2.2.7 删除 2.2.8 从pos后面插入 2.2.9 从pos后面删除 3. 链表的缺陷与优势: 4. 链表与顺序表比较 写在最后: 为什么

    2024年01月17日
    浏览(34)
  • 【数据结构】- 链表之单链表(下)

    未来藏在迷雾中 叫人看来胆怯 带你踏足其中 就会云开雾散 本章是关于数据结构中的链表之单链表(下) 提示:以下是本篇文章正文内容,下面案例可供参考 1.2.1 在pos位置插入(也就是pos位置之前) 流程图 多个节点 一个节点 1.2.2 在pos位置之后插入 流程图: 注意: 下面这种写

    2023年04月23日
    浏览(45)
  • 【数据结构】-- 单链表 vs 双向链表

    🌈 个人主页: 白子寰 🔥 分类专栏: python从入门到精通,魔法指针,进阶C++,C语言,C语言题集,C语言实现游戏 👈 希望得到您的订阅和支持~ 💡 坚持创作博文(平均质量分82+),分享更多关于深度学习、C/C++,python领域的优质内容!(希望得到您的关注~)  目录  单链表和

    2024年04月17日
    浏览(28)
  • <数据结构> 链表 - 单链表(c语言实现)

    哨兵位结点也叫哑节点。哨兵位结点 也是头结点 。该节点 不存储有效数据,只是为了方便操作 (如尾插时用带哨兵位的头结点很爽,不需要判空)。 有哨兵位结点的链表,第一个元素应该是链表第二个节点(head - next,head为哨兵位结点)对应的元素。 有哨兵位结点的链表

    2023年04月11日
    浏览(29)
  • 【数据结构】反转链表、链表的中间节点、链表的回文结构(单链表OJ题)

    正如标题所说,本文会图文详细解析三道单链表OJ题,分别为:  反转链表 (简单)  链表的中间节点 (简单)  链表的回文结构 (较难) 把他们放在一起讲的原因是:  反转链表 和  链表的中间节点 是  链表的回文结构 的基础 为什么这样说?请往下看: 目录 1. 反转链

    2024年02月13日
    浏览(39)
  • 【数据结构】认识链表和模拟实现单链表

    即使骑的小电驴,也要奋力前进 目录 1.链表 1.1 链表的概念  1.2 链表的逻辑结构图和物理结构图 1.2.1 链表的逻辑结构图  1.2.2 链表的物理结构图  1.3链表结构的分类 1.3.1 链表通过什么进行结构的分类  1.3.2 不同链表结构的逻辑图 2.模拟实现一个单向链表  2.1 MyLinkedList类的

    2024年02月14日
    浏览(32)
  • 【数据结构】 链表简介与单链表的实现

    在【数据结构】 ArrayList简介与实战中我们已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素 由于其底层是一段连续空间, 当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为

    2024年02月12日
    浏览(37)
  • 【数据结构】链表(单链表与双链表实现+原理+源码)

    博主介绍:✌全网粉丝喜爱+、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦! 🍅附上相关C语言版源码讲解🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找

    2024年01月24日
    浏览(39)
  • 数据结构学习分享之双向链表详解

        💓博主CSDN:杭电码农-NEO💓🎉🎉🎉       ⏩专栏分类:数据结构学习分享(持续更新中🫵)⏪🎉🎉🎉       我们上一期说到,两链表中有两个最常用的结构,一个是最简单的无头不循环单向链表,还有一个就是 结构相对比较复杂的带头双向循环链表 ,我们这一章节就来

    2024年02月04日
    浏览(59)
  • [C语言][数据结构][链表] 单链表的从零实现!

    目录 零.必备知识 1.一级指针 二级指针 2. 节点的成员列表     a.数据     b.指向下一个节点的指针. 3. 动态内存空间的开辟 (malloc-calloc-realloc) 一.单链表的实现与销毁          1.1 节点的定义         1.2 单链表的尾插         1.3 单链表的头插         1.4 单链表的尾删  

    2024年04月11日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包