LinkedList与链表

目录

一、链表的概念和结构

概念

结构

1、单向或双向

2、带头或不带头

 3、循环或非循环

二、链表的实现

1、无头单向非循环链表

1.1头插法

1.2尾插法

1.3任意位置插入数据

1.4查找是否存在某个值

1.5删除第一次出现的key节点

1.6删除所有值为key的节点

1.7得到单链表的长度

1.8打印单链表的值

1.9清空链表

三、LinkedList的模拟实现

无头双向链表

3.1头插法

3.2尾插法

3.3求链表长度

3.4任意位置插入

3.5检查链表是否包含某个值

3.6删除第一次出现关键字的节点

3.7删除所有出现关键字的节点

3.8打印链表

3.9清空链表

四、LinkedList的使用

简单介绍

说明:

 LinkedList的构造

LinkedList的一些常用方法

注意事项

五、ArrayList和LinkedList的区别


一、链表的概念和结构

概念

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

在我看来,链表和火车车厢的结构比较类似,是由一节一节的车厢连接起来的。

 注意

  1. 这个时候就能看出,链式结构在逻辑上是连续的,但是在物理上不一定是连续的。
  2. 现实中的节点都是从堆上申请的空间开辟的。

结构

实际中链表的结构主要分以下几个方面:

1、单向或双向

2、带头或不带头

 3、循环或非循环

 注意:Java集合中LinkedList底层是无头双向循环链表

 

二、链表的实现

1、无头单向非循环链表

1.1头插法

注意:

单链表中头节点很烦人,不论是插入还是删除均需考虑头节点,因为其没有前驱。

所有的点操作(".")均需注意空指针情况

//头插法
    public void addFirst(int data) {
        //1.拿到一个实体
        ListNode node = new ListNode(data);

        //2.插入
        //如果是第一次插入,直接找到头节点
        if (this.head == null) {
            this.head = node;
            last = node;
        }else {
            //不是第一次插入
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

1.2尾插法

//尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        }else {
            ListNode cur = head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }

有了头插法和尾插法我们就能调用这两个方法来创建链表,这里我就没吧代码拿出来了。

1.3任意位置插入数据

//检查index下标是否合法
    private void checkIndex(int index) {
        if(index < 0 || index > size()) {
            //throw new IndexOutOfBoundsException("index位置不合法!");

            throw new IndexNotLegalException("index位置不合法!");
        }
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data) {
        checkIndex(index);
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }

        //不是头插尾插,且index合法
        ListNode node = new ListNode(data);
        ListNode cur = findIndexSubOfOne(index);
        node.next = cur.next;
        cur.next = node;
    }

    private ListNode findIndexSubOfOne(int index) {
        ListNode cur = head;
        while (index - 1 != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

1.4查找是否存在某个值

就是简单的遍历链表

//查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if(cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

1.5删除第一次出现的key节点

在删除某个节点的时候,我们以防在删除之后,这个节点后面的节点丢失,我们在删除这个节点的时候要先把这个节点后面的节点记录并链接起来

//删除第一次出现关键字为key的节点
    public void remove(int key) {
        //情况没处理
        if(head.val == key) {
            head = head.next;
            return;
        }
        ListNode cur = searchPrevOfKey(key);
        if(cur == null)
            return;
        ListNode del = cur.next;
        cur.next = del.next;
    }

    /**
     * 找到关键字key的前驱
     * @param key
     * @return
     */
    private ListNode searchPrevOfKey(int key) {
        ListNode cur = this.head;
        while (cur.next != null) {
            if(cur.next.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

1.6删除所有值为key的节点

//删除所有值为key的节点
    public void removeAllKey(int key) {
        if(head == null) return;
        ListNode prev = head;
        ListNode cur = head.next;
        while (cur != null) {
            if(cur.val == key) {
                prev.next = cur.next;
                cur = cur.next;
            }else {
                prev = cur;
                cur = cur.next;
            }
        }
        if(head.val == key) {
            head = head.next;
        }
    }

1.7得到单链表的长度

//得到单链表的长度
    public int size() {
        ListNode cur = head;
        int count = 0;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

1.8打印单链表的值

//打印
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

1.9清空链表

//清空
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

三、LinkedList的模拟实现

无头双向链表

首先定义这个类哦

3.1头插法

//头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);

        if (head == null) {
             head = node;
             last = node;
        }else {
            node.next = head;
            head.pre = node;
            head = node;
        }
    }

3.2尾插法

//尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);

        if (head == null) {
            head = node;
            last = node;
        }else {
            last.next = node;
            node.pre = last;
            last = last.next;
        }
    }

3.3求链表长度

 //计算链表长度
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }

        return count;
    }

3.4任意位置插入

//检查index是否合法
    private boolean checkIndex(int index) {
        if (index < 0 || index > size()) {
            throw new IndexOutOfBoundsException("插入位置不合法!");
        }else {
            return true;
        }
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data) {
        if(checkIndex(index) == true) {
            if (index == 0) {
                addFirst(index);
            }
            if (index == size()) {
                addLast(index);
            }

            ListNode node = new ListNode(data);
            //找到当前index节点的地址
            ListNode cur = findIndex(index);

            node.next = cur;
            cur.pre.next = node;
            node.pre = cur.pre;
            cur.pre = node;

        }

    }

    //找到当前index节点的地址
    private ListNode findIndex(int index) {
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

3.5检查链表是否包含某个值

//查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if(cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return true;
    }

3.6删除第一次出现关键字的节点

//删除第一次出现关键字为key的节点
    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 {
                    cur.pre.next = cur.next;
                    if(cur.next != null) {
                        //删除的不是尾巴节点
                        cur.next.pre = cur.pre;
                    }else {
                        //删除的是尾巴节点
                        last = cur.pre;
                    }
                }
                return;
            }
            cur = cur.next;
        }

        /*while (cur != null) {
            if (cur.val == key) {
                cur.pre.next = cur.next;
                cur.pre = cur.next.pre;
            }else {
                cur = cur.next;
            }
        }*/
    }

3.7删除所有出现关键字的节点

//删除所有值为key的节点
    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 {
                    cur.pre.next = cur.next;
                    if(cur.next != null) {
                        //删除的不是尾巴节点
                        cur.next.pre = cur.pre;
                    }else {
                        //删除的是尾巴节点
                        last = cur.pre;
                    }
                }

            }
            cur = cur.next;
        }
    }

3.8打印链表

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

3.9清空链表

//清空链表
    public void clear() {
        this.head = null;
    }

四、LinkedList的使用

简单介绍

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

下面是LinkedList的部分源码

 在集合框架中,LinkedList也实现了List接口

说明:

  1. LinkedList实现了List接口。
  2. LinkedList的底层使用了双向链表。
  3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问。
  4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)。

 LinkedList的构造

方法解释
LinkedList()无参构造
public LinkedList(Collection<? extends E> c)使用其他集合容器中元素构造List

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

注意事项

1、下图是add方法的源码,可以看到源码采用的是尾插

五、ArrayList和LinkedList的区别

不同点ArrayListLinkedList
存储空间上物理上一定连续逻辑上连续,物理上不一定连续
随机访问支持   O(1)不支持   O(n)
头插需要搬移元素,效率低O(n)只需修改引用的指向,时间复杂幅度O(1)
插入空间不够时需要扩容没用容量的概念
应用需要频繁的使用时+元素高效存储任意位置的插入和删除频繁

  • 40
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 40
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

即将秃头的菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值