[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现

这篇具有很好参考价值的文章主要介绍了[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.ArrayList的缺点

上篇文章我们已经对顺序表进行了实现,并且对ArrayList进行了使用,我们知道ArrayList底层是使用数组实现的.
由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

2.链表

2.1 链表的概念与结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的.就像一列动车一样.
[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
通过上图我们可以看到,一个链表中有一节一节的"车厢",还有中间的"链条".我们称每一节"车厢"为结点,我们可以看到每一个结点中有下一个结点的地址,链表就是通过存储节点的地址来连接起来的,还有该结点的值.上面就是我们需要重点掌握的一种链表类型,叫做单向无头非循环链表.其实链表还有好多类型,下面我们来展示.

2.2 链表的类型

链表有以下几种性质:有头/无头,单向/双向,循环/非循环

  1. 有头/无头
    [Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
    [Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
    它们的区别就是,有头链表的head永远指向一个固定的结点,而无头链表的head永远在改变.
  2. 单向/双向
    [Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表

[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
3. 循环/非循环
[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表
上面几种性质排列组合可以得到许多中链表的类型,这里我们重点掌握2中即可,一种是单向无头非循环,它在笔面试中经常出现,另一种是是无头双向非循环链表,Java中的LinkedList底层就是通过它来实现的.

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

下面是要实现的接口,即链表中的常用方法:

public interface ILinkedList {
    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();

}

下面我们来实现这些方法:

public class MyLinkedList implements ILinkedList {
    static class Node{
        public int val;
        public Node next = null;
        public Node(int val) {
            this.val = val;
        }
    }
    public Node head = null;

    /**
     * 创建默认链表
     */
    public void createDeafultLinkedList(){
        Node node = new Node(23);
        this.head = node;
        Node node1 = new Node(34);
        node.next = node1;
        Node node2 = new Node(45);
        node1.next = node2;
        Node node3 = new Node(56);
        node2.next = node3;
        Node node4 = new Node(67);
        node3.next = node4;
    }

    /**
     * 在链表头部添加新的结点
     * @param data
     */
    @Override
    public void addFirst(int data) {
        Node node = new Node(data);
//        if(head == null){
//            head = node;
//        }else {
//            node.next = head;
//            head = node;
//        }
        //上面的代码其实没必要验证链表是否为空,head为null赋值过去还是null
        node.next = this.head;
        this.head = node;
    }

    /**
     * 在尾部添加新的结点
     * @param data
     */
    @Override
    public void addLast(int data) {
        Node node = new Node(data);
        Node cur = this.head;
        if (this.head == null){
            this.head = node;
        }else {
            while (cur.next != null){
                cur = cur.next;
            }
            cur.next = node;
        }
    }

    /**
     * 在指定位置添加结点
     * @param index
     * @param data
     */
    @Override
    public void addIndex(int index, int data) {
        if (index > size() || index < 0){
            throw new IndexExeption("下标有误");
        }
        Node node = new Node(data);
        if (this.head == null){
            this.head = node;
            return;//记得返回,不然会执行中间插入的代码
        }
        if (index == 0){//在链表头添加
            addFirst(node.val);
            return;
        }
        if (index == size()){//在链表尾部添加
            addLast(node.val);
            return;
        }
        //在中部添加
        Node cur = this.head;
        int count = 0;
        while (count != index-1){//寻找cur的前一个结点
            cur = cur.next;
            count++;
        }
        node.next = cur.next;
        cur.next = node;
    }

    /**
     * 检测链表中是否含有指定的值
     * @param key
     * @return
     */
    @Override
    public boolean contains(int key) {
        Node cur = this.head;
        while (cur != null){//先遍历链表
            if(cur.val == key){//在遍历的过程中如果等于,返回true
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    /**
     * 移除遇到的第一个值为key的元素
     * @param key
     */
    @Override
    public void remove(int key) {
        if (this.head == null){
            return;
        }
        if (head.val == key){
            head = head.next;
            return;
        }
        Node cur = findPreNode(key);//需要找到前一个结点才可以删除
        if (cur == null){//没找到要删除的结点
            return;
        }
        cur.next = cur.next.next;
    }
    private Node findPreNode(int key){//找到要删除结点的前一个结点
        Node cur = this.head;
        while (cur.next != null){//这里必须写成next不等于null,否则下面可能会出现空指针异常
            if (cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    /**
     * 移除所有符合值为key的结点
     * @param key
     */

    @Override
    public void removeAllKey(int key) {
        if (this.head == null){
            return;
        }
        Node pre = this.head;
        Node cur = this.head.next;
        //先不要管头结点,先删中间的
        while(cur != null){
            if (cur.val == key){
                pre.next = cur.next;
            }else {
                pre = pre.next;//若该结点不是要删除的结点,pre往后走
            }
            cur = cur.next;
        }
        //所有的都删完了,删除头结点
        if (head.val == key){
            head = head.next;
        }
    }

    /**
     * 计算链表大小
     * @return
     */
    @Override
    public int size() {
        int count = 0;
        Node cur = this.head;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }

    /**
     * 清空链表
     */
    @Override
    public void clear() {
        head = null;//为被引用的结点会被JWM回收
    }

    /**
     * 打印链表
     */
    @Override
    public void display() {
        Node cur = this.head;
        while (cur != null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

    /**
     * 从指定位置开始打印链表
     * @param node
     */
    public void display(Node node) {
        Node cur = node;
        while (cur != null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

}

上面有几个问题需要注意

  1. 在中间插入元素的时候先要进行后链接,在进行前链接,如果先进行前链接,cur的下一个结点的地址就会丢失
  2. 在删除所有值为key的结点的时候,先删除head后符合条件的结点最后再处理head.

下面是对上述问题的动态演示:

[插入结点错误演示]

插入中间节点(错误)

[插入结点正确演示]

插入中间结点(正确)

[删除所有key结点]

删除所有key结点

下面我们对上述实现的方法进行测试:

**
 * 开始测试
 */
public class Test {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.createDeafultLinkedList();
        myLinkedList.addFirst(11);
        myLinkedList.addLast(88);
        myLinkedList.display();
        System.out.println(myLinkedList.size());
        myLinkedList.addIndex(2,33);
        myLinkedList.display();
        System.out.println(myLinkedList.contains(11));
        System.out.println(myLinkedList.contains(12));
        myLinkedList.addIndex(1,23);
        myLinkedList.addIndex(1,23);
        myLinkedList.addIndex(1,23);
        myLinkedList.display();
        myLinkedList.remove(23);
        myLinkedList.display();
        myLinkedList.removeAllKey(23);
        myLinkedList.display();
        myLinkedList.clear();
        myLinkedList.display();
    }
}

测试结果:
[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现,Collection与数据结构,数据结构,链表文章来源地址https://www.toymoban.com/news/detail-858484.html

到了这里,关于[Collection与数据结构] 链表与LinkedList (一):链表概述与单向无头非循环链表实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 数据结构—LinkedList与链表

    目录 一、链表 1. 链表的概念及结构 1. 单向或者双向 2. 带头或者不带头 3. 循环或者非循环 二.LinkedList的使用  1.LinkedList概念及结构 2. LinkedList的构造 3. LinkedList的方法 三. ArrayList和LinkedList的区别           链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑

    2024年02月04日
    浏览(52)
  • LinkedList数据结构链表

    LinkedList 在Java中是一个实现了 List 和 Deque 接口的双向链表。它允许我们在列表的两端添加或删除元素,同时也支持在列表中间插入或移除元素。在分析 LinkedList 之前,需要理解链表这种数据结构: 链表 :链表是一种动态数据结构,由一系列节点组成,每个节点包含数据部分

    2024年02月20日
    浏览(44)
  • 【C/C++数据结构】链表与快慢指针

    目录 一、单链表 二、双向循环链表 三、判断链表是否带环 四、链表的回文结构判断 五、复制带随机指针的链表 优点 :头部增删效率高,动态存储无空间浪费 缺点 :尾部增删、遍历效率低,不支持随机访问节点 头结点 :单链表头结点可有可无,带头结点更方便进行初始

    2024年02月16日
    浏览(90)
  • 【数据结构二】链表和LinkedList详解

    目录 链表和LinkedList  1.链表的实现 2.LinkedList的使用 3.ArrayList和LinkedList的区别 4.链表OJ题训练         当 在 ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为 O(n) ,效率比较低,因此 ArrayList 不适合做任意位置插入和删除比较多

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

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

    2024年01月24日
    浏览(142)
  • 【数据结构 | 入门】线性表与链表 (问题引入&实现&算法优化)

    🤵‍♂️ 个人主页: @计算机魔术师 👨‍💻 作者简介:CSDN内容合伙人,全栈领域优质创作者。 本文是浙大数据结构学习笔记专栏 这里我们引入一个问题,最常见的多项式,我们如何使用编程将多项式表示出来呢? 我们可以使用数组来表示,但是会随着一个问题,如下图底

    2024年01月21日
    浏览(71)
  • 数据结构(Java实现)LinkedList与链表(上)

    链表 逻辑结构 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。 链表的实现 创建一个链表 遍历单链表 、 得到

    2024年02月11日
    浏览(53)
  • 数据结构(Java实现)LinkedList与链表(下)

    ** ** 结论 让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。 LinkedList的模拟实现 单个节点的实现 尾插 运行结果如下: 也可以暴力使用 全部代码 MyLinkedList IndexOut

    2024年02月11日
    浏览(46)
  • 【数据结构与算法分析】反转链表与顺序表(内含源码,思路清晰)

      顺序表和链表都是数据结构中常见的线性表。它们的主要区别在于 内存管理方式不同 。   顺序表(Array)是由一系列元素按照一定顺序依次排列而成,它使用连续的内存空间存储数据。顺序表使用一个数组来存储数据,数组中的每个元素都可以通过下标来访问。顺序

    2024年02月07日
    浏览(105)
  • 【数据结构】_4.List接口实现类LinkedList与链表

    目录 1.链表的结构与特点 1.1 链表的结构: 1.2 链表的特点: 2. 不带头单链表的模拟实现 3. 单链表OJ 3.1 题目1:移除链表元素:  3.2 题目2:反转一个单链表 3.3 题目3:返回链表的中间结点 3.4 题目4:链表的倒数第k个结点 3.5 题目5:合并两个有序链表 3.6 题目6:链表的回文结构

    2024年02月15日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包